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

typeorm / typeorm / 22333330403

24 Feb 2026 01:58AM UTC coverage: 81.418% (-0.03%) from 81.451%
22333330403

push

github

web-flow
feat(sqlite)!: drop support for sqlite3 and default to better-sqlite3 (#11836)

Co-authored-by: Lucian Mocanu <alumni@users.noreply.github.com>
Co-authored-by: Giorgio Boa <35845425+gioboa@users.noreply.github.com>

26555 of 32182 branches covered (82.52%)

Branch coverage included in aggregate %.

99 of 120 new or added lines in 5 files covered. (82.5%)

30 existing lines in 3 files now uncovered.

94931 of 117030 relevant lines covered (81.12%)

65233.75 hits per line

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

89.9
/src/driver/sqlite-abstract/AbstractSqliteQueryRunner.ts
1
import { QueryRunner } from "../../query-runner/QueryRunner"
23✔
2
import { ObjectLiteral } from "../../common/ObjectLiteral"
23✔
3
import { TransactionNotStartedError } from "../../error/TransactionNotStartedError"
23✔
4
import { TableColumn } from "../../schema-builder/table/TableColumn"
23✔
5
import { Table } from "../../schema-builder/table/Table"
23✔
6
import { TableIndex } from "../../schema-builder/table/TableIndex"
23✔
7
import { TableForeignKey } from "../../schema-builder/table/TableForeignKey"
23✔
8
import { View } from "../../schema-builder/view/View"
23✔
9
import { Query } from "../Query"
23✔
10
import { AbstractSqliteDriver } from "./AbstractSqliteDriver"
23✔
11
import { ReadStream } from "../../platform/PlatformTools"
23✔
12
import { TableIndexOptions } from "../../schema-builder/options/TableIndexOptions"
23✔
13
import { TableUnique } from "../../schema-builder/table/TableUnique"
23✔
14
import { BaseQueryRunner } from "../../query-runner/BaseQueryRunner"
23✔
15
import { OrmUtils } from "../../util/OrmUtils"
23✔
16
import { TableCheck } from "../../schema-builder/table/TableCheck"
23✔
17
import { IsolationLevel } from "../types/IsolationLevel"
23✔
18
import { TableExclusion } from "../../schema-builder/table/TableExclusion"
23✔
19
import { TransactionAlreadyStartedError, TypeORMError } from "../../error"
23✔
20
import { MetadataTableType } from "../types/MetadataTableType"
23✔
21
import { InstanceChecker } from "../../util/InstanceChecker"
23✔
22

23✔
23
/**
23✔
24
 * Runs queries on a single sqlite database connection.
23✔
25
 */
23✔
26
export abstract class AbstractSqliteQueryRunner
23✔
27
    extends BaseQueryRunner
23✔
28
    implements QueryRunner
23✔
29
{
23✔
30
    // -------------------------------------------------------------------------
23✔
31
    // Public Implemented Properties
23✔
32
    // -------------------------------------------------------------------------
23✔
33

23✔
34
    /**
23✔
35
     * Database driver used by connection.
23✔
36
     */
23✔
37
    driver: AbstractSqliteDriver
23✔
38

23✔
39
    protected transactionPromise: Promise<any> | null = null
23✔
40

23✔
41
    // -------------------------------------------------------------------------
23✔
42
    // Constructor
23✔
43
    // -------------------------------------------------------------------------
23✔
44

23✔
45
    constructor() {
23✔
46
        super()
2,817✔
47
    }
2,817✔
48

23✔
49
    // -------------------------------------------------------------------------
23✔
50
    // Public Methods
23✔
51
    // -------------------------------------------------------------------------
23✔
52

23✔
53
    /**
23✔
54
     * Creates/uses database connection from the connection pool to perform further operations.
23✔
55
     * Returns obtained database connection.
23✔
56
     */
23✔
57
    connect(): Promise<any> {
23✔
58
        return Promise.resolve(this.driver.databaseConnection)
76,294✔
59
    }
76,294✔
60

23✔
61
    /**
23✔
62
     * Releases used database connection.
23✔
63
     * We just clear loaded tables and sql in memory, because sqlite do not support multiple connections thus query runners.
23✔
64
     */
23✔
65
    release(): Promise<void> {
23✔
66
        this.loadedTables = []
73,020✔
67
        this.clearSqlMemory()
73,020✔
68
        return Promise.resolve()
73,020✔
69
    }
73,020✔
70

23✔
71
    /**
23✔
72
     * Starts transaction.
23✔
73
     * @param isolationLevel
23✔
74
     */
23✔
75
    async startTransaction(isolationLevel?: IsolationLevel): Promise<void> {
23✔
76
        if (this.driver.transactionSupport === "none")
57,942✔
77
            throw new TypeORMError(
57,942!
78
                `Transactions aren't supported by ${this.connection.driver.options.type}.`,
×
79
            )
×
80

57,942✔
81
        if (
57,942✔
82
            this.isTransactionActive &&
57,942✔
83
            this.driver.transactionSupport === "simple"
96✔
84
        )
57,942✔
85
            throw new TransactionAlreadyStartedError()
57,942!
86

57,942✔
87
        if (
57,942✔
88
            isolationLevel &&
57,942✔
89
            isolationLevel !== "READ UNCOMMITTED" &&
57,942✔
90
            isolationLevel !== "SERIALIZABLE"
9✔
91
        )
57,942✔
92
            throw new TypeORMError(
57,942!
93
                `SQLite only supports SERIALIZABLE and READ UNCOMMITTED isolation`,
×
94
            )
×
95

57,942✔
96
        this.isTransactionActive = true
57,942✔
97
        try {
57,942✔
98
            await this.broadcaster.broadcast("BeforeTransactionStart")
57,942✔
99
        } catch (err) {
57,942!
100
            this.isTransactionActive = false
×
101
            throw err
×
102
        }
×
103

57,942✔
104
        if (this.transactionDepth === 0) {
57,942✔
105
            if (isolationLevel) {
57,846✔
106
                if (isolationLevel === "READ UNCOMMITTED") {
18✔
107
                    await this.query("PRAGMA read_uncommitted = true")
9✔
108
                } else {
9✔
109
                    await this.query("PRAGMA read_uncommitted = false")
9✔
110
                }
9✔
111
            }
18✔
112
            await this.query("BEGIN TRANSACTION")
57,846✔
113
        } else {
57,942✔
114
            await this.query(`SAVEPOINT typeorm_${this.transactionDepth}`)
96✔
115
        }
96✔
116
        this.transactionDepth += 1
57,942✔
117

57,942✔
118
        await this.broadcaster.broadcast("AfterTransactionStart")
57,942✔
119
    }
57,942✔
120

23✔
121
    /**
23✔
122
     * Commits transaction.
23✔
123
     * Error will be thrown if transaction was not started.
23✔
124
     */
23✔
125
    async commitTransaction(): Promise<void> {
23✔
126
        if (!this.isTransactionActive) throw new TransactionNotStartedError()
57,783!
127

57,783✔
128
        await this.broadcaster.broadcast("BeforeTransactionCommit")
57,783✔
129

57,783✔
130
        if (this.transactionDepth > 1) {
57,783✔
131
            await this.query(
60✔
132
                `RELEASE SAVEPOINT typeorm_${this.transactionDepth - 1}`,
60✔
133
            )
60✔
134
        } else {
57,783✔
135
            await this.query("COMMIT")
57,723✔
136
            this.isTransactionActive = false
57,720✔
137
        }
57,720✔
138
        this.transactionDepth -= 1
57,780✔
139

57,780✔
140
        await this.broadcaster.broadcast("AfterTransactionCommit")
57,780✔
141
    }
57,780✔
142

23✔
143
    /**
23✔
144
     * Rollbacks transaction.
23✔
145
     * Error will be thrown if transaction was not started.
23✔
146
     */
23✔
147
    async rollbackTransaction(): Promise<void> {
23✔
148
        if (!this.isTransactionActive) throw new TransactionNotStartedError()
162!
149

162✔
150
        await this.broadcaster.broadcast("BeforeTransactionRollback")
162✔
151

162✔
152
        if (this.transactionDepth > 1) {
162✔
153
            await this.query(
36✔
154
                `ROLLBACK TO SAVEPOINT typeorm_${this.transactionDepth - 1}`,
36✔
155
            )
36✔
156
        } else {
162✔
157
            await this.query("ROLLBACK")
126✔
158
            this.isTransactionActive = false
126✔
159
        }
126✔
160
        this.transactionDepth -= 1
162✔
161

162✔
162
        await this.broadcaster.broadcast("AfterTransactionRollback")
162✔
163
    }
162✔
164

23✔
165
    /**
23✔
166
     * Returns raw data stream.
23✔
167
     * @param query
23✔
168
     * @param parameters
23✔
169
     * @param onEnd
23✔
170
     * @param onError
23✔
171
     */
23✔
172
    stream(
23✔
173
        query: string,
×
174
        parameters?: any[],
×
175
        onEnd?: Function,
×
176
        onError?: Function,
×
177
    ): Promise<ReadStream> {
×
178
        throw new TypeORMError(`Stream is not supported by sqlite driver.`)
×
179
    }
×
180

23✔
181
    /**
23✔
182
     * Returns all available database names including system databases.
23✔
183
     */
23✔
184
    async getDatabases(): Promise<string[]> {
23✔
185
        return Promise.resolve([])
×
186
    }
×
187

23✔
188
    /**
23✔
189
     * Returns all available schema names including system schemas.
23✔
190
     * If database parameter specified, returns schemas of that database.
23✔
191
     * @param database
23✔
192
     */
23✔
193
    async getSchemas(database?: string): Promise<string[]> {
23✔
194
        return Promise.resolve([])
×
195
    }
×
196

23✔
197
    /**
23✔
198
     * Checks if database with the given name exist.
23✔
199
     * @param database
23✔
200
     */
23✔
201
    async hasDatabase(database: string): Promise<boolean> {
23✔
202
        return Promise.resolve(false)
×
203
    }
×
204

23✔
205
    /**
23✔
206
     * Loads currently using database
23✔
207
     */
23✔
208
    async getCurrentDatabase(): Promise<undefined> {
23✔
209
        return Promise.resolve(undefined)
×
210
    }
×
211

23✔
212
    /**
23✔
213
     * Checks if schema with the given name exist.
23✔
214
     * @param schema
23✔
215
     */
23✔
216
    async hasSchema(schema: string): Promise<boolean> {
23✔
217
        throw new TypeORMError(`This driver does not support table schemas`)
×
218
    }
×
219

23✔
220
    /**
23✔
221
     * Loads currently using database schema
23✔
222
     */
23✔
223
    async getCurrentSchema(): Promise<undefined> {
23✔
224
        return Promise.resolve(undefined)
×
225
    }
×
226

23✔
227
    /**
23✔
228
     * Checks if table with the given name exist in the database.
23✔
229
     * @param tableOrName
23✔
230
     */
23✔
231
    async hasTable(tableOrName: Table | string): Promise<boolean> {
23✔
232
        const tableName = InstanceChecker.isTable(tableOrName)
9,042✔
233
            ? tableOrName.name
9,042✔
234
            : tableOrName
9,042✔
235
        const sql = `SELECT * FROM "sqlite_master" WHERE "type" = 'table' AND "name" = '${tableName}'`
9,042✔
236
        const result = await this.query(sql)
9,042✔
237
        return result.length ? true : false
9,042✔
238
    }
9,042✔
239

23✔
240
    /**
23✔
241
     * Checks if column with the given name exist in the given table.
23✔
242
     * @param tableOrName
23✔
243
     * @param columnName
23✔
244
     */
23✔
245
    async hasColumn(
23✔
246
        tableOrName: Table | string,
24✔
247
        columnName: string,
24✔
248
    ): Promise<boolean> {
24✔
249
        const tableName = InstanceChecker.isTable(tableOrName)
24✔
250
            ? tableOrName.name
24!
251
            : tableOrName
24✔
252
        const sql = `PRAGMA table_xinfo(${this.escapePath(tableName)})`
24✔
253
        const columns: ObjectLiteral[] = await this.query(sql)
24✔
254
        return !!columns.find((column) => column["name"] === columnName)
24✔
255
    }
24✔
256

23✔
257
    /**
23✔
258
     * Creates a new database.
23✔
259
     * @param database
23✔
260
     * @param ifNotExist
23✔
261
     */
23✔
262
    async createDatabase(
23✔
263
        database: string,
12✔
264
        ifNotExist?: boolean,
12✔
265
    ): Promise<void> {
12✔
266
        return Promise.resolve()
12✔
267
    }
12✔
268

23✔
269
    /**
23✔
270
     * Drops database.
23✔
271
     * @param database
23✔
272
     * @param ifExist
23✔
273
     */
23✔
274
    async dropDatabase(database: string, ifExist?: boolean): Promise<void> {
23✔
275
        return Promise.resolve()
×
276
    }
×
277

23✔
278
    /**
23✔
279
     * Creates a new table schema.
23✔
280
     * @param schemaPath
23✔
281
     * @param ifNotExist
23✔
282
     */
23✔
283
    async createSchema(
23✔
284
        schemaPath: string,
×
285
        ifNotExist?: boolean,
×
286
    ): Promise<void> {
×
287
        return Promise.resolve()
×
288
    }
×
289

23✔
290
    /**
23✔
291
     * Drops table schema.
23✔
292
     * @param schemaPath
23✔
293
     * @param ifExist
23✔
294
     */
23✔
295
    async dropSchema(schemaPath: string, ifExist?: boolean): Promise<void> {
23✔
296
        return Promise.resolve()
×
297
    }
×
298

23✔
299
    /**
23✔
300
     * Creates a new table.
23✔
301
     * @param table
23✔
302
     * @param ifNotExist
23✔
303
     * @param createForeignKeys
23✔
304
     * @param createIndices
23✔
305
     */
23✔
306
    async createTable(
23✔
307
        table: Table,
29,526✔
308
        ifNotExist: boolean = false,
29,526✔
309
        createForeignKeys: boolean = true,
29,526✔
310
        createIndices: boolean = true,
29,526✔
311
    ): Promise<void> {
29,526✔
312
        const upQueries: Query[] = []
29,526✔
313
        const downQueries: Query[] = []
29,526✔
314

29,526✔
315
        if (ifNotExist) {
29,526✔
316
            const isTableExist = await this.hasTable(table)
168✔
317
            if (isTableExist) return Promise.resolve()
168✔
318
        }
168✔
319

29,520✔
320
        upQueries.push(this.createTableSql(table, createForeignKeys))
29,520✔
321
        downQueries.push(this.dropTableSql(table))
29,520✔
322

29,520✔
323
        if (createIndices) {
29,520✔
324
            table.indices.forEach((index) => {
29,520✔
325
                // new index may be passed without name. In this case we generate index name manually.
10,200✔
326
                if (!index.name)
10,200✔
327
                    index.name = this.connection.namingStrategy.indexName(
10,200✔
328
                        table,
24✔
329
                        index.columnNames,
24✔
330
                        index.where,
24✔
331
                    )
24✔
332
                upQueries.push(this.createIndexSql(table, index))
10,200✔
333
                downQueries.push(this.dropIndexSql(index))
10,200✔
334
            })
29,520✔
335
        }
29,520✔
336

29,520✔
337
        // if table have column with generated type, we must add the expression to the metadata table
29,520✔
338
        const generatedColumns = table.columns.filter(
29,520✔
339
            (column) => column.generatedType && column.asExpression,
29,520!
340
        )
29,520✔
341

29,520✔
342
        for (const column of generatedColumns) {
29,526!
343
            const insertQuery = this.insertTypeormMetadataSql({
63✔
344
                table: table.name,
63✔
345
                type: MetadataTableType.GENERATED_COLUMN,
63✔
346
                name: column.name,
63✔
347
                value: column.asExpression,
63✔
348
            })
63✔
349

63✔
350
            const deleteQuery = this.deleteTypeormMetadataSql({
63✔
351
                table: table.name,
63✔
352
                type: MetadataTableType.GENERATED_COLUMN,
63✔
353
                name: column.name,
63✔
354
            })
63✔
355

63✔
356
            upQueries.push(insertQuery)
63✔
357
            downQueries.push(deleteQuery)
63✔
358
        }
63✔
359

29,520✔
360
        await this.executeQueries(upQueries, downQueries)
29,520✔
361
    }
29,520✔
362

23✔
363
    /**
23✔
364
     * Drops the table.
23✔
365
     * @param tableOrName
23✔
366
     * @param ifExist
23✔
367
     * @param dropForeignKeys
23✔
368
     * @param dropIndices
23✔
369
     */
23✔
370
    async dropTable(
23✔
371
        tableOrName: Table | string,
60✔
372
        ifExist?: boolean,
60✔
373
        dropForeignKeys: boolean = true,
60✔
374
        dropIndices: boolean = true,
60✔
375
    ): Promise<void> {
60✔
376
        if (ifExist) {
60!
377
            const isTableExist = await this.hasTable(tableOrName)
×
378
            if (!isTableExist) return Promise.resolve()
×
379
        }
×
380

60✔
381
        // if dropTable called with dropForeignKeys = true, we must create foreign keys in down query.
60✔
382
        const createForeignKeys: boolean = dropForeignKeys
60✔
383
        const table = InstanceChecker.isTable(tableOrName)
60✔
384
            ? tableOrName
60✔
385
            : await this.getCachedTable(tableOrName)
60✔
386
        const upQueries: Query[] = []
39✔
387
        const downQueries: Query[] = []
39✔
388

39✔
389
        if (dropIndices) {
60✔
390
            table.indices.forEach((index) => {
60✔
391
                upQueries.push(this.dropIndexSql(index))
6✔
392
                downQueries.push(this.createIndexSql(table, index))
6✔
393
            })
60✔
394
        }
60✔
395

60✔
396
        upQueries.push(this.dropTableSql(table, ifExist))
60✔
397
        downQueries.push(this.createTableSql(table, createForeignKeys))
60✔
398

60✔
399
        // if table had columns with generated type, we must remove the expression from the metadata table
60✔
400
        const generatedColumns = table.columns.filter(
60✔
401
            (column) => column.generatedType && column.asExpression,
60!
402
        )
60✔
403

60✔
404
        for (const column of generatedColumns) {
60!
405
            const deleteQuery = this.deleteTypeormMetadataSql({
9✔
406
                table: table.name,
9✔
407
                type: MetadataTableType.GENERATED_COLUMN,
9✔
408
                name: column.name,
9✔
409
            })
9✔
410

9✔
411
            const insertQuery = this.insertTypeormMetadataSql({
9✔
412
                table: table.name,
9✔
413
                type: MetadataTableType.GENERATED_COLUMN,
9✔
414
                name: column.name,
9✔
415
                value: column.asExpression,
9✔
416
            })
9✔
417

9✔
418
            upQueries.push(deleteQuery)
9✔
419
            downQueries.push(insertQuery)
9✔
420
        }
9✔
421

60✔
422
        await this.executeQueries(upQueries, downQueries)
60✔
423
    }
60✔
424

23✔
425
    /**
23✔
426
     * Creates a new view.
23✔
427
     * @param view
23✔
428
     * @param syncWithMetadata
23✔
429
     */
23✔
430
    async createView(
23✔
431
        view: View,
45✔
432
        syncWithMetadata: boolean = false,
45✔
433
    ): Promise<void> {
45✔
434
        const upQueries: Query[] = []
45✔
435
        const downQueries: Query[] = []
45✔
436
        upQueries.push(this.createViewSql(view))
45✔
437
        if (syncWithMetadata) upQueries.push(this.insertViewDefinitionSql(view))
45✔
438
        downQueries.push(this.dropViewSql(view))
45✔
439
        if (syncWithMetadata)
45✔
440
            downQueries.push(this.deleteViewDefinitionSql(view))
45✔
441
        await this.executeQueries(upQueries, downQueries)
45✔
442
    }
45✔
443

23✔
444
    /**
23✔
445
     * Drops the view.
23✔
446
     * @param target
23✔
447
     */
23✔
448
    async dropView(target: View | string): Promise<void> {
23✔
449
        const viewName = InstanceChecker.isView(target) ? target.name : target
×
450
        const view = await this.getCachedView(viewName)
×
451

×
452
        const upQueries: Query[] = []
×
453
        const downQueries: Query[] = []
×
454
        upQueries.push(this.deleteViewDefinitionSql(view))
×
455
        upQueries.push(this.dropViewSql(view))
×
456
        downQueries.push(this.insertViewDefinitionSql(view))
×
457
        downQueries.push(this.createViewSql(view))
×
458
        await this.executeQueries(upQueries, downQueries)
×
459
    }
×
460

23✔
461
    /**
23✔
462
     * Renames the given table.
23✔
463
     * @param oldTableOrName
23✔
464
     * @param newTableName
23✔
465
     */
23✔
466
    async renameTable(
23✔
467
        oldTableOrName: Table | string,
78✔
468
        newTableName: string,
78✔
469
    ): Promise<void> {
78✔
470
        const oldTable = InstanceChecker.isTable(oldTableOrName)
78✔
471
            ? oldTableOrName
78✔
472
            : await this.getCachedTable(oldTableOrName)
78✔
473
        const newTable = oldTable.clone()
66✔
474

66✔
475
        newTable.name = newTableName
66✔
476

66✔
477
        // rename table
66✔
478
        const up = new Query(
66✔
479
            `ALTER TABLE ${this.escapePath(
66✔
480
                oldTable.name,
66✔
481
            )} RENAME TO ${this.escapePath(newTableName)}`,
66✔
482
        )
66✔
483
        const down = new Query(
66✔
484
            `ALTER TABLE ${this.escapePath(
66✔
485
                newTableName,
66✔
486
            )} RENAME TO ${this.escapePath(oldTable.name)}`,
66✔
487
        )
66✔
488
        await this.executeQueries(up, down)
66✔
489

78✔
490
        // rename unique constraints
78✔
491
        newTable.uniques.forEach((unique) => {
78✔
492
            const oldUniqueName =
42✔
493
                this.connection.namingStrategy.uniqueConstraintName(
42✔
494
                    oldTable,
42✔
495
                    unique.columnNames,
42✔
496
                )
42✔
497

42✔
498
            // Skip renaming if Unique has user defined constraint name
42✔
499
            if (unique.name !== oldUniqueName) return
42✔
500

18✔
501
            unique.name = this.connection.namingStrategy.uniqueConstraintName(
18✔
502
                newTable,
18✔
503
                unique.columnNames,
18✔
504
            )
18✔
505
        })
78✔
506

78✔
507
        // rename foreign key constraints
78✔
508
        newTable.foreignKeys.forEach((foreignKey) => {
78✔
509
            const oldForeignKeyName =
54✔
510
                this.connection.namingStrategy.foreignKeyName(
54✔
511
                    oldTable,
54✔
512
                    foreignKey.columnNames,
54✔
513
                    this.getTablePath(foreignKey),
54✔
514
                    foreignKey.referencedColumnNames,
54✔
515
                )
54✔
516

54✔
517
            // Skip renaming if foreign key has user defined constraint name
54✔
518
            if (foreignKey.name !== oldForeignKeyName) return
54✔
519

6✔
520
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
6✔
521
                newTable,
6✔
522
                foreignKey.columnNames,
6✔
523
                this.getTablePath(foreignKey),
6✔
524
                foreignKey.referencedColumnNames,
6✔
525
            )
6✔
526
        })
78✔
527

78✔
528
        // rename indices
78✔
529
        newTable.indices.forEach((index) => {
78✔
530
            const oldIndexName = this.connection.namingStrategy.indexName(
54✔
531
                oldTable,
54✔
532
                index.columnNames,
54✔
533
                index.where,
54✔
534
            )
54✔
535

54✔
536
            // Skip renaming if Index has user defined constraint name
54✔
537
            if (index.name !== oldIndexName) return
54✔
538

30✔
539
            index.name = this.connection.namingStrategy.indexName(
30✔
540
                newTable,
30✔
541
                index.columnNames,
30✔
542
                index.where,
30✔
543
            )
30✔
544
        })
78✔
545

78✔
546
        // rename old table;
78✔
547
        oldTable.name = newTable.name
78✔
548

78✔
549
        // recreate table with new constraint names
78✔
550
        await this.recreateTable(newTable, oldTable)
78✔
551
    }
78✔
552

23✔
553
    /**
23✔
554
     * Creates a new column from the column in the table.
23✔
555
     * @param tableOrName
23✔
556
     * @param column
23✔
557
     */
23✔
558
    async addColumn(
23✔
559
        tableOrName: Table | string,
30✔
560
        column: TableColumn,
30✔
561
    ): Promise<void> {
30✔
562
        const table = InstanceChecker.isTable(tableOrName)
30✔
563
            ? tableOrName
30✔
564
            : await this.getCachedTable(tableOrName)
30✔
565
        return this.addColumns(table!, [column])
18✔
566
    }
18✔
567

23✔
568
    /**
23✔
569
     * Creates a new columns from the column in the table.
23✔
570
     * @param tableOrName
23✔
571
     * @param columns
23✔
572
     */
23✔
573
    async addColumns(
23✔
574
        tableOrName: Table | string,
54✔
575
        columns: TableColumn[],
54✔
576
    ): Promise<void> {
54✔
577
        const table = InstanceChecker.isTable(tableOrName)
54✔
578
            ? tableOrName
54✔
579
            : await this.getCachedTable(tableOrName)
54!
580
        const changedTable = table.clone()
×
581
        columns.forEach((column) => changedTable.addColumn(column))
✔
582
        await this.recreateTable(changedTable, table)
×
583
    }
48✔
584

23✔
585
    /**
23✔
586
     * Renames column in the given table.
23✔
587
     * @param tableOrName
23✔
588
     * @param oldTableColumnOrName
23✔
589
     * @param newTableColumnOrName
23✔
590
     */
23✔
591
    async renameColumn(
23✔
592
        tableOrName: Table | string,
78✔
593
        oldTableColumnOrName: TableColumn | string,
78✔
594
        newTableColumnOrName: TableColumn | string,
78✔
595
    ): Promise<void> {
78✔
596
        const table = InstanceChecker.isTable(tableOrName)
78✔
597
            ? tableOrName
78✔
598
            : await this.getCachedTable(tableOrName)
78✔
599
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
12✔
600
            ? oldTableColumnOrName
78✔
601
            : table.columns.find((c) => c.name === oldTableColumnOrName)
78✔
602
        if (!oldColumn)
78✔
603
            throw new TypeORMError(
78!
604
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
×
605
            )
×
606

78✔
607
        let newColumn: TableColumn | undefined = undefined
78✔
608
        if (InstanceChecker.isTableColumn(newTableColumnOrName)) {
78✔
609
            newColumn = newTableColumnOrName
48✔
610
        } else {
78✔
611
            newColumn = oldColumn.clone()
30✔
612
            newColumn.name = newTableColumnOrName
30✔
613
        }
30✔
614

78✔
615
        return this.changeColumn(table, oldColumn, newColumn)
78✔
616
    }
78✔
617

23✔
618
    /**
23✔
619
     * Changes a column in the table.
23✔
620
     * @param tableOrName
23✔
621
     * @param oldTableColumnOrName
23✔
622
     * @param newColumn
23✔
623
     */
23✔
624
    async changeColumn(
23✔
625
        tableOrName: Table | string,
117✔
626
        oldTableColumnOrName: TableColumn | string,
117✔
627
        newColumn: TableColumn,
117✔
628
    ): Promise<void> {
117✔
629
        const table = InstanceChecker.isTable(tableOrName)
117✔
630
            ? tableOrName
117✔
631
            : await this.getCachedTable(tableOrName)
117!
632
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
×
633
            ? oldTableColumnOrName
117✔
634
            : table.columns.find((c) => c.name === oldTableColumnOrName)
117!
635
        if (!oldColumn)
117✔
636
            throw new TypeORMError(
117!
637
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
×
638
            )
×
639

117✔
640
        await this.changeColumns(table, [{ oldColumn, newColumn }])
117✔
641
    }
117✔
642

23✔
643
    /**
23✔
644
     * Changes a column in the table.
23✔
645
     * Changed column looses all its keys in the db.
23✔
646
     * @param tableOrName
23✔
647
     * @param changedColumns
23✔
648
     */
23✔
649
    async changeColumns(
23✔
650
        tableOrName: Table | string,
246✔
651
        changedColumns: { oldColumn: TableColumn; newColumn: TableColumn }[],
246✔
652
    ): Promise<void> {
246✔
653
        const table = InstanceChecker.isTable(tableOrName)
246✔
654
            ? tableOrName
246✔
655
            : await this.getCachedTable(tableOrName)
246!
656
        const changedTable = table.clone()
×
657
        changedColumns.forEach((changedColumnSet) => {
✔
658
            if (
294✔
659
                changedColumnSet.newColumn.name !==
294✔
660
                changedColumnSet.oldColumn.name
294✔
661
            ) {
294✔
662
                changedTable
123✔
663
                    .findColumnUniques(changedColumnSet.oldColumn)
123✔
664
                    .forEach((unique) => {
123✔
665
                        const uniqueName =
48✔
666
                            this.connection.namingStrategy.uniqueConstraintName(
48✔
667
                                table,
48✔
668
                                unique.columnNames,
48✔
669
                            )
48✔
670

48✔
671
                        unique.columnNames.splice(
48✔
672
                            unique.columnNames.indexOf(
48✔
673
                                changedColumnSet.oldColumn.name,
48✔
674
                            ),
48✔
675
                            1,
48✔
676
                        )
48✔
677
                        unique.columnNames.push(changedColumnSet.newColumn.name)
48✔
678

48✔
679
                        // rename Unique only if it has default constraint name
48✔
680
                        if (unique.name === uniqueName) {
48✔
681
                            unique.name =
24✔
682
                                this.connection.namingStrategy.uniqueConstraintName(
24✔
683
                                    changedTable,
24✔
684
                                    unique.columnNames,
24✔
685
                                )
24✔
686
                        }
24✔
687
                    })
123✔
688

123✔
689
                changedTable
123✔
690
                    .findColumnForeignKeys(changedColumnSet.oldColumn)
123✔
691
                    .forEach((foreignKey) => {
123✔
692
                        const foreignKeyName =
54✔
693
                            this.connection.namingStrategy.foreignKeyName(
54✔
694
                                table,
54✔
695
                                foreignKey.columnNames,
54✔
696
                                this.getTablePath(foreignKey),
54✔
697
                                foreignKey.referencedColumnNames,
54✔
698
                            )
54✔
699

54✔
700
                        foreignKey.columnNames.splice(
54✔
701
                            foreignKey.columnNames.indexOf(
54✔
702
                                changedColumnSet.oldColumn.name,
54✔
703
                            ),
54✔
704
                            1,
54✔
705
                        )
54✔
706
                        foreignKey.columnNames.push(
54✔
707
                            changedColumnSet.newColumn.name,
54✔
708
                        )
54✔
709

54✔
710
                        // rename FK only if it has default constraint name
54✔
711
                        if (foreignKey.name === foreignKeyName) {
54✔
712
                            foreignKey.name =
6✔
713
                                this.connection.namingStrategy.foreignKeyName(
6✔
714
                                    changedTable,
6✔
715
                                    foreignKey.columnNames,
6✔
716
                                    this.getTablePath(foreignKey),
6✔
717
                                    foreignKey.referencedColumnNames,
6✔
718
                                )
6✔
719
                        }
6✔
720
                    })
123✔
721

123✔
722
                changedTable
123✔
723
                    .findColumnIndices(changedColumnSet.oldColumn)
123✔
724
                    .forEach((index) => {
123✔
725
                        const indexName =
42✔
726
                            this.connection.namingStrategy.indexName(
42✔
727
                                table,
42✔
728
                                index.columnNames,
42✔
729
                                index.where,
42✔
730
                            )
42✔
731

42✔
732
                        index.columnNames.splice(
42✔
733
                            index.columnNames.indexOf(
42✔
734
                                changedColumnSet.oldColumn.name,
42✔
735
                            ),
42✔
736
                            1,
42✔
737
                        )
42✔
738
                        index.columnNames.push(changedColumnSet.newColumn.name)
42✔
739

42✔
740
                        // rename Index only if it has default constraint name
42✔
741
                        if (index.name === indexName) {
42✔
742
                            index.name =
30✔
743
                                this.connection.namingStrategy.indexName(
30✔
744
                                    changedTable,
30✔
745
                                    index.columnNames,
30✔
746
                                    index.where,
30✔
747
                                )
30✔
748
                        }
30✔
749
                    })
123✔
750
            }
123✔
751
            const originalColumn = changedTable.columns.find(
294✔
752
                (column) => column.name === changedColumnSet.oldColumn.name,
294✔
753
            )
294✔
754
            if (originalColumn)
294✔
755
                changedTable.columns[
294✔
756
                    changedTable.columns.indexOf(originalColumn)
294✔
757
                ] = changedColumnSet.newColumn
294✔
758
        })
×
759

×
760
        await this.recreateTable(changedTable, table)
×
761
    }
246✔
762

23✔
763
    /**
23✔
764
     * Drops column in the table.
23✔
765
     * @param tableOrName
23✔
766
     * @param columnOrName
23✔
767
     */
23✔
768
    async dropColumn(
23✔
769
        tableOrName: Table | string,
24✔
770
        columnOrName: TableColumn | string,
24✔
771
    ): Promise<void> {
24✔
772
        const table = InstanceChecker.isTable(tableOrName)
24✔
773
            ? tableOrName
24!
774
            : await this.getCachedTable(tableOrName)
24✔
775
        const column = InstanceChecker.isTableColumn(columnOrName)
18✔
776
            ? columnOrName
24!
777
            : table.findColumnByName(columnOrName)
24✔
778
        if (!column)
24✔
779
            throw new TypeORMError(
24✔
780
                `Column "${columnOrName}" was not found in table "${table.name}"`,
6✔
781
            )
6✔
782

18✔
783
        await this.dropColumns(table, [column])
18✔
784
    }
18✔
785

23✔
786
    /**
23✔
787
     * Drops the columns in the table.
23✔
788
     * @param tableOrName
23✔
789
     * @param columns
23✔
790
     */
23✔
791
    async dropColumns(
23✔
792
        tableOrName: Table | string,
42✔
793
        columns: TableColumn[] | string[],
42✔
794
    ): Promise<void> {
42✔
795
        const table = InstanceChecker.isTable(tableOrName)
42✔
796
            ? tableOrName
42✔
797
            : await this.getCachedTable(tableOrName)
42!
798

×
799
        // clone original table and remove column and its constraints from cloned table
×
800
        const changedTable = table.clone()
×
801
        columns.forEach((column: TableColumn | string) => {
✔
802
            const columnInstance = InstanceChecker.isTableColumn(column)
72✔
803
                ? column
72✔
804
                : table.findColumnByName(column)
72✔
805
            if (!columnInstance)
72✔
806
                throw new Error(
72!
807
                    `Column "${column}" was not found in table "${table.name}"`,
×
808
                )
×
809

72✔
810
            changedTable.removeColumn(columnInstance)
72✔
811
            changedTable
72✔
812
                .findColumnUniques(columnInstance)
72✔
813
                .forEach((unique) =>
72✔
814
                    changedTable.removeUniqueConstraint(unique),
72✔
815
                )
72✔
816
            changedTable
72✔
817
                .findColumnIndices(columnInstance)
72✔
818
                .forEach((index) => changedTable.removeIndex(index))
72✔
819
            changedTable
72✔
820
                .findColumnForeignKeys(columnInstance)
72✔
821
                .forEach((fk) => changedTable.removeForeignKey(fk))
72✔
822
        })
×
823

×
824
        await this.recreateTable(changedTable, table)
×
825
    }
42✔
826

23✔
827
    /**
23✔
828
     * Creates a new primary key.
23✔
829
     * @param tableOrName
23✔
830
     * @param columnNames
23✔
831
     */
23✔
832
    async createPrimaryKey(
23✔
833
        tableOrName: Table | string,
12✔
834
        columnNames: string[],
12✔
835
    ): Promise<void> {
12✔
836
        const table = InstanceChecker.isTable(tableOrName)
12✔
837
            ? tableOrName
12!
838
            : await this.getCachedTable(tableOrName)
12✔
839
        // clone original table and mark columns as primary
12✔
840
        const changedTable = table.clone()
12✔
841
        changedTable.columns.forEach((column) => {
12✔
842
            if (columnNames.find((columnName) => columnName === column.name))
30✔
843
                column.isPrimary = true
30✔
844
        })
12✔
845

12✔
846
        await this.recreateTable(changedTable, table)
12✔
847
        // mark columns as primary in original table
12✔
848
        table.columns.forEach((column) => {
12✔
849
            if (columnNames.find((columnName) => columnName === column.name))
30✔
850
                column.isPrimary = true
30✔
851
        })
12✔
852
    }
12✔
853

23✔
854
    /**
23✔
855
     * Updates composite primary keys.
23✔
856
     * @param tableOrName
23✔
857
     * @param columns
23✔
858
     */
23✔
859
    async updatePrimaryKeys(
23✔
860
        tableOrName: Table | string,
6✔
861
        columns: TableColumn[],
6✔
862
    ): Promise<void> {
6✔
863
        await Promise.resolve()
6✔
864
    }
6✔
865

23✔
866
    /**
23✔
867
     * Drops a primary key.
23✔
868
     * @param tableOrName
23✔
869
     */
23✔
870
    async dropPrimaryKey(tableOrName: Table | string): Promise<void> {
23✔
871
        const table = InstanceChecker.isTable(tableOrName)
18✔
872
            ? tableOrName
18✔
873
            : await this.getCachedTable(tableOrName)
18!
874
        // clone original table and mark primary columns as non-primary
×
875
        const changedTable = table.clone()
×
876
        changedTable.primaryColumns.forEach((column) => {
✔
877
            column.isPrimary = false
18✔
878
        })
×
879

×
880
        await this.recreateTable(changedTable, table)
×
881
        // mark primary columns as non-primary in original table
18✔
882
        table.primaryColumns.forEach((column) => {
18✔
883
            column.isPrimary = false
×
884
        })
18✔
885
    }
18✔
886

23✔
887
    /**
23✔
888
     * Creates a new unique constraint.
23✔
889
     * @param tableOrName
23✔
890
     * @param uniqueConstraint
23✔
891
     */
23✔
892
    async createUniqueConstraint(
23✔
893
        tableOrName: Table | string,
9✔
894
        uniqueConstraint: TableUnique,
9✔
895
    ): Promise<void> {
9✔
896
        await this.createUniqueConstraints(tableOrName, [uniqueConstraint])
9✔
897
    }
9✔
898

23✔
899
    /**
23✔
900
     * Creates a new unique constraints.
23✔
901
     * @param tableOrName
23✔
902
     * @param uniqueConstraints
23✔
903
     */
23✔
904
    async createUniqueConstraints(
23✔
905
        tableOrName: Table | string,
18✔
906
        uniqueConstraints: TableUnique[],
18✔
907
    ): Promise<void> {
18✔
908
        const table = InstanceChecker.isTable(tableOrName)
18✔
909
            ? tableOrName
18✔
910
            : await this.getCachedTable(tableOrName)
18!
911

12!
912
        // clone original table and add unique constraints in to cloned table
12✔
913
        const changedTable = table.clone()
12✔
914
        uniqueConstraints.forEach((uniqueConstraint) =>
12✔
915
            changedTable.addUniqueConstraint(uniqueConstraint),
12✔
916
        )
12✔
917
        await this.recreateTable(changedTable, table)
12✔
918
    }
18✔
919

23✔
920
    /**
23✔
921
     * Drops a unique constraint.
23✔
922
     * @param tableOrName
23✔
923
     * @param uniqueOrName
23✔
924
     */
23✔
925
    async dropUniqueConstraint(
23✔
926
        tableOrName: Table | string,
3✔
927
        uniqueOrName: TableUnique | string,
3✔
928
    ): Promise<void> {
3✔
929
        const table = InstanceChecker.isTable(tableOrName)
3✔
930
            ? tableOrName
3✔
931
            : await this.getCachedTable(tableOrName)
3!
932
        const uniqueConstraint = InstanceChecker.isTableUnique(uniqueOrName)
×
933
            ? uniqueOrName
3✔
934
            : table.uniques.find((u) => u.name === uniqueOrName)
3!
935
        if (!uniqueConstraint)
3✔
936
            throw new TypeORMError(
3!
937
                `Supplied unique constraint was not found in table ${table.name}`,
×
938
            )
×
939

3✔
940
        await this.dropUniqueConstraints(table, [uniqueConstraint])
3✔
941
    }
3✔
942

23✔
943
    /**
23✔
944
     * Creates a unique constraints.
23✔
945
     * @param tableOrName
23✔
946
     * @param uniqueConstraints
23✔
947
     */
23✔
948
    async dropUniqueConstraints(
23✔
949
        tableOrName: Table | string,
18✔
950
        uniqueConstraints: TableUnique[],
18✔
951
    ): Promise<void> {
18✔
952
        const table = InstanceChecker.isTable(tableOrName)
18✔
953
            ? tableOrName
18✔
954
            : await this.getCachedTable(tableOrName)
18!
955

3!
956
        // clone original table and remove unique constraints from cloned table
3✔
957
        const changedTable = table.clone()
3✔
958
        uniqueConstraints.forEach((uniqueConstraint) =>
3✔
959
            changedTable.removeUniqueConstraint(uniqueConstraint),
3✔
960
        )
3✔
961

3✔
962
        await this.recreateTable(changedTable, table)
3✔
963
    }
18✔
964

23✔
965
    /**
23✔
966
     * Creates new check constraint.
23✔
967
     * @param tableOrName
23✔
968
     * @param checkConstraint
23✔
969
     */
23✔
970
    async createCheckConstraint(
23✔
971
        tableOrName: Table | string,
21✔
972
        checkConstraint: TableCheck,
21✔
973
    ): Promise<void> {
21✔
974
        await this.createCheckConstraints(tableOrName, [checkConstraint])
21✔
975
    }
21✔
976

23✔
977
    /**
23✔
978
     * Creates new check constraints.
23✔
979
     * @param tableOrName
23✔
980
     * @param checkConstraints
23✔
981
     */
23✔
982
    async createCheckConstraints(
23✔
983
        tableOrName: Table | string,
33✔
984
        checkConstraints: TableCheck[],
33✔
985
    ): Promise<void> {
33✔
986
        const table = InstanceChecker.isTable(tableOrName)
33✔
987
            ? tableOrName
33✔
988
            : await this.getCachedTable(tableOrName)
33✔
989

21✔
990
        // clone original table and add check constraints in to cloned table
21✔
991
        const changedTable = table.clone()
21✔
992
        checkConstraints.forEach((checkConstraint) =>
21✔
993
            changedTable.addCheckConstraint(checkConstraint),
21✔
994
        )
21✔
995
        await this.recreateTable(changedTable, table)
21✔
996
    }
33✔
997

23✔
998
    /**
23✔
999
     * Drops check constraint.
23✔
1000
     * @param tableOrName
23✔
1001
     * @param checkOrName
23✔
1002
     */
23✔
1003
    async dropCheckConstraint(
23✔
1004
        tableOrName: Table | string,
6✔
1005
        checkOrName: TableCheck | string,
6✔
1006
    ): Promise<void> {
6✔
1007
        const table = InstanceChecker.isTable(tableOrName)
6✔
1008
            ? tableOrName
6✔
1009
            : await this.getCachedTable(tableOrName)
6!
1010
        const checkConstraint = InstanceChecker.isTableCheck(checkOrName)
×
1011
            ? checkOrName
6✔
1012
            : table.checks.find((c) => c.name === checkOrName)
6!
1013
        if (!checkConstraint)
6✔
1014
            throw new TypeORMError(
6!
1015
                `Supplied check constraint was not found in table ${table.name}`,
×
1016
            )
×
1017

6✔
1018
        await this.dropCheckConstraints(table, [checkConstraint])
6✔
1019
    }
6✔
1020

23✔
1021
    /**
23✔
1022
     * Drops check constraints.
23✔
1023
     * @param tableOrName
23✔
1024
     * @param checkConstraints
23✔
1025
     */
23✔
1026
    async dropCheckConstraints(
23✔
1027
        tableOrName: Table | string,
24✔
1028
        checkConstraints: TableCheck[],
24✔
1029
    ): Promise<void> {
24✔
1030
        const table = InstanceChecker.isTable(tableOrName)
24✔
1031
            ? tableOrName
24✔
1032
            : await this.getCachedTable(tableOrName)
24!
1033

×
1034
        // clone original table and remove check constraints from cloned table
×
1035
        const changedTable = table.clone()
×
1036
        checkConstraints.forEach((checkConstraint) =>
✔
1037
            changedTable.removeCheckConstraint(checkConstraint),
×
1038
        )
×
1039

×
1040
        await this.recreateTable(changedTable, table)
×
1041
    }
24✔
1042

23✔
1043
    /**
23✔
1044
     * Creates a new exclusion constraint.
23✔
1045
     * @param tableOrName
23✔
1046
     * @param exclusionConstraint
23✔
1047
     */
23✔
1048
    async createExclusionConstraint(
23✔
1049
        tableOrName: Table | string,
×
1050
        exclusionConstraint: TableExclusion,
×
1051
    ): Promise<void> {
×
1052
        throw new TypeORMError(`Sqlite does not support exclusion constraints.`)
×
1053
    }
×
1054

23✔
1055
    /**
23✔
1056
     * Creates a new exclusion constraints.
23✔
1057
     * @param tableOrName
23✔
1058
     * @param exclusionConstraints
23✔
1059
     */
23✔
1060
    async createExclusionConstraints(
23✔
1061
        tableOrName: Table | string,
×
1062
        exclusionConstraints: TableExclusion[],
×
1063
    ): Promise<void> {
×
1064
        throw new TypeORMError(`Sqlite does not support exclusion constraints.`)
×
1065
    }
×
1066

23✔
1067
    /**
23✔
1068
     * Drops exclusion constraint.
23✔
1069
     * @param tableOrName
23✔
1070
     * @param exclusionOrName
23✔
1071
     */
23✔
1072
    async dropExclusionConstraint(
23✔
1073
        tableOrName: Table | string,
×
1074
        exclusionOrName: TableExclusion | string,
×
1075
    ): Promise<void> {
×
1076
        throw new TypeORMError(`Sqlite does not support exclusion constraints.`)
×
1077
    }
×
1078

23✔
1079
    /**
23✔
1080
     * Drops exclusion constraints.
23✔
1081
     * @param tableOrName
23✔
1082
     * @param exclusionConstraints
23✔
1083
     */
23✔
1084
    async dropExclusionConstraints(
23✔
1085
        tableOrName: Table | string,
×
1086
        exclusionConstraints: TableExclusion[],
×
1087
    ): Promise<void> {
×
1088
        throw new TypeORMError(`Sqlite does not support exclusion constraints.`)
×
1089
    }
×
1090

23✔
1091
    /**
23✔
1092
     * Creates a new foreign key.
23✔
1093
     * @param tableOrName
23✔
1094
     * @param foreignKey
23✔
1095
     */
23✔
1096
    async createForeignKey(
23✔
1097
        tableOrName: Table | string,
9✔
1098
        foreignKey: TableForeignKey,
9✔
1099
    ): Promise<void> {
9✔
1100
        await this.createForeignKeys(tableOrName, [foreignKey])
9✔
1101
    }
9✔
1102

23✔
1103
    /**
23✔
1104
     * Creates a new foreign keys.
23✔
1105
     * @param tableOrName
23✔
1106
     * @param foreignKeys
23✔
1107
     */
23✔
1108
    async createForeignKeys(
23✔
1109
        tableOrName: Table | string,
10,974✔
1110
        foreignKeys: TableForeignKey[],
10,974✔
1111
    ): Promise<void> {
10,974✔
1112
        const table = InstanceChecker.isTable(tableOrName)
10,974✔
1113
            ? tableOrName
10,974✔
1114
            : await this.getCachedTable(tableOrName)
10,974✔
1115
        // clone original table and add foreign keys in to cloned table
15✔
1116
        const changedTable = table.clone()
15✔
1117
        foreignKeys.forEach((foreignKey) =>
15✔
1118
            changedTable.addForeignKey(foreignKey),
15✔
1119
        )
15✔
1120

15✔
1121
        await this.recreateTable(changedTable, table)
15✔
1122
    }
10,974✔
1123

23✔
1124
    /**
23✔
1125
     * Drops a foreign key from the table.
23✔
1126
     * @param tableOrName
23✔
1127
     * @param foreignKeyOrName
23✔
1128
     */
23✔
1129
    async dropForeignKey(
23✔
1130
        tableOrName: Table | string,
6✔
1131
        foreignKeyOrName: TableForeignKey | string,
6✔
1132
    ): Promise<void> {
6✔
1133
        const table = InstanceChecker.isTable(tableOrName)
6✔
1134
            ? tableOrName
6✔
1135
            : await this.getCachedTable(tableOrName)
6!
1136
        const foreignKey = InstanceChecker.isTableForeignKey(foreignKeyOrName)
×
1137
            ? foreignKeyOrName
6✔
1138
            : table.foreignKeys.find((fk) => fk.name === foreignKeyOrName)
6!
1139
        if (!foreignKey)
6✔
1140
            throw new TypeORMError(
6!
1141
                `Supplied foreign key was not found in table ${table.name}`,
×
1142
            )
×
1143

6✔
1144
        await this.dropForeignKeys(tableOrName, [foreignKey])
6✔
1145
    }
6✔
1146

23✔
1147
    /**
23✔
1148
     * Drops a foreign keys from the table.
23✔
1149
     * @param tableOrName
23✔
1150
     * @param foreignKeys
23✔
1151
     */
23✔
1152
    async dropForeignKeys(
23✔
1153
        tableOrName: Table | string,
42✔
1154
        foreignKeys: TableForeignKey[],
42✔
1155
    ): Promise<void> {
42✔
1156
        const table = InstanceChecker.isTable(tableOrName)
42✔
1157
            ? tableOrName
42✔
1158
            : await this.getCachedTable(tableOrName)
42✔
1159

6✔
1160
        // clone original table and remove foreign keys from cloned table
6✔
1161
        const changedTable = table.clone()
6✔
1162
        foreignKeys.forEach((foreignKey) =>
6✔
1163
            changedTable.removeForeignKey(foreignKey),
6✔
1164
        )
6✔
1165

6✔
1166
        await this.recreateTable(changedTable, table)
6✔
1167
    }
42✔
1168

23✔
1169
    /**
23✔
1170
     * Creates a new index.
23✔
1171
     * @param tableOrName
23✔
1172
     * @param index
23✔
1173
     */
23✔
1174
    async createIndex(
23✔
1175
        tableOrName: Table | string,
84✔
1176
        index: TableIndex,
84✔
1177
    ): Promise<void> {
84✔
1178
        const table = InstanceChecker.isTable(tableOrName)
84✔
1179
            ? tableOrName
84✔
1180
            : await this.getCachedTable(tableOrName)
84✔
1181

42✔
1182
        // new index may be passed without name. In this case we generate index name manually.
42✔
1183
        if (!index.name) index.name = this.generateIndexName(table, index)
84✔
1184

84✔
1185
        const up = this.createIndexSql(table, index)
84✔
1186
        const down = this.dropIndexSql(index)
84✔
1187
        await this.executeQueries(up, down)
84✔
1188
        table.addIndex(index)
84✔
1189
    }
84✔
1190

23✔
1191
    /**
23✔
1192
     * Creates a new indices
23✔
1193
     * @param tableOrName
23✔
1194
     * @param indices
23✔
1195
     */
23✔
1196
    async createIndices(
23✔
1197
        tableOrName: Table | string,
42✔
1198
        indices: TableIndex[],
42✔
1199
    ): Promise<void> {
42✔
1200
        const promises = indices.map((index) =>
42✔
1201
            this.createIndex(tableOrName, index),
42✔
1202
        )
42✔
1203
        await Promise.all(promises)
42✔
1204
    }
42✔
1205

23✔
1206
    /**
23✔
1207
     * Drops an index from the table.
23✔
1208
     * @param tableOrName
23✔
1209
     * @param indexOrName
23✔
1210
     */
23✔
1211
    async dropIndex(
23✔
1212
        tableOrName: Table | string,
90✔
1213
        indexOrName: TableIndex | string,
90✔
1214
    ): Promise<void> {
90✔
1215
        const table = InstanceChecker.isTable(tableOrName)
90✔
1216
            ? tableOrName
90✔
1217
            : await this.getCachedTable(tableOrName)
90✔
1218
        const index = InstanceChecker.isTableIndex(indexOrName)
30✔
1219
            ? indexOrName
90✔
1220
            : table.indices.find((i) => i.name === indexOrName)
90!
1221
        if (!index)
90✔
1222
            throw new TypeORMError(
90!
1223
                `Supplied index ${indexOrName} was not found in table ${table.name}`,
×
1224
            )
×
1225

90✔
1226
        // old index may be passed without name. In this case we generate index name manually.
90✔
1227
        if (!index.name) index.name = this.generateIndexName(table, index)
90✔
1228

90✔
1229
        const up = this.dropIndexSql(index)
90✔
1230
        const down = this.createIndexSql(table, index)
90✔
1231
        await this.executeQueries(up, down)
90✔
1232
        table.removeIndex(index)
90✔
1233
    }
90✔
1234

23✔
1235
    /**
23✔
1236
     * Drops an indices from the table.
23✔
1237
     * @param tableOrName
23✔
1238
     * @param indices
23✔
1239
     */
23✔
1240
    async dropIndices(
23✔
1241
        tableOrName: Table | string,
9✔
1242
        indices: TableIndex[],
9✔
1243
    ): Promise<void> {
9✔
1244
        const promises = indices.map((index) =>
9✔
1245
            this.dropIndex(tableOrName, index),
9✔
1246
        )
9✔
1247
        await Promise.all(promises)
9✔
1248
    }
9✔
1249

23✔
1250
    /**
23✔
1251
     * Clears all table contents.
23✔
1252
     * Note: this operation uses SQL's TRUNCATE query which cannot be reverted in transactions.
23✔
1253
     * @param tableName
23✔
1254
     */
23✔
1255
    async clearTable(tableName: string): Promise<void> {
23✔
1256
        await this.query(`DELETE FROM ${this.escapePath(tableName)}`)
12✔
1257
    }
12✔
1258

23✔
1259
    /**
23✔
1260
     * Removes all tables from the currently connected database.
23✔
1261
     * @param database
23✔
1262
     */
23✔
1263
    async clearDatabase(database?: string): Promise<void> {
23✔
1264
        let dbPath: string | undefined = undefined
8,403✔
1265
        if (
8,403✔
1266
            database &&
8,403✔
1267
            this.driver.getAttachedDatabaseHandleByRelativePath(database)
4,482✔
1268
        ) {
8,403!
1269
            dbPath =
27✔
1270
                this.driver.getAttachedDatabaseHandleByRelativePath(database)
27✔
1271
        }
27✔
1272

8,403✔
1273
        await this.query(`PRAGMA foreign_keys = OFF`)
8,403✔
1274

8,403✔
1275
        const isAnotherTransactionActive = this.isTransactionActive
8,403✔
1276
        if (!isAnotherTransactionActive) await this.startTransaction()
8,403✔
1277
        try {
8,403✔
1278
            const selectViewDropsQuery = dbPath
8,403✔
1279
                ? `SELECT 'DROP VIEW "${dbPath}"."' || name || '";' as query FROM "${dbPath}"."sqlite_master" WHERE "type" = 'view'`
8,403!
1280
                : `SELECT 'DROP VIEW "' || name || '";' as query FROM "sqlite_master" WHERE "type" = 'view'`
8,403✔
1281
            const dropViewQueries: ObjectLiteral[] =
8,403✔
1282
                await this.query(selectViewDropsQuery)
8,403✔
1283
            await Promise.all(
8,403✔
1284
                dropViewQueries.map((q) => this.query(q["query"])),
8,403✔
1285
            )
8,403✔
1286

8,403✔
1287
            const selectTableDropsQuery = dbPath
8,403✔
1288
                ? `SELECT 'DROP TABLE "${dbPath}"."' || name || '";' as query FROM "${dbPath}"."sqlite_master" WHERE "type" = 'table' AND "name" != 'sqlite_sequence'`
8,403!
1289
                : `SELECT 'DROP TABLE "' || name || '";' as query FROM "sqlite_master" WHERE "type" = 'table' AND "name" != 'sqlite_sequence'`
8,403✔
1290
            const dropTableQueries: ObjectLiteral[] = await this.query(
8,403✔
1291
                selectTableDropsQuery,
8,403✔
1292
            )
8,403✔
1293
            await Promise.all(
8,403✔
1294
                dropTableQueries.map((q) => this.query(q["query"])),
8,403✔
1295
            )
8,403✔
1296

8,403✔
1297
            if (!isAnotherTransactionActive) await this.commitTransaction()
8,403✔
1298
        } catch (error) {
8,403!
1299
            try {
×
1300
                // we throw original error even if rollback thrown an error
×
1301
                if (!isAnotherTransactionActive)
×
1302
                    await this.rollbackTransaction()
×
1303
            } catch (rollbackError) {}
×
1304
            throw error
×
1305
        } finally {
8,403✔
1306
            await this.query(`PRAGMA foreign_keys = ON`)
8,403✔
1307
        }
8,403✔
1308
    }
8,403✔
1309

23✔
1310
    // -------------------------------------------------------------------------
23✔
1311
    // Protected Methods
23✔
1312
    // -------------------------------------------------------------------------
23✔
1313

23✔
1314
    protected async loadViews(viewNames?: string[]): Promise<View[]> {
23✔
1315
        const hasTable = await this.hasTable(this.getTypeormMetadataTableName())
8,700✔
1316
        if (!hasTable) {
8,700✔
1317
            return []
8,643✔
1318
        }
8,643✔
1319

57✔
1320
        if (!viewNames) {
8,700!
1321
            viewNames = []
×
1322
        }
×
1323

57✔
1324
        const viewNamesString = viewNames
57✔
1325
            .map((name) => "'" + name + "'")
57✔
1326
            .join(", ")
57✔
1327
        let query = `SELECT "t".* FROM "${this.getTypeormMetadataTableName()}" "t" INNER JOIN "sqlite_master" s ON "s"."name" = "t"."name" AND "s"."type" = 'view' WHERE "t"."type" = '${
57✔
1328
            MetadataTableType.VIEW
57✔
1329
        }'`
57✔
1330
        if (viewNamesString.length > 0)
57✔
1331
            query += ` AND "t"."name" IN (${viewNamesString})`
4,656✔
1332
        const dbViews = await this.query(query)
57✔
1333
        return dbViews.map((dbView: any) => {
57✔
1334
            const view = new View()
15✔
1335
            view.name = dbView["name"]
15✔
1336
            view.expression = dbView["value"]
15✔
1337
            return view
15✔
1338
        })
57✔
1339
    }
57✔
1340

23✔
1341
    protected async loadTableRecords(
23✔
UNCOV
1342
        tablePath: string,
×
UNCOV
1343
        tableOrIndex: "table" | "index",
×
UNCOV
1344
    ) {
×
UNCOV
1345
        let database: string | undefined = undefined
×
UNCOV
1346
        const [schema, tableName] = this.splitTablePath(tablePath)
×
UNCOV
1347
        if (
×
UNCOV
1348
            schema &&
×
UNCOV
1349
            this.driver.getAttachedDatabasePathRelativeByHandle(schema)
×
UNCOV
1350
        ) {
×
UNCOV
1351
            database =
×
UNCOV
1352
                this.driver.getAttachedDatabasePathRelativeByHandle(schema)
×
UNCOV
1353
        }
×
UNCOV
1354
        return this.query(
×
UNCOV
1355
            `SELECT ${database ? `'${database}'` : null} as database, ${
×
UNCOV
1356
                schema ? `'${schema}'` : null
×
UNCOV
1357
            } as schema, * FROM ${
×
UNCOV
1358
                schema ? `"${schema}".` : ""
×
UNCOV
1359
            }${this.escapePath(
×
UNCOV
1360
                `sqlite_master`,
×
UNCOV
1361
            )} WHERE "type" = '${tableOrIndex}' AND "${
×
UNCOV
1362
                tableOrIndex === "table" ? "name" : "tbl_name"
×
UNCOV
1363
            }" IN ('${tableName}')`,
×
UNCOV
1364
        )
×
UNCOV
1365
    }
×
1366

23✔
1367
    protected async loadPragmaRecords(tablePath: string, pragma: string) {
23✔
1368
        const [, tableName] = this.splitTablePath(tablePath)
3,393✔
1369
        return this.query(`PRAGMA ${pragma}("${tableName}")`)
3,393✔
1370
    }
3,393✔
1371

23✔
1372
    /**
23✔
1373
     * Loads all tables (with given names) from the database and creates a Table from them.
23✔
1374
     * @param tableNames
23✔
1375
     */
23✔
1376
    protected async loadTables(tableNames?: string[]): Promise<Table[]> {
23✔
1377
        // if no tables given then no need to proceed
10,116✔
1378
        if (tableNames && tableNames.length === 0) {
10,116✔
1379
            return []
75✔
1380
        }
75✔
1381

10,041✔
1382
        let dbTables: { database?: string; name: string; sql: string }[] = []
10,041✔
1383
        let dbIndicesDef: ObjectLiteral[]
10,041✔
1384

10,041✔
1385
        if (!tableNames) {
10,116!
1386
            const tablesSql = `SELECT * FROM "sqlite_master" WHERE "type" = 'table'`
3✔
1387
            dbTables.push(...(await this.query(tablesSql)))
3✔
1388

3✔
1389
            const tableNamesString = dbTables
3✔
1390
                .map(({ name }) => `'${name}'`)
3✔
1391
                .join(", ")
3✔
1392
            dbIndicesDef = await this.query(
3✔
1393
                `SELECT * FROM "sqlite_master" WHERE "type" = 'index' AND "tbl_name" IN (${tableNamesString})`,
3✔
1394
            )
3✔
1395
        } else {
10,116✔
1396
            const tableNamesWithoutDot = tableNames
10,038✔
1397
                .filter((tableName) => {
10,038✔
1398
                    return tableName.split(".").length === 1
31,830✔
1399
                })
10,038✔
1400
                .map((tableName) => `'${tableName}'`)
10,038✔
1401

10,038✔
1402
            const tableNamesWithDot = tableNames.filter((tableName) => {
10,038✔
1403
                return tableName.split(".").length > 1
31,830✔
1404
            })
10,038✔
1405

10,038✔
1406
            const queryPromises = (type: "table" | "index") => {
10,038✔
1407
                const promises = [
20,076✔
1408
                    ...tableNamesWithDot.map((tableName) =>
20,076✔
1409
                        this.loadTableRecords(tableName, type),
20,076✔
1410
                    ),
20,076✔
1411
                ]
20,076✔
1412

20,076✔
1413
                if (tableNamesWithoutDot.length) {
20,076✔
1414
                    promises.push(
20,052✔
1415
                        this.query(
20,052✔
1416
                            `SELECT * FROM "sqlite_master" WHERE "type" = '${type}' AND "${
20,052✔
1417
                                type === "table" ? "name" : "tbl_name"
20,052✔
1418
                            }" IN (${tableNamesWithoutDot})`,
20,052✔
1419
                        ),
20,052✔
1420
                    )
20,052✔
1421
                }
20,052✔
1422

20,076✔
1423
                return promises
20,076✔
1424
            }
20,076✔
1425
            dbTables = (await Promise.all(queryPromises("table")))
10,038✔
1426
                .reduce((acc, res) => [...acc, ...res], [])
10,038✔
1427
                .filter(Boolean)
10,038✔
1428
            dbIndicesDef = (await Promise.all(queryPromises("index")))
10,038✔
1429
                .reduce((acc, res) => [...acc, ...res], [])
10,038✔
1430
                .filter(Boolean)
10,038✔
1431
        }
10,038✔
1432

10,041✔
1433
        // if tables were not found in the db, no need to proceed
10,041✔
1434
        if (dbTables.length === 0) {
10,116✔
1435
            return []
8,418✔
1436
        }
8,418✔
1437

1,623✔
1438
        // create table schemas for loaded tables
1,623✔
1439
        return Promise.all(
1,623✔
1440
            dbTables.map(async (dbTable) => {
1,623✔
1441
                const tablePath =
2,490✔
1442
                    dbTable["database"] &&
2,490!
1443
                    this.driver.getAttachedDatabaseHandleByRelativePath(
9✔
1444
                        dbTable["database"],
9✔
1445
                    )
2,490✔
1446
                        ? `${this.driver.getAttachedDatabaseHandleByRelativePath(
2,490!
1447
                              dbTable["database"],
9✔
1448
                          )}.${dbTable["name"]}`
9✔
1449
                        : dbTable["name"]
2,490✔
1450

2,490✔
1451
                const sql = dbTable["sql"]
2,490✔
1452

2,490✔
1453
                const withoutRowid = sql.includes("WITHOUT ROWID")
2,490✔
1454
                const table = new Table({ name: tablePath, withoutRowid })
2,490✔
1455

2,490✔
1456
                // load columns and indices
2,490✔
1457
                const [dbColumns, dbIndices, dbForeignKeys]: ObjectLiteral[][] =
2,490✔
1458
                    await Promise.all([
2,490✔
1459
                        this.loadPragmaRecords(tablePath, `table_xinfo`),
2,490✔
1460
                        this.loadPragmaRecords(tablePath, `index_list`),
2,490✔
1461
                        this.loadPragmaRecords(tablePath, `foreign_key_list`),
2,490✔
1462
                    ])
2,490✔
1463

2,490✔
1464
                // find column name with auto increment
2,490✔
1465
                let autoIncrementColumnName: string | undefined = undefined
2,490✔
1466
                const tableSql: string = dbTable["sql"]
2,490✔
1467
                const autoIncrementIndex = tableSql
2,490✔
1468
                    .toUpperCase()
2,490✔
1469
                    .indexOf("AUTOINCREMENT")
2,490✔
1470
                if (autoIncrementIndex !== -1) {
2,490✔
1471
                    autoIncrementColumnName = tableSql.substr(
1,410✔
1472
                        0,
1,410✔
1473
                        autoIncrementIndex,
1,410✔
1474
                    )
1,410✔
1475
                    const comma = autoIncrementColumnName.lastIndexOf(",")
1,410✔
1476
                    const bracket = autoIncrementColumnName.lastIndexOf("(")
1,410✔
1477
                    if (comma !== -1) {
1,410!
1478
                        autoIncrementColumnName =
×
1479
                            autoIncrementColumnName.substr(comma)
×
1480
                        autoIncrementColumnName =
×
1481
                            autoIncrementColumnName.substr(
×
1482
                                0,
×
1483
                                autoIncrementColumnName.lastIndexOf('"'),
×
1484
                            )
×
1485
                        autoIncrementColumnName =
×
1486
                            autoIncrementColumnName.substr(
×
1487
                                autoIncrementColumnName.indexOf('"') + 1,
×
1488
                            )
×
1489
                    } else if (bracket !== -1) {
1,410✔
1490
                        autoIncrementColumnName =
1,410✔
1491
                            autoIncrementColumnName.substr(bracket)
1,410✔
1492
                        autoIncrementColumnName =
1,410✔
1493
                            autoIncrementColumnName.substr(
1,410✔
1494
                                0,
1,410✔
1495
                                autoIncrementColumnName.lastIndexOf('"'),
1,410✔
1496
                            )
1,410✔
1497
                        autoIncrementColumnName =
1,410✔
1498
                            autoIncrementColumnName.substr(
1,410✔
1499
                                autoIncrementColumnName.indexOf('"') + 1,
1,410✔
1500
                            )
1,410✔
1501
                    }
1,410✔
1502
                }
1,410✔
1503

2,490✔
1504
                // create columns from the loaded columns
2,490✔
1505
                table.columns = await Promise.all(
2,490✔
1506
                    dbColumns.map(async (dbColumn) => {
2,490✔
1507
                        const tableColumn = new TableColumn()
8,667✔
1508
                        tableColumn.name = dbColumn["name"]
8,667✔
1509
                        tableColumn.type = dbColumn["type"].toLowerCase()
8,667✔
1510
                        tableColumn.default =
8,667✔
1511
                            dbColumn["dflt_value"] !== null &&
8,667✔
1512
                            dbColumn["dflt_value"] !== undefined
567✔
1513
                                ? dbColumn["dflt_value"]
8,667✔
1514
                                : undefined
8,667✔
1515
                        tableColumn.isNullable = dbColumn["notnull"] === 0
8,667✔
1516
                        // primary keys are numbered starting with 1, columns that aren't primary keys are marked with 0
8,667✔
1517
                        tableColumn.isPrimary = dbColumn["pk"] > 0
8,667✔
1518
                        tableColumn.comment = "" // SQLite does not support column comments
8,667✔
1519
                        tableColumn.isGenerated =
8,667✔
1520
                            autoIncrementColumnName === dbColumn["name"]
8,667✔
1521
                        if (tableColumn.isGenerated) {
8,667✔
1522
                            tableColumn.generationStrategy = "increment"
1,410✔
1523
                        }
1,410✔
1524

8,667✔
1525
                        if (
8,667✔
1526
                            dbColumn["hidden"] === 2 ||
8,667✔
1527
                            dbColumn["hidden"] === 3
8,631✔
1528
                        ) {
8,667!
1529
                            tableColumn.generatedType =
105✔
1530
                                dbColumn["hidden"] === 2 ? "VIRTUAL" : "STORED"
105✔
1531

105✔
1532
                            const asExpressionQuery =
105✔
1533
                                this.selectTypeormMetadataSql({
105✔
1534
                                    table: table.name,
105✔
1535
                                    type: MetadataTableType.GENERATED_COLUMN,
105✔
1536
                                    name: tableColumn.name,
105✔
1537
                                })
105✔
1538

105✔
1539
                            const results = await this.query(
105✔
1540
                                asExpressionQuery.query,
105✔
1541
                                asExpressionQuery.parameters,
105✔
1542
                            )
105✔
1543
                            if (results[0] && results[0].value) {
105✔
1544
                                tableColumn.asExpression = results[0].value
105✔
1545
                            } else {
105!
1546
                                tableColumn.asExpression = ""
×
1547
                            }
×
1548
                        }
105✔
1549

8,667✔
1550
                        if (tableColumn.type === "varchar") {
8,667✔
1551
                            tableColumn.enum = OrmUtils.parseSqlCheckExpression(
4,056✔
1552
                                sql,
4,056✔
1553
                                tableColumn.name,
4,056✔
1554
                            )
4,056✔
1555
                        }
4,056✔
1556

8,667✔
1557
                        // parse datatype and attempt to retrieve length, precision and scale
8,667✔
1558
                        const pos = tableColumn.type.indexOf("(")
8,667✔
1559
                        if (pos !== -1) {
8,667✔
1560
                            const fullType = tableColumn.type
567✔
1561
                            const dataType = fullType.substr(0, pos)
567✔
1562
                            if (
567✔
1563
                                this.driver.withLengthColumnTypes.find(
567✔
1564
                                    (col) => col === dataType,
567✔
1565
                                )
567✔
1566
                            ) {
567✔
1567
                                const len = parseInt(
534✔
1568
                                    fullType.substring(
534✔
1569
                                        pos + 1,
534✔
1570
                                        fullType.length - 1,
534✔
1571
                                    ),
534✔
1572
                                )
534✔
1573
                                if (len) {
534✔
1574
                                    tableColumn.length = len.toString()
534✔
1575
                                    tableColumn.type = dataType // remove the length part from the datatype
534✔
1576
                                }
534✔
1577
                            }
534✔
1578
                            if (
567✔
1579
                                this.driver.withPrecisionColumnTypes.find(
567✔
1580
                                    (col) => col === dataType,
567✔
1581
                                )
567✔
1582
                            ) {
567✔
1583
                                const re = new RegExp(
33✔
1584
                                    `^${dataType}\\((\\d+),?\\s?(\\d+)?\\)`,
33✔
1585
                                )
33✔
1586
                                const matches = fullType.match(re)
33✔
1587
                                if (matches && matches[1]) {
33✔
1588
                                    tableColumn.precision = +matches[1]
33✔
1589
                                }
33✔
1590
                                if (
33✔
1591
                                    this.driver.withScaleColumnTypes.find(
33✔
1592
                                        (col) => col === dataType,
33✔
1593
                                    )
33✔
1594
                                ) {
33✔
1595
                                    if (matches && matches[2]) {
18✔
1596
                                        tableColumn.scale = +matches[2]
15✔
1597
                                    }
15✔
1598
                                }
18✔
1599
                                tableColumn.type = dataType // remove the precision/scale part from the datatype
33✔
1600
                            }
33✔
1601
                        }
567✔
1602

8,667✔
1603
                        return tableColumn
8,667✔
1604
                    }),
2,490✔
1605
                )
2,490✔
1606

2,490✔
1607
                // find foreign key constraints from CREATE TABLE sql
2,490✔
1608
                let fkResult
2,490✔
1609
                const fkMappings: {
2,490✔
1610
                    name: string
2,490✔
1611
                    columns: string[]
2,490✔
1612
                    referencedTableName: string
2,490✔
1613
                }[] = []
2,490✔
1614
                const fkRegex =
2,490✔
1615
                    /CONSTRAINT "([^"]*)" FOREIGN KEY ?\((.*?)\) REFERENCES "([^"]*)"/g
2,490✔
1616
                while ((fkResult = fkRegex.exec(sql)) !== null) {
2,490✔
1617
                    fkMappings.push({
1,032✔
1618
                        name: fkResult[1],
1,032✔
1619
                        columns: fkResult[2]
1,032✔
1620
                            .substr(1, fkResult[2].length - 2)
1,032✔
1621
                            .split(`", "`),
1,032✔
1622
                        referencedTableName: fkResult[3],
1,032✔
1623
                    })
1,032✔
1624
                }
1,032✔
1625

2,490✔
1626
                // build foreign keys
2,490✔
1627
                const tableForeignKeyConstraints = OrmUtils.uniq(
2,490✔
1628
                    dbForeignKeys,
2,490✔
1629
                    (dbForeignKey) => dbForeignKey["id"],
2,490✔
1630
                )
2,490✔
1631

2,490✔
1632
                table.foreignKeys = tableForeignKeyConstraints.map(
2,490✔
1633
                    (foreignKey) => {
2,490✔
1634
                        const ownForeignKeys = dbForeignKeys.filter(
1,035✔
1635
                            (dbForeignKey) =>
1,035✔
1636
                                dbForeignKey["id"] === foreignKey["id"] &&
2,205✔
1637
                                dbForeignKey["table"] === foreignKey["table"],
1,035✔
1638
                        )
1,035✔
1639
                        const columnNames = ownForeignKeys.map(
1,035✔
1640
                            (dbForeignKey) => dbForeignKey["from"],
1,035✔
1641
                        )
1,035✔
1642
                        const referencedColumnNames = ownForeignKeys.map(
1,035✔
1643
                            (dbForeignKey) => dbForeignKey["to"],
1,035✔
1644
                        )
1,035✔
1645

1,035✔
1646
                        // find related foreign key mapping
1,035✔
1647
                        const fkMapping = fkMappings.find(
1,035✔
1648
                            (it) =>
1,035✔
1649
                                it.referencedTableName ===
1,563✔
1650
                                    foreignKey["table"] &&
1,563✔
1651
                                it.columns.every(
1,110✔
1652
                                    (column) =>
1,110✔
1653
                                        columnNames.indexOf(column) !== -1,
1,110✔
1654
                                ),
1,035✔
1655
                        )
1,035✔
1656

1,035✔
1657
                        return new TableForeignKey({
1,035✔
1658
                            name: fkMapping?.name,
1,035✔
1659
                            columnNames: columnNames,
1,035✔
1660
                            referencedTableName: foreignKey["table"],
1,035✔
1661
                            referencedColumnNames: referencedColumnNames,
1,035✔
1662
                            onDelete: foreignKey["on_delete"],
1,035✔
1663
                            onUpdate: foreignKey["on_update"],
1,035✔
1664
                        })
1,035✔
1665
                    },
2,490✔
1666
                )
2,490✔
1667

2,490✔
1668
                // find unique constraints from CREATE TABLE sql
2,490✔
1669
                let uniqueRegexResult
2,490✔
1670
                const uniqueMappings: { name: string; columns: string[] }[] = []
2,490✔
1671
                const uniqueRegex = /CONSTRAINT "([^"]*)" UNIQUE ?\((.*?)\)/g
2,490✔
1672
                while ((uniqueRegexResult = uniqueRegex.exec(sql)) !== null) {
2,490✔
1673
                    uniqueMappings.push({
1,248✔
1674
                        name: uniqueRegexResult[1],
1,248✔
1675
                        columns: uniqueRegexResult[2]
1,248✔
1676
                            .substr(1, uniqueRegexResult[2].length - 2)
1,248✔
1677
                            .split(`", "`),
1,248✔
1678
                    })
1,248✔
1679
                }
1,248✔
1680

2,490✔
1681
                // build unique constraints
2,490✔
1682
                const tableUniquePromises = dbIndices
2,490✔
1683
                    .filter((dbIndex) => dbIndex["origin"] === "u")
2,490✔
1684
                    .map((dbIndex) => dbIndex["name"])
2,490✔
1685
                    .filter(
2,490✔
1686
                        (value, index, self) => self.indexOf(value) === index,
2,490✔
1687
                    )
2,490✔
1688
                    .map(async (dbIndexName) => {
2,490✔
1689
                        const dbIndex = dbIndices.find(
1,248✔
1690
                            (dbIndex) => dbIndex["name"] === dbIndexName,
1,248✔
1691
                        )
1,248✔
1692
                        const indexInfos: ObjectLiteral[] = await this.query(
1,248✔
1693
                            `PRAGMA index_info("${dbIndex!["name"]}")`,
1,248✔
1694
                        )
1,248✔
1695
                        const indexColumns = indexInfos
1,248✔
1696
                            .sort(
1,248✔
1697
                                (indexInfo1, indexInfo2) =>
1,248✔
1698
                                    parseInt(indexInfo1["seqno"]) -
498✔
1699
                                    parseInt(indexInfo2["seqno"]),
1,248✔
1700
                            )
1,248✔
1701
                            .map((indexInfo) => indexInfo["name"])
1,248✔
1702
                        if (indexColumns.length === 1) {
1,248✔
1703
                            const column = table.columns.find((column) => {
753✔
1704
                                return !!indexColumns.find(
1,722✔
1705
                                    (indexColumn) =>
1,722✔
1706
                                        indexColumn === column.name,
1,722✔
1707
                                )
1,722✔
1708
                            })
753✔
1709
                            if (column) column.isUnique = true
753✔
1710
                        }
753✔
1711

1,248✔
1712
                        // find existent mapping by a column names
1,248✔
1713
                        const foundMapping = uniqueMappings.find((mapping) => {
1,248✔
1714
                            return mapping!.columns.every(
1,824✔
1715
                                (column) => indexColumns.indexOf(column) !== -1,
1,824✔
1716
                            )
1,824✔
1717
                        })
1,248✔
1718

1,248✔
1719
                        return new TableUnique({
1,248✔
1720
                            name: foundMapping
1,248✔
1721
                                ? foundMapping.name
1,248✔
1722
                                : this.connection.namingStrategy.uniqueConstraintName(
1,248!
1723
                                      table,
×
1724
                                      indexColumns,
×
1725
                                  ),
1,248✔
1726
                            columnNames: indexColumns,
1,248✔
1727
                        })
1,248✔
1728
                    })
2,490✔
1729
                table.uniques = (await Promise.all(
2,490✔
1730
                    tableUniquePromises,
2,490✔
1731
                )) as TableUnique[]
2,490✔
1732

2,490✔
1733
                // build checks
2,490✔
1734
                let result
2,490✔
1735
                const regexp =
2,490✔
1736
                    /CONSTRAINT "([^"]*)" CHECK ?(\(.*?\))([,]|[)]$)/g
2,490✔
1737
                while ((result = regexp.exec(sql)) !== null) {
2,490✔
1738
                    table.checks.push(
483✔
1739
                        new TableCheck({
483✔
1740
                            name: result[1],
483✔
1741
                            expression: result[2],
483✔
1742
                        }),
483✔
1743
                    )
483✔
1744
                }
483✔
1745

2,490✔
1746
                // build indices
2,490✔
1747
                const indicesPromises = dbIndices
2,490✔
1748
                    .filter((dbIndex) => dbIndex["origin"] === "c")
2,490✔
1749
                    .map((dbIndex) => dbIndex["name"])
2,490✔
1750
                    .filter(
2,490✔
1751
                        (value, index, self) => self.indexOf(value) === index,
2,490✔
1752
                    ) // unqiue
2,490✔
1753
                    .map(async (dbIndexName) => {
2,490✔
1754
                        const indexDef = dbIndicesDef.find(
699✔
1755
                            (dbIndexDef) => dbIndexDef["name"] === dbIndexName,
699✔
1756
                        )
699✔
1757
                        const condition = /WHERE (.*)/.exec(indexDef!["sql"])
699✔
1758
                        const dbIndex = dbIndices.find(
699✔
1759
                            (dbIndex) => dbIndex["name"] === dbIndexName,
699✔
1760
                        )
699✔
1761
                        const indexInfos: ObjectLiteral[] = await this.query(
699✔
1762
                            `PRAGMA index_info("${dbIndex!["name"]}")`,
699✔
1763
                        )
699✔
1764
                        const indexColumns = indexInfos
699✔
1765
                            .sort(
699✔
1766
                                (indexInfo1, indexInfo2) =>
699✔
1767
                                    parseInt(indexInfo1["seqno"]) -
81✔
1768
                                    parseInt(indexInfo2["seqno"]),
699✔
1769
                            )
699✔
1770
                            .map((indexInfo) => indexInfo["name"])
699✔
1771
                        const dbIndexPath = `${
699✔
1772
                            dbTable["database"] ? `${dbTable["database"]}.` : ""
699!
1773
                        }${dbIndex!["name"]}`
699✔
1774

699✔
1775
                        const isUnique =
699✔
1776
                            dbIndex!["unique"] === "1" ||
699✔
1777
                            dbIndex!["unique"] === 1
699✔
1778
                        return new TableIndex(<TableIndexOptions>{
699✔
1779
                            table: table,
699✔
1780
                            name: dbIndexPath,
699✔
1781
                            columnNames: indexColumns,
699✔
1782
                            isUnique: isUnique,
699✔
1783
                            where: condition ? condition[1] : undefined,
699!
1784
                        })
699✔
1785
                    })
2,490✔
1786
                const indices = await Promise.all(indicesPromises)
2,490✔
1787
                table.indices = indices.filter(
2,490✔
1788
                    (index) => !!index,
2,490✔
1789
                ) as TableIndex[]
2,490✔
1790

2,490✔
1791
                return table
2,490✔
1792
            }),
1,623✔
1793
        )
1,623✔
1794
    }
1,623✔
1795

23✔
1796
    /**
23✔
1797
     * Builds create table sql.
23✔
1798
     * @param table
23✔
1799
     * @param createForeignKeys
23✔
1800
     * @param temporaryTable
23✔
1801
     */
23✔
1802
    protected createTableSql(
23✔
1803
        table: Table,
52,698✔
1804
        createForeignKeys?: boolean,
52,698✔
1805
        temporaryTable?: boolean,
52,698✔
1806
    ): Query {
52,698✔
1807
        const primaryColumns = table.columns.filter(
52,698✔
1808
            (column) => column.isPrimary,
52,698✔
1809
        )
52,698✔
1810
        const hasAutoIncrement = primaryColumns.find(
52,698✔
1811
            (column) =>
52,698✔
1812
                column.isGenerated && column.generationStrategy === "increment",
52,698✔
1813
        )
52,698✔
1814
        const skipPrimary = primaryColumns.length > 1
52,698✔
1815
        if (skipPrimary && hasAutoIncrement)
52,698✔
1816
            throw new TypeORMError(
52,698!
1817
                `Sqlite does not support AUTOINCREMENT on composite primary key`,
×
1818
            )
×
1819

52,698✔
1820
        const columnDefinitions = table.columns
52,698✔
1821
            .map((column) => this.buildCreateColumnSql(column, skipPrimary))
52,698✔
1822
            .join(", ")
52,698✔
1823
        const [database] = this.splitTablePath(table.name)
52,698✔
1824
        let sql = `CREATE TABLE ${this.escapePath(
52,698✔
1825
            table.name,
52,698✔
1826
        )} (${columnDefinitions}`
52,698✔
1827

52,698✔
1828
        const [databaseNew, tableName] = this.splitTablePath(table.name)
52,698✔
1829
        const newTableName = temporaryTable
52,698✔
1830
            ? `${databaseNew ? `${databaseNew}.` : ""}${tableName.replace(
52,698!
1831
                  /^temporary_/,
11,559✔
1832
                  "",
11,559✔
1833
              )}`
11,559✔
1834
            : table.name
52,698✔
1835

52,698✔
1836
        // need for `addColumn()` method, because it recreates table.
52,698✔
1837
        table.columns
52,698✔
1838
            .filter((column) => column.isUnique)
52,698✔
1839
            .forEach((column) => {
52,698✔
1840
                const isUniqueExist = table.uniques.some(
9,321✔
1841
                    (unique) =>
9,321✔
1842
                        unique.columnNames.length === 1 &&
12,930✔
1843
                        unique.columnNames[0] === column.name,
9,321✔
1844
                )
9,321✔
1845
                if (!isUniqueExist)
9,321✔
1846
                    table.uniques.push(
9,321✔
1847
                        new TableUnique({
48✔
1848
                            name: this.connection.namingStrategy.uniqueConstraintName(
48✔
1849
                                table,
48✔
1850
                                [column.name],
48✔
1851
                            ),
48✔
1852
                            columnNames: [column.name],
48✔
1853
                        }),
48✔
1854
                    )
48✔
1855
            })
52,698✔
1856

52,698✔
1857
        if (table.uniques.length > 0) {
52,698✔
1858
            const uniquesSql = table.uniques
8,361✔
1859
                .map((unique) => {
8,361✔
1860
                    const uniqueName = unique.name
12,060✔
1861
                        ? unique.name
12,060✔
1862
                        : this.connection.namingStrategy.uniqueConstraintName(
12,060✔
1863
                              newTableName,
21✔
1864
                              unique.columnNames,
21✔
1865
                          )
12,060✔
1866
                    const columnNames = unique.columnNames
12,060✔
1867
                        .map((columnName) => `"${columnName}"`)
12,060✔
1868
                        .join(", ")
12,060✔
1869
                    return `CONSTRAINT "${uniqueName}" UNIQUE (${columnNames})`
12,060✔
1870
                })
8,361✔
1871
                .join(", ")
8,361✔
1872

8,361✔
1873
            sql += `, ${uniquesSql}`
8,361✔
1874
        }
8,361✔
1875

52,698✔
1876
        if (table.checks.length > 0) {
52,698✔
1877
            const checksSql = table.checks
837✔
1878
                .map((check) => {
837✔
1879
                    const checkName = check.name
861✔
1880
                        ? check.name
861✔
1881
                        : this.connection.namingStrategy.checkConstraintName(
861✔
1882
                              newTableName,
63✔
1883
                              check.expression!,
63✔
1884
                          )
861✔
1885
                    return `CONSTRAINT "${checkName}" CHECK (${check.expression})`
861✔
1886
                })
837✔
1887
                .join(", ")
837✔
1888

837✔
1889
            sql += `, ${checksSql}`
837✔
1890
        }
837✔
1891

52,698✔
1892
        if (table.foreignKeys.length > 0 && createForeignKeys) {
52,698✔
1893
            const foreignKeysSql = table.foreignKeys
11,214✔
1894
                .filter((fk) => {
11,214✔
1895
                    const [referencedDatabase] = this.splitTablePath(
19,155✔
1896
                        fk.referencedTableName,
19,155✔
1897
                    )
19,155✔
1898
                    if (referencedDatabase !== database) {
19,155!
1899
                        return false
×
1900
                    }
×
1901
                    return true
19,155✔
1902
                })
11,214✔
1903
                .map((fk) => {
11,214✔
1904
                    const [, referencedTable] = this.splitTablePath(
19,155✔
1905
                        fk.referencedTableName,
19,155✔
1906
                    )
19,155✔
1907
                    const columnNames = fk.columnNames
19,155✔
1908
                        .map((columnName) => `"${columnName}"`)
19,155✔
1909
                        .join(", ")
19,155✔
1910
                    if (!fk.name)
19,155✔
1911
                        fk.name = this.connection.namingStrategy.foreignKeyName(
19,155✔
1912
                            newTableName,
33✔
1913
                            fk.columnNames,
33✔
1914
                            this.getTablePath(fk),
33✔
1915
                            fk.referencedColumnNames,
33✔
1916
                        )
33✔
1917
                    const referencedColumnNames = fk.referencedColumnNames
19,155✔
1918
                        .map((columnName) => `"${columnName}"`)
19,155✔
1919
                        .join(", ")
19,155✔
1920

19,155✔
1921
                    let constraint = `CONSTRAINT "${fk.name}" FOREIGN KEY (${columnNames}) REFERENCES "${referencedTable}" (${referencedColumnNames})`
19,155✔
1922
                    if (fk.onDelete) constraint += ` ON DELETE ${fk.onDelete}`
19,155✔
1923
                    if (fk.onUpdate) constraint += ` ON UPDATE ${fk.onUpdate}`
19,155✔
1924
                    if (fk.deferrable)
19,155✔
1925
                        constraint += ` DEFERRABLE ${fk.deferrable}`
19,155✔
1926

19,155✔
1927
                    return constraint
19,155✔
1928
                })
11,214✔
1929
                .join(", ")
11,214✔
1930

11,214✔
1931
            sql += `, ${foreignKeysSql}`
11,214✔
1932
        }
11,214✔
1933

52,698✔
1934
        if (primaryColumns.length > 1) {
52,698✔
1935
            const columnNames = primaryColumns
15,636✔
1936
                .map((column) => `"${column.name}"`)
15,636✔
1937
                .join(", ")
15,636✔
1938
            sql += `, PRIMARY KEY (${columnNames})`
15,636✔
1939
        }
15,636✔
1940

52,698✔
1941
        sql += `)`
52,698✔
1942

52,698✔
1943
        if (table.withoutRowid) {
52,698✔
1944
            sql += " WITHOUT ROWID"
213✔
1945
        }
213✔
1946

52,698✔
1947
        return new Query(sql)
52,698✔
1948
    }
52,698✔
1949

23✔
1950
    /**
23✔
1951
     * Builds drop table sql.
23✔
1952
     * @param tableOrName
23✔
1953
     * @param ifExist
23✔
1954
     */
23✔
1955
    protected dropTableSql(
23✔
1956
        tableOrName: Table | string,
52,698✔
1957
        ifExist?: boolean,
52,698✔
1958
    ): Query {
52,698✔
1959
        const tableName = InstanceChecker.isTable(tableOrName)
52,698✔
1960
            ? tableOrName.name
52,698✔
1961
            : tableOrName
52,698!
1962
        const query = ifExist
52,698✔
1963
            ? `DROP TABLE IF EXISTS ${this.escapePath(tableName)}`
52,698!
1964
            : `DROP TABLE ${this.escapePath(tableName)}`
52,698✔
1965
        return new Query(query)
52,698✔
1966
    }
52,698✔
1967

23✔
1968
    protected createViewSql(view: View): Query {
23✔
1969
        if (typeof view.expression === "string") {
45!
1970
            return new Query(`CREATE VIEW "${view.name}" AS ${view.expression}`)
9✔
1971
        } else {
45✔
1972
            return new Query(
36✔
1973
                `CREATE VIEW "${view.name}" AS ${view
36✔
1974
                    .expression(this.connection)
36✔
1975
                    .getQuery()}`,
36✔
1976
            )
36✔
1977
        }
36✔
1978
    }
45✔
1979

23✔
1980
    protected insertViewDefinitionSql(view: View): Query {
23✔
1981
        const expression =
42✔
1982
            typeof view.expression === "string"
42✔
1983
                ? view.expression.trim()
42!
1984
                : view.expression(this.connection).getQuery()
42✔
1985
        return this.insertTypeormMetadataSql({
42✔
1986
            type: MetadataTableType.VIEW,
42✔
1987
            name: view.name,
42✔
1988
            value: expression,
42✔
1989
        })
42✔
1990
    }
42✔
1991

23✔
1992
    /**
23✔
1993
     * Builds drop view sql.
23✔
1994
     * @param viewOrPath
23✔
1995
     */
23✔
1996
    protected dropViewSql(viewOrPath: View | string): Query {
23✔
1997
        const viewName = InstanceChecker.isView(viewOrPath)
45✔
1998
            ? viewOrPath.name
45✔
1999
            : viewOrPath
45!
2000
        return new Query(`DROP VIEW "${viewName}"`)
45✔
2001
    }
45✔
2002

23✔
2003
    /**
23✔
2004
     * Builds remove view sql.
23✔
2005
     * @param viewOrPath
23✔
2006
     */
23✔
2007
    protected deleteViewDefinitionSql(viewOrPath: View | string): Query {
23✔
2008
        const viewName = InstanceChecker.isView(viewOrPath)
42✔
2009
            ? viewOrPath.name
42✔
2010
            : viewOrPath
42!
2011
        return this.deleteTypeormMetadataSql({
42✔
2012
            type: MetadataTableType.VIEW,
42✔
2013
            name: viewName,
42✔
2014
        })
42✔
2015
    }
42✔
2016

23✔
2017
    /**
23✔
2018
     * Builds create index sql.
23✔
2019
     * @param table
23✔
2020
     * @param index
23✔
2021
     */
23✔
2022
    protected createIndexSql(table: Table, index: TableIndex): Query {
23✔
2023
        const columns = index.columnNames
29,136✔
2024
            .map((columnName) => `"${columnName}"`)
29,136✔
2025
            .join(", ")
29,136✔
2026
        const [database, tableName] = this.splitTablePath(table.name)
29,136✔
2027
        return new Query(
29,136✔
2028
            `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX ${
29,136✔
2029
                database ? `"${database}".` : ""
29,136!
2030
            }${this.escapePath(index.name!)} ON "${tableName}" (${columns}) ${
29,136✔
2031
                index.where ? "WHERE " + index.where : ""
29,136!
2032
            }`,
29,136✔
2033
        )
29,136✔
2034
    }
29,136✔
2035

23✔
2036
    /**
23✔
2037
     * Builds drop index sql.
23✔
2038
     * @param indexOrName
23✔
2039
     */
23✔
2040
    protected dropIndexSql(indexOrName: TableIndex | string): Query {
23✔
2041
        const indexName = InstanceChecker.isTableIndex(indexOrName)
29,136✔
2042
            ? indexOrName.name
29,136✔
2043
            : indexOrName
29,136!
2044
        return new Query(`DROP INDEX ${this.escapePath(indexName!)}`)
29,136✔
2045
    }
29,136✔
2046

23✔
2047
    /**
23✔
2048
     * Builds a query for create column.
23✔
2049
     * @param column
23✔
2050
     * @param skipPrimary
23✔
2051
     */
23✔
2052
    protected buildCreateColumnSql(
23✔
2053
        column: TableColumn,
172,134✔
2054
        skipPrimary?: boolean,
172,134✔
2055
    ): string {
172,134✔
2056
        let c = '"' + column.name + '"'
172,134✔
2057
        if (InstanceChecker.isColumnMetadata(column)) {
172,134!
2058
            c += " " + this.driver.normalizeType(column)
×
2059
        } else {
172,134✔
2060
            c += " " + this.connection.driver.createFullType(column)
172,134✔
2061
        }
172,134✔
2062
        if (column.enum && !column.isArray)
172,134!
2063
            c +=
172,134!
2064
                ' CHECK( "' +
81✔
2065
                column.name +
81✔
2066
                '" IN (' +
81✔
2067
                column.enum.map((val) => "'" + val + "'").join(",") +
81✔
2068
                ") )"
81✔
2069
        if (column.isPrimary && !skipPrimary) c += " PRIMARY KEY"
172,134✔
2070
        if (
172,134✔
2071
            column.isGenerated === true &&
172,134✔
2072
            column.generationStrategy === "increment"
28,278✔
2073
        )
172,134✔
2074
            // don't use skipPrimary here since updates can update already exist primary without auto inc.
172,134✔
2075
            c += " AUTOINCREMENT"
172,134✔
2076
        if (column.collation) c += " COLLATE " + column.collation
172,134!
2077
        if (column.isNullable !== true) c += " NOT NULL"
172,134✔
2078

172,134✔
2079
        if (column.asExpression) {
172,134!
2080
            c += ` AS (${column.asExpression}) ${
159✔
2081
                column.generatedType ? column.generatedType : "VIRTUAL"
159!
2082
            }`
159✔
2083
        } else {
172,134✔
2084
            if (column.default !== undefined && column.default !== null)
171,975✔
2085
                c += " DEFAULT (" + column.default + ")"
171,975✔
2086
        }
171,975✔
2087

172,134✔
2088
        return c
172,134✔
2089
    }
172,134✔
2090

23✔
2091
    protected async recreateTable(
23✔
2092
        newTable: Table,
11,559✔
2093
        oldTable: Table,
11,559✔
2094
        migrateData = true,
11,559✔
2095
    ): Promise<void> {
11,559✔
2096
        const upQueries: Query[] = []
11,559✔
2097
        const downQueries: Query[] = []
11,559✔
2098

11,559✔
2099
        // drop old table indices
11,559✔
2100
        oldTable.indices.forEach((index) => {
11,559✔
2101
            upQueries.push(this.dropIndexSql(index))
9,378✔
2102
            downQueries.push(this.createIndexSql(oldTable, index))
9,378✔
2103
        })
11,559✔
2104

11,559✔
2105
        // change table name into 'temporary_table'
11,559✔
2106
        let [databaseNew, tableNameNew] = this.splitTablePath(newTable.name)
11,559✔
2107
        const [, tableNameOld] = this.splitTablePath(oldTable.name)
11,559✔
2108
        newTable.name = tableNameNew = `${
11,559✔
2109
            databaseNew ? `${databaseNew}.` : ""
11,559!
2110
        }temporary_${tableNameNew}`
11,559✔
2111

11,559✔
2112
        // create new table
11,559✔
2113
        upQueries.push(this.createTableSql(newTable, true, true))
11,559✔
2114
        downQueries.push(this.dropTableSql(newTable))
11,559✔
2115

11,559✔
2116
        // migrate all data from the old table into new table
11,559✔
2117
        if (migrateData) {
11,559✔
2118
            let newColumnNames = newTable.columns
11,559✔
2119
                .filter((column) => !column.generatedType)
11,559✔
2120
                .map((column) => `"${column.name}"`)
11,559✔
2121

11,559✔
2122
            let oldColumnNames = oldTable.columns
11,559✔
2123
                .filter((column) => !column.generatedType)
11,559✔
2124
                .map((column) => `"${column.name}"`)
11,559✔
2125

11,559✔
2126
            if (oldColumnNames.length < newColumnNames.length) {
11,559✔
2127
                newColumnNames = newTable.columns
51✔
2128
                    .filter((column) => {
51✔
2129
                        const oldColumn = oldTable.columns.find(
261✔
2130
                            (c) => c.name === column.name,
261✔
2131
                        )
261✔
2132
                        if (oldColumn && oldColumn.generatedType) return false
261!
2133
                        return !column.generatedType && oldColumn
252✔
2134
                    })
51✔
2135
                    .map((column) => `"${column.name}"`)
51✔
2136
            } else if (oldColumnNames.length > newColumnNames.length) {
11,559✔
2137
                oldColumnNames = oldTable.columns
36✔
2138
                    .filter((column) => {
36✔
2139
                        return (
144✔
2140
                            !column.generatedType &&
144✔
2141
                            newTable.columns.find((c) => c.name === column.name)
144✔
2142
                        )
144✔
2143
                    })
36✔
2144
                    .map((column) => `"${column.name}"`)
36✔
2145
            }
36✔
2146

11,559✔
2147
            upQueries.push(
11,559✔
2148
                new Query(
11,559✔
2149
                    `INSERT INTO ${this.escapePath(
11,559✔
2150
                        newTable.name,
11,559✔
2151
                    )}(${newColumnNames.join(
11,559✔
2152
                        ", ",
11,559✔
2153
                    )}) SELECT ${oldColumnNames.join(
11,559✔
2154
                        ", ",
11,559✔
2155
                    )} FROM ${this.escapePath(oldTable.name)}`,
11,559✔
2156
                ),
11,559✔
2157
            )
11,559✔
2158
            downQueries.push(
11,559✔
2159
                new Query(
11,559✔
2160
                    `INSERT INTO ${this.escapePath(
11,559✔
2161
                        oldTable.name,
11,559✔
2162
                    )}(${oldColumnNames.join(
11,559✔
2163
                        ", ",
11,559✔
2164
                    )}) SELECT ${newColumnNames.join(
11,559✔
2165
                        ", ",
11,559✔
2166
                    )} FROM ${this.escapePath(newTable.name)}`,
11,559✔
2167
                ),
11,559✔
2168
            )
11,559✔
2169
        }
11,559✔
2170

11,559✔
2171
        // drop old table
11,559✔
2172
        upQueries.push(this.dropTableSql(oldTable))
11,559✔
2173
        downQueries.push(this.createTableSql(oldTable, true))
11,559✔
2174

11,559✔
2175
        // rename old table
11,559✔
2176
        upQueries.push(
11,559✔
2177
            new Query(
11,559✔
2178
                `ALTER TABLE ${this.escapePath(
11,559✔
2179
                    newTable.name,
11,559✔
2180
                )} RENAME TO ${this.escapePath(tableNameOld)}`,
11,559✔
2181
            ),
11,559✔
2182
        )
11,559✔
2183
        downQueries.push(
11,559✔
2184
            new Query(
11,559✔
2185
                `ALTER TABLE ${this.escapePath(
11,559✔
2186
                    oldTable.name,
11,559✔
2187
                )} RENAME TO ${this.escapePath(tableNameNew)}`,
11,559✔
2188
            ),
11,559✔
2189
        )
11,559✔
2190

11,559✔
2191
        newTable.name = oldTable.name
11,559✔
2192

11,559✔
2193
        // recreate table indices
11,559✔
2194
        newTable.indices.forEach((index) => {
11,559✔
2195
            // new index may be passed without name. In this case we generate index name manually.
9,378✔
2196
            if (!index.name)
9,378✔
2197
                index.name = this.connection.namingStrategy.indexName(
9,378!
2198
                    newTable,
×
2199
                    index.columnNames,
×
2200
                    index.where,
×
2201
                )
×
2202
            upQueries.push(this.createIndexSql(newTable, index))
9,378✔
2203
            downQueries.push(this.dropIndexSql(index))
9,378✔
2204
        })
11,559✔
2205

11,559✔
2206
        // update generated columns in "typeorm_metadata" table
11,559✔
2207
        // Step 1: clear data for removed generated columns
11,559✔
2208
        oldTable.columns
11,559✔
2209
            .filter((column) => {
11,559✔
2210
                const newTableColumn = newTable.columns.find(
40,194✔
2211
                    (c) => c.name === column.name,
40,194✔
2212
                )
40,194✔
2213
                // we should delete record from "typeorm_metadata" if generated column was removed
40,194✔
2214
                // or it was changed to non-generated
40,194✔
2215
                return (
40,194✔
2216
                    column.generatedType &&
40,194!
2217
                    column.asExpression &&
40,194!
2218
                    (!newTableColumn ||
45✔
2219
                        (!newTableColumn.generatedType &&
39✔
2220
                            !newTableColumn.asExpression))
45✔
2221
                )
40,194✔
2222
            })
11,559✔
2223
            .forEach((column) => {
11,559✔
2224
                const deleteQuery = this.deleteTypeormMetadataSql({
9✔
2225
                    table: oldTable.name,
9✔
2226
                    type: MetadataTableType.GENERATED_COLUMN,
9✔
2227
                    name: column.name,
9✔
2228
                })
9✔
2229

9✔
2230
                const insertQuery = this.insertTypeormMetadataSql({
9✔
2231
                    table: oldTable.name,
9✔
2232
                    type: MetadataTableType.GENERATED_COLUMN,
9✔
2233
                    name: column.name,
9✔
2234
                    value: column.asExpression,
9✔
2235
                })
9✔
2236

9✔
2237
                upQueries.push(deleteQuery)
9✔
2238
                downQueries.push(insertQuery)
9✔
2239
            })
11,559✔
2240

11,559✔
2241
        // Step 2: add data for new generated columns
11,559✔
2242
        newTable.columns
11,559✔
2243
            .filter(
11,559✔
2244
                (column) =>
11,559✔
2245
                    column.generatedType &&
40,182!
2246
                    column.asExpression &&
40,182!
2247
                    !oldTable.columns.some((c) => c.name === column.name),
11,559✔
2248
            )
11,559✔
2249
            .forEach((column) => {
11,559✔
2250
                const insertQuery = this.insertTypeormMetadataSql({
6✔
2251
                    table: newTable.name,
6✔
2252
                    type: MetadataTableType.GENERATED_COLUMN,
6✔
2253
                    name: column.name,
6✔
2254
                    value: column.asExpression,
6✔
2255
                })
6✔
2256

6✔
2257
                const deleteQuery = this.deleteTypeormMetadataSql({
6✔
2258
                    table: newTable.name,
6✔
2259
                    type: MetadataTableType.GENERATED_COLUMN,
6✔
2260
                    name: column.name,
6✔
2261
                })
6✔
2262

6✔
2263
                upQueries.push(insertQuery)
6✔
2264
                downQueries.push(deleteQuery)
6✔
2265
            })
11,559✔
2266

11,559✔
2267
        // Step 3: update changed expressions
11,559✔
2268
        newTable.columns
11,559✔
2269
            .filter((column) => column.generatedType && column.asExpression)
11,559!
2270
            .forEach((column) => {
11,559✔
2271
                const oldColumn = oldTable.columns.find(
42✔
2272
                    (c) =>
42✔
2273
                        c.name === column.name &&
345✔
2274
                        c.generatedType &&
345✔
2275
                        column.generatedType &&
345✔
2276
                        c.asExpression !== column.asExpression,
42✔
2277
                )
42✔
2278

42✔
2279
                if (!oldColumn) return
42✔
2280

3✔
2281
                // update expression
3✔
2282
                const deleteQuery = this.deleteTypeormMetadataSql({
3✔
2283
                    table: oldTable.name,
3✔
2284
                    type: MetadataTableType.GENERATED_COLUMN,
3✔
2285
                    name: oldColumn.name,
3✔
2286
                })
3✔
2287

3✔
2288
                const insertQuery = this.insertTypeormMetadataSql({
3✔
2289
                    table: newTable.name,
3✔
2290
                    type: MetadataTableType.GENERATED_COLUMN,
3✔
2291
                    name: column.name,
3✔
2292
                    value: column.asExpression,
3✔
2293
                })
3✔
2294

3✔
2295
                upQueries.push(deleteQuery)
3✔
2296
                upQueries.push(insertQuery)
3✔
2297

3✔
2298
                // revert update
3✔
2299
                const revertInsertQuery = this.insertTypeormMetadataSql({
3✔
2300
                    table: newTable.name,
3✔
2301
                    type: MetadataTableType.GENERATED_COLUMN,
3✔
2302
                    name: oldColumn.name,
3✔
2303
                    value: oldColumn.asExpression,
3✔
2304
                })
3✔
2305

3✔
2306
                const revertDeleteQuery = this.deleteTypeormMetadataSql({
3✔
2307
                    table: oldTable.name,
3✔
2308
                    type: MetadataTableType.GENERATED_COLUMN,
3✔
2309
                    name: column.name,
3✔
2310
                })
3✔
2311

3✔
2312
                downQueries.push(revertInsertQuery)
3✔
2313
                downQueries.push(revertDeleteQuery)
3✔
2314
            })
11,559✔
2315

11,559✔
2316
        await this.executeQueries(upQueries, downQueries)
11,559✔
2317
        this.replaceCachedTable(oldTable, newTable)
11,553✔
2318
    }
11,553✔
2319

23✔
2320
    /**
23✔
2321
     * tablePath e.g. "myDB.myTable", "myTable"
23✔
2322
     * @param tablePath
23✔
2323
     */
23✔
2324
    protected splitTablePath(tablePath: string): [string | undefined, string] {
23✔
2325
        return (
203,526✔
2326
            tablePath.indexOf(".") !== -1
203,526✔
2327
                ? tablePath.split(".")
203,526!
2328
                : [undefined, tablePath]
203,526✔
2329
        ) as [string | undefined, string]
203,526✔
2330
    }
203,526✔
2331

23✔
2332
    /**
23✔
2333
     * Escapes given table or view path. Tolerates leading/trailing dots
23✔
2334
     * @param target
23✔
2335
     * @param disableEscape
23✔
2336
     */
23✔
2337
    protected escapePath(
23✔
2338
        target: Table | View | string,
256,584✔
2339
        disableEscape?: boolean,
256,584✔
2340
    ): string {
256,584✔
2341
        const tableName =
256,584✔
2342
            InstanceChecker.isTable(target) || InstanceChecker.isView(target)
256,584✔
2343
                ? target.name
256,584!
2344
                : target
256,584✔
2345
        return tableName
256,584✔
2346
            .replace(/^\.+|\.+$/g, "")
256,584✔
2347
            .split(".")
256,584✔
2348
            .map((i) => (disableEscape ? i : `"${i}"`))
256,584!
2349
            .join(".")
256,584✔
2350
    }
256,584✔
2351

23✔
2352
    /**
23✔
2353
     * Change table comment.
23✔
2354
     * @param tableOrName
23✔
2355
     * @param comment
23✔
2356
     */
23✔
2357
    changeTableComment(
23✔
2358
        tableOrName: Table | string,
×
2359
        comment?: string,
×
2360
    ): Promise<void> {
×
2361
        throw new TypeORMError(`sqlit driver does not support change comment.`)
×
2362
    }
×
2363
}
23✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc