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

typeorm / typeorm / 19549987525

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

push

github

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

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

26500 of 32174 branches covered (82.36%)

Branch coverage included in aggregate %.

91252 of 113615 relevant lines covered (80.32%)

88980.79 hits per line

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

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

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

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

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

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

26✔
45
    constructor() {
26✔
46
        super()
4,179✔
47
    }
4,179✔
48

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

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

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

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

85,050✔
80
        if (
85,050✔
81
            this.isTransactionActive &&
85,050✔
82
            this.driver.transactionSupport === "simple"
144✔
83
        )
85,050✔
84
            throw new TransactionAlreadyStartedError()
85,050!
85

85,050✔
86
        if (
85,050✔
87
            isolationLevel &&
85,050✔
88
            isolationLevel !== "READ UNCOMMITTED" &&
85,050✔
89
            isolationLevel !== "SERIALIZABLE"
15✔
90
        )
85,050✔
91
            throw new TypeORMError(
85,050!
92
                `SQLite only supports SERIALIZABLE and READ UNCOMMITTED isolation`,
×
93
            )
×
94

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

85,050✔
103
        if (this.transactionDepth === 0) {
85,050✔
104
            if (isolationLevel) {
84,906✔
105
                if (isolationLevel === "READ UNCOMMITTED") {
30✔
106
                    await this.query("PRAGMA read_uncommitted = true")
15✔
107
                } else {
15✔
108
                    await this.query("PRAGMA read_uncommitted = false")
15✔
109
                }
15✔
110
            }
30✔
111
            await this.query("BEGIN TRANSACTION")
84,906✔
112
        } else {
85,050✔
113
            await this.query(`SAVEPOINT typeorm_${this.transactionDepth}`)
144✔
114
        }
144✔
115
        this.transactionDepth += 1
85,050✔
116

85,050✔
117
        await this.broadcaster.broadcast("AfterTransactionStart")
85,050✔
118
    }
85,050✔
119

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

84,819✔
127
        await this.broadcaster.broadcast("BeforeTransactionCommit")
84,819✔
128

84,819✔
129
        if (this.transactionDepth > 1) {
84,819✔
130
            await this.query(
90✔
131
                `RELEASE SAVEPOINT typeorm_${this.transactionDepth - 1}`,
90✔
132
            )
90✔
133
        } else {
84,819✔
134
            await this.query("COMMIT")
84,729✔
135
            this.isTransactionActive = false
84,723✔
136
        }
84,723✔
137
        this.transactionDepth -= 1
84,813✔
138

84,813✔
139
        await this.broadcaster.broadcast("AfterTransactionCommit")
84,813✔
140
    }
84,813✔
141

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

237✔
149
        await this.broadcaster.broadcast("BeforeTransactionRollback")
237✔
150

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

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

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

26✔
176
    /**
26✔
177
     * Returns all available database names including system databases.
26✔
178
     */
26✔
179
    async getDatabases(): Promise<string[]> {
26✔
180
        return Promise.resolve([])
×
181
    }
×
182

26✔
183
    /**
26✔
184
     * Returns all available schema names including system schemas.
26✔
185
     * If database parameter specified, returns schemas of that database.
26✔
186
     */
26✔
187
    async getSchemas(database?: string): Promise<string[]> {
26✔
188
        return Promise.resolve([])
×
189
    }
×
190

26✔
191
    /**
26✔
192
     * Checks if database with the given name exist.
26✔
193
     */
26✔
194
    async hasDatabase(database: string): Promise<boolean> {
26✔
195
        return Promise.resolve(false)
×
196
    }
×
197

26✔
198
    /**
26✔
199
     * Loads currently using database
26✔
200
     */
26✔
201
    async getCurrentDatabase(): Promise<undefined> {
26✔
202
        return Promise.resolve(undefined)
×
203
    }
×
204

26✔
205
    /**
26✔
206
     * Checks if schema with the given name exist.
26✔
207
     */
26✔
208
    async hasSchema(schema: string): Promise<boolean> {
26✔
209
        throw new TypeORMError(`This driver does not support table schemas`)
×
210
    }
×
211

26✔
212
    /**
26✔
213
     * Loads currently using database schema
26✔
214
     */
26✔
215
    async getCurrentSchema(): Promise<undefined> {
26✔
216
        return Promise.resolve(undefined)
×
217
    }
×
218

26✔
219
    /**
26✔
220
     * Checks if table with the given name exist in the database.
26✔
221
     */
26✔
222
    async hasTable(tableOrName: Table | string): Promise<boolean> {
26✔
223
        const tableName = InstanceChecker.isTable(tableOrName)
12,936✔
224
            ? tableOrName.name
12,936✔
225
            : tableOrName
12,936✔
226
        const sql = `SELECT * FROM "sqlite_master" WHERE "type" = 'table' AND "name" = '${tableName}'`
12,936✔
227
        const result = await this.query(sql)
12,936✔
228
        return result.length ? true : false
12,936✔
229
    }
12,936✔
230

26✔
231
    /**
26✔
232
     * Checks if column with the given name exist in the given table.
26✔
233
     */
26✔
234
    async hasColumn(
26✔
235
        tableOrName: Table | string,
36✔
236
        columnName: string,
36✔
237
    ): Promise<boolean> {
36✔
238
        const tableName = InstanceChecker.isTable(tableOrName)
36✔
239
            ? tableOrName.name
36!
240
            : tableOrName
36✔
241
        const sql = `PRAGMA table_xinfo(${this.escapePath(tableName)})`
36✔
242
        const columns: ObjectLiteral[] = await this.query(sql)
36✔
243
        return !!columns.find((column) => column["name"] === columnName)
36✔
244
    }
36✔
245

26✔
246
    /**
26✔
247
     * Creates a new database.
26✔
248
     */
26✔
249
    async createDatabase(
26✔
250
        database: string,
15✔
251
        ifNotExist?: boolean,
15✔
252
    ): Promise<void> {
15✔
253
        return Promise.resolve()
15✔
254
    }
15✔
255

26✔
256
    /**
26✔
257
     * Drops database.
26✔
258
     */
26✔
259
    async dropDatabase(database: string, ifExist?: boolean): Promise<void> {
26✔
260
        return Promise.resolve()
×
261
    }
×
262

26✔
263
    /**
26✔
264
     * Creates a new table schema.
26✔
265
     */
26✔
266
    async createSchema(
26✔
267
        schemaPath: string,
×
268
        ifNotExist?: boolean,
×
269
    ): Promise<void> {
×
270
        return Promise.resolve()
×
271
    }
×
272

26✔
273
    /**
26✔
274
     * Drops table schema.
26✔
275
     */
26✔
276
    async dropSchema(schemaPath: string, ifExist?: boolean): Promise<void> {
26✔
277
        return Promise.resolve()
×
278
    }
×
279

26✔
280
    /**
26✔
281
     * Creates a new table.
26✔
282
     */
26✔
283
    async createTable(
26✔
284
        table: Table,
40,662✔
285
        ifNotExist: boolean = false,
40,662✔
286
        createForeignKeys: boolean = true,
40,662✔
287
        createIndices: boolean = true,
40,662✔
288
    ): Promise<void> {
40,662✔
289
        const upQueries: Query[] = []
40,662✔
290
        const downQueries: Query[] = []
40,662✔
291

40,662✔
292
        if (ifNotExist) {
40,662✔
293
            const isTableExist = await this.hasTable(table)
243✔
294
            if (isTableExist) return Promise.resolve()
243✔
295
        }
243✔
296

40,653✔
297
        upQueries.push(this.createTableSql(table, createForeignKeys))
40,653✔
298
        downQueries.push(this.dropTableSql(table))
40,653✔
299

40,653✔
300
        if (createIndices) {
40,653✔
301
            table.indices.forEach((index) => {
40,653✔
302
                // new index may be passed without name. In this case we generate index name manually.
14,274✔
303
                if (!index.name)
14,274✔
304
                    index.name = this.connection.namingStrategy.indexName(
14,274✔
305
                        table,
36✔
306
                        index.columnNames,
36✔
307
                        index.where,
36✔
308
                    )
36✔
309
                upQueries.push(this.createIndexSql(table, index))
14,274✔
310
                downQueries.push(this.dropIndexSql(index))
14,274✔
311
            })
40,653✔
312
        }
40,653✔
313

40,653✔
314
        // if table have column with generated type, we must add the expression to the metadata table
40,653✔
315
        const generatedColumns = table.columns.filter(
40,653✔
316
            (column) => column.generatedType && column.asExpression,
40,653!
317
        )
40,653✔
318

40,653✔
319
        for (const column of generatedColumns) {
40,662!
320
            const insertQuery = this.insertTypeormMetadataSql({
63✔
321
                table: table.name,
63✔
322
                type: MetadataTableType.GENERATED_COLUMN,
63✔
323
                name: column.name,
63✔
324
                value: column.asExpression,
63✔
325
            })
63✔
326

63✔
327
            const deleteQuery = this.deleteTypeormMetadataSql({
63✔
328
                table: table.name,
63✔
329
                type: MetadataTableType.GENERATED_COLUMN,
63✔
330
                name: column.name,
63✔
331
            })
63✔
332

63✔
333
            upQueries.push(insertQuery)
63✔
334
            downQueries.push(deleteQuery)
63✔
335
        }
63✔
336

40,653✔
337
        await this.executeQueries(upQueries, downQueries)
40,653✔
338
    }
40,653✔
339

26✔
340
    /**
26✔
341
     * Drops the table.
26✔
342
     */
26✔
343
    async dropTable(
26✔
344
        tableOrName: Table | string,
93✔
345
        ifExist?: boolean,
93✔
346
        dropForeignKeys: boolean = true,
93✔
347
        dropIndices: boolean = true,
93✔
348
    ): Promise<void> {
93✔
349
        if (ifExist) {
93!
350
            const isTableExist = await this.hasTable(tableOrName)
×
351
            if (!isTableExist) return Promise.resolve()
×
352
        }
×
353

93✔
354
        // if dropTable called with dropForeignKeys = true, we must create foreign keys in down query.
93✔
355
        const createForeignKeys: boolean = dropForeignKeys
93✔
356
        const table = InstanceChecker.isTable(tableOrName)
93✔
357
            ? tableOrName
93✔
358
            : await this.getCachedTable(tableOrName)
93✔
359
        const upQueries: Query[] = []
63✔
360
        const downQueries: Query[] = []
63✔
361

63✔
362
        if (dropIndices) {
93✔
363
            table.indices.forEach((index) => {
93✔
364
                upQueries.push(this.dropIndexSql(index))
9✔
365
                downQueries.push(this.createIndexSql(table, index))
9✔
366
            })
93✔
367
        }
93✔
368

93✔
369
        upQueries.push(this.dropTableSql(table, ifExist))
93✔
370
        downQueries.push(this.createTableSql(table, createForeignKeys))
93✔
371

93✔
372
        // if table had columns with generated type, we must remove the expression from the metadata table
93✔
373
        const generatedColumns = table.columns.filter(
93✔
374
            (column) => column.generatedType && column.asExpression,
93!
375
        )
93✔
376

93✔
377
        for (const column of generatedColumns) {
93!
378
            const deleteQuery = this.deleteTypeormMetadataSql({
9✔
379
                table: table.name,
9✔
380
                type: MetadataTableType.GENERATED_COLUMN,
9✔
381
                name: column.name,
9✔
382
            })
9✔
383

9✔
384
            const insertQuery = this.insertTypeormMetadataSql({
9✔
385
                table: table.name,
9✔
386
                type: MetadataTableType.GENERATED_COLUMN,
9✔
387
                name: column.name,
9✔
388
                value: column.asExpression,
9✔
389
            })
9✔
390

9✔
391
            upQueries.push(deleteQuery)
9✔
392
            downQueries.push(insertQuery)
9✔
393
        }
9✔
394

93✔
395
        await this.executeQueries(upQueries, downQueries)
93✔
396
    }
93✔
397

26✔
398
    /**
26✔
399
     * Creates a new view.
26✔
400
     */
26✔
401
    async createView(
26✔
402
        view: View,
72✔
403
        syncWithMetadata: boolean = false,
72✔
404
    ): Promise<void> {
72✔
405
        const upQueries: Query[] = []
72✔
406
        const downQueries: Query[] = []
72✔
407
        upQueries.push(this.createViewSql(view))
72✔
408
        if (syncWithMetadata) upQueries.push(this.insertViewDefinitionSql(view))
72✔
409
        downQueries.push(this.dropViewSql(view))
72✔
410
        if (syncWithMetadata)
72✔
411
            downQueries.push(this.deleteViewDefinitionSql(view))
72✔
412
        await this.executeQueries(upQueries, downQueries)
72✔
413
    }
72✔
414

26✔
415
    /**
26✔
416
     * Drops the view.
26✔
417
     */
26✔
418
    async dropView(target: View | string): Promise<void> {
26✔
419
        const viewName = InstanceChecker.isView(target) ? target.name : target
×
420
        const view = await this.getCachedView(viewName)
×
421

×
422
        const upQueries: Query[] = []
×
423
        const downQueries: Query[] = []
×
424
        upQueries.push(this.deleteViewDefinitionSql(view))
×
425
        upQueries.push(this.dropViewSql(view))
×
426
        downQueries.push(this.insertViewDefinitionSql(view))
×
427
        downQueries.push(this.createViewSql(view))
×
428
        await this.executeQueries(upQueries, downQueries)
×
429
    }
×
430

26✔
431
    /**
26✔
432
     * Renames the given table.
26✔
433
     */
26✔
434
    async renameTable(
26✔
435
        oldTableOrName: Table | string,
117✔
436
        newTableName: string,
117✔
437
    ): Promise<void> {
117✔
438
        const oldTable = InstanceChecker.isTable(oldTableOrName)
117✔
439
            ? oldTableOrName
117✔
440
            : await this.getCachedTable(oldTableOrName)
117✔
441
        const newTable = oldTable.clone()
99✔
442

99✔
443
        newTable.name = newTableName
99✔
444

99✔
445
        // rename table
99✔
446
        const up = new Query(
99✔
447
            `ALTER TABLE ${this.escapePath(
99✔
448
                oldTable.name,
99✔
449
            )} RENAME TO ${this.escapePath(newTableName)}`,
99✔
450
        )
99✔
451
        const down = new Query(
99✔
452
            `ALTER TABLE ${this.escapePath(
99✔
453
                newTableName,
99✔
454
            )} RENAME TO ${this.escapePath(oldTable.name)}`,
99✔
455
        )
99✔
456
        await this.executeQueries(up, down)
99✔
457

117✔
458
        // rename unique constraints
117✔
459
        newTable.uniques.forEach((unique) => {
117✔
460
            const oldUniqueName =
63✔
461
                this.connection.namingStrategy.uniqueConstraintName(
63✔
462
                    oldTable,
63✔
463
                    unique.columnNames,
63✔
464
                )
63✔
465

63✔
466
            // Skip renaming if Unique has user defined constraint name
63✔
467
            if (unique.name !== oldUniqueName) return
63✔
468

27✔
469
            unique.name = this.connection.namingStrategy.uniqueConstraintName(
27✔
470
                newTable,
27✔
471
                unique.columnNames,
27✔
472
            )
27✔
473
        })
117✔
474

117✔
475
        // rename foreign key constraints
117✔
476
        newTable.foreignKeys.forEach((foreignKey) => {
117✔
477
            const oldForeignKeyName =
81✔
478
                this.connection.namingStrategy.foreignKeyName(
81✔
479
                    oldTable,
81✔
480
                    foreignKey.columnNames,
81✔
481
                    this.getTablePath(foreignKey),
81✔
482
                    foreignKey.referencedColumnNames,
81✔
483
                )
81✔
484

81✔
485
            // Skip renaming if foreign key has user defined constraint name
81✔
486
            if (foreignKey.name !== oldForeignKeyName) return
81✔
487

9✔
488
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
9✔
489
                newTable,
9✔
490
                foreignKey.columnNames,
9✔
491
                this.getTablePath(foreignKey),
9✔
492
                foreignKey.referencedColumnNames,
9✔
493
            )
9✔
494
        })
117✔
495

117✔
496
        // rename indices
117✔
497
        newTable.indices.forEach((index) => {
117✔
498
            const oldIndexName = this.connection.namingStrategy.indexName(
81✔
499
                oldTable,
81✔
500
                index.columnNames,
81✔
501
                index.where,
81✔
502
            )
81✔
503

81✔
504
            // Skip renaming if Index has user defined constraint name
81✔
505
            if (index.name !== oldIndexName) return
81✔
506

45✔
507
            index.name = this.connection.namingStrategy.indexName(
45✔
508
                newTable,
45✔
509
                index.columnNames,
45✔
510
                index.where,
45✔
511
            )
45✔
512
        })
117✔
513

117✔
514
        // rename old table;
117✔
515
        oldTable.name = newTable.name
117✔
516

117✔
517
        // recreate table with new constraint names
117✔
518
        await this.recreateTable(newTable, oldTable)
117✔
519
    }
117✔
520

26✔
521
    /**
26✔
522
     * Creates a new column from the column in the table.
26✔
523
     */
26✔
524
    async addColumn(
26✔
525
        tableOrName: Table | string,
42✔
526
        column: TableColumn,
42✔
527
    ): Promise<void> {
42✔
528
        const table = InstanceChecker.isTable(tableOrName)
42✔
529
            ? tableOrName
42✔
530
            : await this.getCachedTable(tableOrName)
42✔
531
        return this.addColumns(table!, [column])
27✔
532
    }
27✔
533

26✔
534
    /**
26✔
535
     * Creates a new columns from the column in the table.
26✔
536
     */
26✔
537
    async addColumns(
26✔
538
        tableOrName: Table | string,
78✔
539
        columns: TableColumn[],
78✔
540
    ): Promise<void> {
78✔
541
        const table = InstanceChecker.isTable(tableOrName)
78✔
542
            ? tableOrName
78✔
543
            : await this.getCachedTable(tableOrName)
78!
544
        const changedTable = table.clone()
×
545
        columns.forEach((column) => changedTable.addColumn(column))
✔
546
        await this.recreateTable(changedTable, table)
×
547
    }
69✔
548

26✔
549
    /**
26✔
550
     * Renames column in the given table.
26✔
551
     */
26✔
552
    async renameColumn(
26✔
553
        tableOrName: Table | string,
117✔
554
        oldTableColumnOrName: TableColumn | string,
117✔
555
        newTableColumnOrName: TableColumn | string,
117✔
556
    ): Promise<void> {
117✔
557
        const table = InstanceChecker.isTable(tableOrName)
117✔
558
            ? tableOrName
117✔
559
            : await this.getCachedTable(tableOrName)
117✔
560
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
18✔
561
            ? oldTableColumnOrName
117✔
562
            : table.columns.find((c) => c.name === oldTableColumnOrName)
117✔
563
        if (!oldColumn)
117✔
564
            throw new TypeORMError(
117!
565
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
×
566
            )
×
567

117✔
568
        let newColumn: TableColumn | undefined = undefined
117✔
569
        if (InstanceChecker.isTableColumn(newTableColumnOrName)) {
117✔
570
            newColumn = newTableColumnOrName
72✔
571
        } else {
117✔
572
            newColumn = oldColumn.clone()
45✔
573
            newColumn.name = newTableColumnOrName
45✔
574
        }
45✔
575

117✔
576
        return this.changeColumn(table, oldColumn, newColumn)
117✔
577
    }
117✔
578

26✔
579
    /**
26✔
580
     * Changes a column in the table.
26✔
581
     */
26✔
582
    async changeColumn(
26✔
583
        tableOrName: Table | string,
177✔
584
        oldTableColumnOrName: TableColumn | string,
177✔
585
        newColumn: TableColumn,
177✔
586
    ): Promise<void> {
177✔
587
        const table = InstanceChecker.isTable(tableOrName)
177✔
588
            ? tableOrName
177✔
589
            : await this.getCachedTable(tableOrName)
177!
590
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
×
591
            ? oldTableColumnOrName
177✔
592
            : table.columns.find((c) => c.name === oldTableColumnOrName)
177!
593
        if (!oldColumn)
177✔
594
            throw new TypeORMError(
177!
595
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
×
596
            )
×
597

177✔
598
        await this.changeColumns(table, [{ oldColumn, newColumn }])
177✔
599
    }
177✔
600

26✔
601
    /**
26✔
602
     * Changes a column in the table.
26✔
603
     * Changed column looses all its keys in the db.
26✔
604
     */
26✔
605
    async changeColumns(
26✔
606
        tableOrName: Table | string,
369✔
607
        changedColumns: { oldColumn: TableColumn; newColumn: TableColumn }[],
369✔
608
    ): Promise<void> {
369✔
609
        const table = InstanceChecker.isTable(tableOrName)
369✔
610
            ? tableOrName
369✔
611
            : await this.getCachedTable(tableOrName)
369!
612
        const changedTable = table.clone()
×
613
        changedColumns.forEach((changedColumnSet) => {
✔
614
            if (
447✔
615
                changedColumnSet.newColumn.name !==
447✔
616
                changedColumnSet.oldColumn.name
447✔
617
            ) {
447✔
618
                changedTable
186✔
619
                    .findColumnUniques(changedColumnSet.oldColumn)
186✔
620
                    .forEach((unique) => {
186✔
621
                        const uniqueName =
72✔
622
                            this.connection.namingStrategy.uniqueConstraintName(
72✔
623
                                table,
72✔
624
                                unique.columnNames,
72✔
625
                            )
72✔
626

72✔
627
                        unique.columnNames.splice(
72✔
628
                            unique.columnNames.indexOf(
72✔
629
                                changedColumnSet.oldColumn.name,
72✔
630
                            ),
72✔
631
                            1,
72✔
632
                        )
72✔
633
                        unique.columnNames.push(changedColumnSet.newColumn.name)
72✔
634

72✔
635
                        // rename Unique only if it has default constraint name
72✔
636
                        if (unique.name === uniqueName) {
72✔
637
                            unique.name =
36✔
638
                                this.connection.namingStrategy.uniqueConstraintName(
36✔
639
                                    changedTable,
36✔
640
                                    unique.columnNames,
36✔
641
                                )
36✔
642
                        }
36✔
643
                    })
186✔
644

186✔
645
                changedTable
186✔
646
                    .findColumnForeignKeys(changedColumnSet.oldColumn)
186✔
647
                    .forEach((foreignKey) => {
186✔
648
                        const foreignKeyName =
81✔
649
                            this.connection.namingStrategy.foreignKeyName(
81✔
650
                                table,
81✔
651
                                foreignKey.columnNames,
81✔
652
                                this.getTablePath(foreignKey),
81✔
653
                                foreignKey.referencedColumnNames,
81✔
654
                            )
81✔
655

81✔
656
                        foreignKey.columnNames.splice(
81✔
657
                            foreignKey.columnNames.indexOf(
81✔
658
                                changedColumnSet.oldColumn.name,
81✔
659
                            ),
81✔
660
                            1,
81✔
661
                        )
81✔
662
                        foreignKey.columnNames.push(
81✔
663
                            changedColumnSet.newColumn.name,
81✔
664
                        )
81✔
665

81✔
666
                        // rename FK only if it has default constraint name
81✔
667
                        if (foreignKey.name === foreignKeyName) {
81✔
668
                            foreignKey.name =
9✔
669
                                this.connection.namingStrategy.foreignKeyName(
9✔
670
                                    changedTable,
9✔
671
                                    foreignKey.columnNames,
9✔
672
                                    this.getTablePath(foreignKey),
9✔
673
                                    foreignKey.referencedColumnNames,
9✔
674
                                )
9✔
675
                        }
9✔
676
                    })
186✔
677

186✔
678
                changedTable
186✔
679
                    .findColumnIndices(changedColumnSet.oldColumn)
186✔
680
                    .forEach((index) => {
186✔
681
                        const indexName =
63✔
682
                            this.connection.namingStrategy.indexName(
63✔
683
                                table,
63✔
684
                                index.columnNames,
63✔
685
                                index.where,
63✔
686
                            )
63✔
687

63✔
688
                        index.columnNames.splice(
63✔
689
                            index.columnNames.indexOf(
63✔
690
                                changedColumnSet.oldColumn.name,
63✔
691
                            ),
63✔
692
                            1,
63✔
693
                        )
63✔
694
                        index.columnNames.push(changedColumnSet.newColumn.name)
63✔
695

63✔
696
                        // rename Index only if it has default constraint name
63✔
697
                        if (index.name === indexName) {
63✔
698
                            index.name =
45✔
699
                                this.connection.namingStrategy.indexName(
45✔
700
                                    changedTable,
45✔
701
                                    index.columnNames,
45✔
702
                                    index.where,
45✔
703
                                )
45✔
704
                        }
45✔
705
                    })
186✔
706
            }
186✔
707
            const originalColumn = changedTable.columns.find(
447✔
708
                (column) => column.name === changedColumnSet.oldColumn.name,
447✔
709
            )
447✔
710
            if (originalColumn)
447✔
711
                changedTable.columns[
447✔
712
                    changedTable.columns.indexOf(originalColumn)
447✔
713
                ] = changedColumnSet.newColumn
447✔
714
        })
×
715

×
716
        await this.recreateTable(changedTable, table)
×
717
    }
369✔
718

26✔
719
    /**
26✔
720
     * Drops column in the table.
26✔
721
     */
26✔
722
    async dropColumn(
26✔
723
        tableOrName: Table | string,
33✔
724
        columnOrName: TableColumn | string,
33✔
725
    ): Promise<void> {
33✔
726
        const table = InstanceChecker.isTable(tableOrName)
33✔
727
            ? tableOrName
33!
728
            : await this.getCachedTable(tableOrName)
33✔
729
        const column = InstanceChecker.isTableColumn(columnOrName)
27✔
730
            ? columnOrName
33!
731
            : table.findColumnByName(columnOrName)
33✔
732
        if (!column)
33✔
733
            throw new TypeORMError(
33✔
734
                `Column "${columnOrName}" was not found in table "${table.name}"`,
9✔
735
            )
9✔
736

24✔
737
        await this.dropColumns(table, [column])
24✔
738
    }
24✔
739

26✔
740
    /**
26✔
741
     * Drops the columns in the table.
26✔
742
     */
26✔
743
    async dropColumns(
26✔
744
        tableOrName: Table | string,
63✔
745
        columns: TableColumn[] | string[],
63✔
746
    ): Promise<void> {
63✔
747
        const table = InstanceChecker.isTable(tableOrName)
63✔
748
            ? tableOrName
63✔
749
            : await this.getCachedTable(tableOrName)
63!
750

×
751
        // clone original table and remove column and its constraints from cloned table
×
752
        const changedTable = table.clone()
×
753
        columns.forEach((column: TableColumn | string) => {
✔
754
            const columnInstance = InstanceChecker.isTableColumn(column)
117✔
755
                ? column
117✔
756
                : table.findColumnByName(column)
117✔
757
            if (!columnInstance)
117✔
758
                throw new Error(
117!
759
                    `Column "${column}" was not found in table "${table.name}"`,
×
760
                )
×
761

117✔
762
            changedTable.removeColumn(columnInstance)
117✔
763
            changedTable
117✔
764
                .findColumnUniques(columnInstance)
117✔
765
                .forEach((unique) =>
117✔
766
                    changedTable.removeUniqueConstraint(unique),
117✔
767
                )
117✔
768
            changedTable
117✔
769
                .findColumnIndices(columnInstance)
117✔
770
                .forEach((index) => changedTable.removeIndex(index))
117✔
771
            changedTable
117✔
772
                .findColumnForeignKeys(columnInstance)
117✔
773
                .forEach((fk) => changedTable.removeForeignKey(fk))
117✔
774
        })
×
775

×
776
        await this.recreateTable(changedTable, table)
×
777
    }
63✔
778

26✔
779
    /**
26✔
780
     * Creates a new primary key.
26✔
781
     */
26✔
782
    async createPrimaryKey(
26✔
783
        tableOrName: Table | string,
18✔
784
        columnNames: string[],
18✔
785
    ): Promise<void> {
18✔
786
        const table = InstanceChecker.isTable(tableOrName)
18✔
787
            ? tableOrName
18!
788
            : await this.getCachedTable(tableOrName)
18✔
789
        // clone original table and mark columns as primary
18✔
790
        const changedTable = table.clone()
18✔
791
        changedTable.columns.forEach((column) => {
18✔
792
            if (columnNames.find((columnName) => columnName === column.name))
45✔
793
                column.isPrimary = true
45✔
794
        })
18✔
795

18✔
796
        await this.recreateTable(changedTable, table)
18✔
797
        // mark columns as primary in original table
18✔
798
        table.columns.forEach((column) => {
18✔
799
            if (columnNames.find((columnName) => columnName === column.name))
45✔
800
                column.isPrimary = true
45✔
801
        })
18✔
802
    }
18✔
803

26✔
804
    /**
26✔
805
     * Updates composite primary keys.
26✔
806
     */
26✔
807
    async updatePrimaryKeys(
26✔
808
        tableOrName: Table | string,
9✔
809
        columns: TableColumn[],
9✔
810
    ): Promise<void> {
9✔
811
        await Promise.resolve()
9✔
812
    }
9✔
813

26✔
814
    /**
26✔
815
     * Drops a primary key.
26✔
816
     */
26✔
817
    async dropPrimaryKey(tableOrName: Table | string): Promise<void> {
26✔
818
        const table = InstanceChecker.isTable(tableOrName)
27✔
819
            ? tableOrName
27✔
820
            : await this.getCachedTable(tableOrName)
27!
821
        // clone original table and mark primary columns as non-primary
×
822
        const changedTable = table.clone()
×
823
        changedTable.primaryColumns.forEach((column) => {
✔
824
            column.isPrimary = false
27✔
825
        })
×
826

×
827
        await this.recreateTable(changedTable, table)
×
828
        // mark primary columns as non-primary in original table
27✔
829
        table.primaryColumns.forEach((column) => {
27✔
830
            column.isPrimary = false
×
831
        })
27✔
832
    }
27✔
833

26✔
834
    /**
26✔
835
     * Creates a new unique constraint.
26✔
836
     */
26✔
837
    async createUniqueConstraint(
26✔
838
        tableOrName: Table | string,
15✔
839
        uniqueConstraint: TableUnique,
15✔
840
    ): Promise<void> {
15✔
841
        await this.createUniqueConstraints(tableOrName, [uniqueConstraint])
15✔
842
    }
15✔
843

26✔
844
    /**
26✔
845
     * Creates a new unique constraints.
26✔
846
     */
26✔
847
    async createUniqueConstraints(
26✔
848
        tableOrName: Table | string,
30✔
849
        uniqueConstraints: TableUnique[],
30✔
850
    ): Promise<void> {
30✔
851
        const table = InstanceChecker.isTable(tableOrName)
30✔
852
            ? tableOrName
30✔
853
            : await this.getCachedTable(tableOrName)
30!
854

21!
855
        // clone original table and add unique constraints in to cloned table
21✔
856
        const changedTable = table.clone()
21✔
857
        uniqueConstraints.forEach((uniqueConstraint) =>
21✔
858
            changedTable.addUniqueConstraint(uniqueConstraint),
21✔
859
        )
21✔
860
        await this.recreateTable(changedTable, table)
21✔
861
    }
30✔
862

26✔
863
    /**
26✔
864
     * Drops a unique constraint.
26✔
865
     */
26✔
866
    async dropUniqueConstraint(
26✔
867
        tableOrName: Table | string,
6✔
868
        uniqueOrName: TableUnique | string,
6✔
869
    ): Promise<void> {
6✔
870
        const table = InstanceChecker.isTable(tableOrName)
6✔
871
            ? tableOrName
6✔
872
            : await this.getCachedTable(tableOrName)
6!
873
        const uniqueConstraint = InstanceChecker.isTableUnique(uniqueOrName)
×
874
            ? uniqueOrName
6✔
875
            : table.uniques.find((u) => u.name === uniqueOrName)
6!
876
        if (!uniqueConstraint)
6✔
877
            throw new TypeORMError(
6!
878
                `Supplied unique constraint was not found in table ${table.name}`,
×
879
            )
×
880

6✔
881
        await this.dropUniqueConstraints(table, [uniqueConstraint])
6✔
882
    }
6✔
883

26✔
884
    /**
26✔
885
     * Creates a unique constraints.
26✔
886
     */
26✔
887
    async dropUniqueConstraints(
26✔
888
        tableOrName: Table | string,
30✔
889
        uniqueConstraints: TableUnique[],
30✔
890
    ): Promise<void> {
30✔
891
        const table = InstanceChecker.isTable(tableOrName)
30✔
892
            ? tableOrName
30✔
893
            : await this.getCachedTable(tableOrName)
30!
894

6!
895
        // clone original table and remove unique constraints from cloned table
6✔
896
        const changedTable = table.clone()
6✔
897
        uniqueConstraints.forEach((uniqueConstraint) =>
6✔
898
            changedTable.removeUniqueConstraint(uniqueConstraint),
6✔
899
        )
6✔
900

6✔
901
        await this.recreateTable(changedTable, table)
6✔
902
    }
30✔
903

26✔
904
    /**
26✔
905
     * Creates new check constraint.
26✔
906
     */
26✔
907
    async createCheckConstraint(
26✔
908
        tableOrName: Table | string,
30✔
909
        checkConstraint: TableCheck,
30✔
910
    ): Promise<void> {
30✔
911
        await this.createCheckConstraints(tableOrName, [checkConstraint])
30✔
912
    }
30✔
913

26✔
914
    /**
26✔
915
     * Creates new check constraints.
26✔
916
     */
26✔
917
    async createCheckConstraints(
26✔
918
        tableOrName: Table | string,
48✔
919
        checkConstraints: TableCheck[],
48✔
920
    ): Promise<void> {
48✔
921
        const table = InstanceChecker.isTable(tableOrName)
48✔
922
            ? tableOrName
48✔
923
            : await this.getCachedTable(tableOrName)
48✔
924

30✔
925
        // clone original table and add check constraints in to cloned table
30✔
926
        const changedTable = table.clone()
30✔
927
        checkConstraints.forEach((checkConstraint) =>
30✔
928
            changedTable.addCheckConstraint(checkConstraint),
30✔
929
        )
30✔
930
        await this.recreateTable(changedTable, table)
30✔
931
    }
48✔
932

26✔
933
    /**
26✔
934
     * Drops check constraint.
26✔
935
     */
26✔
936
    async dropCheckConstraint(
26✔
937
        tableOrName: Table | string,
9✔
938
        checkOrName: TableCheck | string,
9✔
939
    ): Promise<void> {
9✔
940
        const table = InstanceChecker.isTable(tableOrName)
9✔
941
            ? tableOrName
9✔
942
            : await this.getCachedTable(tableOrName)
9!
943
        const checkConstraint = InstanceChecker.isTableCheck(checkOrName)
×
944
            ? checkOrName
9✔
945
            : table.checks.find((c) => c.name === checkOrName)
9!
946
        if (!checkConstraint)
9✔
947
            throw new TypeORMError(
9!
948
                `Supplied check constraint was not found in table ${table.name}`,
×
949
            )
×
950

9✔
951
        await this.dropCheckConstraints(table, [checkConstraint])
9✔
952
    }
9✔
953

26✔
954
    /**
26✔
955
     * Drops check constraints.
26✔
956
     */
26✔
957
    async dropCheckConstraints(
26✔
958
        tableOrName: Table | string,
33✔
959
        checkConstraints: TableCheck[],
33✔
960
    ): Promise<void> {
33✔
961
        const table = InstanceChecker.isTable(tableOrName)
33✔
962
            ? tableOrName
33✔
963
            : await this.getCachedTable(tableOrName)
33!
964

×
965
        // clone original table and remove check constraints from cloned table
×
966
        const changedTable = table.clone()
×
967
        checkConstraints.forEach((checkConstraint) =>
✔
968
            changedTable.removeCheckConstraint(checkConstraint),
×
969
        )
×
970

×
971
        await this.recreateTable(changedTable, table)
×
972
    }
33✔
973

26✔
974
    /**
26✔
975
     * Creates a new exclusion constraint.
26✔
976
     */
26✔
977
    async createExclusionConstraint(
26✔
978
        tableOrName: Table | string,
×
979
        exclusionConstraint: TableExclusion,
×
980
    ): Promise<void> {
×
981
        throw new TypeORMError(`Sqlite does not support exclusion constraints.`)
×
982
    }
×
983

26✔
984
    /**
26✔
985
     * Creates a new exclusion constraints.
26✔
986
     */
26✔
987
    async createExclusionConstraints(
26✔
988
        tableOrName: Table | string,
×
989
        exclusionConstraints: TableExclusion[],
×
990
    ): Promise<void> {
×
991
        throw new TypeORMError(`Sqlite does not support exclusion constraints.`)
×
992
    }
×
993

26✔
994
    /**
26✔
995
     * Drops exclusion constraint.
26✔
996
     */
26✔
997
    async dropExclusionConstraint(
26✔
998
        tableOrName: Table | string,
×
999
        exclusionOrName: TableExclusion | string,
×
1000
    ): Promise<void> {
×
1001
        throw new TypeORMError(`Sqlite does not support exclusion constraints.`)
×
1002
    }
×
1003

26✔
1004
    /**
26✔
1005
     * Drops exclusion constraints.
26✔
1006
     */
26✔
1007
    async dropExclusionConstraints(
26✔
1008
        tableOrName: Table | string,
×
1009
        exclusionConstraints: TableExclusion[],
×
1010
    ): Promise<void> {
×
1011
        throw new TypeORMError(`Sqlite does not support exclusion constraints.`)
×
1012
    }
×
1013

26✔
1014
    /**
26✔
1015
     * Creates a new foreign key.
26✔
1016
     */
26✔
1017
    async createForeignKey(
26✔
1018
        tableOrName: Table | string,
12✔
1019
        foreignKey: TableForeignKey,
12✔
1020
    ): Promise<void> {
12✔
1021
        await this.createForeignKeys(tableOrName, [foreignKey])
12✔
1022
    }
12✔
1023

26✔
1024
    /**
26✔
1025
     * Creates a new foreign keys.
26✔
1026
     */
26✔
1027
    async createForeignKeys(
26✔
1028
        tableOrName: Table | string,
14,871✔
1029
        foreignKeys: TableForeignKey[],
14,871✔
1030
    ): Promise<void> {
14,871✔
1031
        const table = InstanceChecker.isTable(tableOrName)
14,871✔
1032
            ? tableOrName
14,871✔
1033
            : await this.getCachedTable(tableOrName)
14,871✔
1034
        // clone original table and add foreign keys in to cloned table
21✔
1035
        const changedTable = table.clone()
21✔
1036
        foreignKeys.forEach((foreignKey) =>
21✔
1037
            changedTable.addForeignKey(foreignKey),
21✔
1038
        )
21✔
1039

21✔
1040
        await this.recreateTable(changedTable, table)
21✔
1041
    }
14,871✔
1042

26✔
1043
    /**
26✔
1044
     * Drops a foreign key from the table.
26✔
1045
     */
26✔
1046
    async dropForeignKey(
26✔
1047
        tableOrName: Table | string,
9✔
1048
        foreignKeyOrName: TableForeignKey | string,
9✔
1049
    ): Promise<void> {
9✔
1050
        const table = InstanceChecker.isTable(tableOrName)
9✔
1051
            ? tableOrName
9✔
1052
            : await this.getCachedTable(tableOrName)
9!
1053
        const foreignKey = InstanceChecker.isTableForeignKey(foreignKeyOrName)
×
1054
            ? foreignKeyOrName
9✔
1055
            : table.foreignKeys.find((fk) => fk.name === foreignKeyOrName)
9!
1056
        if (!foreignKey)
9✔
1057
            throw new TypeORMError(
9!
1058
                `Supplied foreign key was not found in table ${table.name}`,
×
1059
            )
×
1060

9✔
1061
        await this.dropForeignKeys(tableOrName, [foreignKey])
9✔
1062
    }
9✔
1063

26✔
1064
    /**
26✔
1065
     * Drops a foreign keys from the table.
26✔
1066
     */
26✔
1067
    async dropForeignKeys(
26✔
1068
        tableOrName: Table | string,
63✔
1069
        foreignKeys: TableForeignKey[],
63✔
1070
    ): Promise<void> {
63✔
1071
        const table = InstanceChecker.isTable(tableOrName)
63✔
1072
            ? tableOrName
63✔
1073
            : await this.getCachedTable(tableOrName)
63✔
1074

9✔
1075
        // clone original table and remove foreign keys from cloned table
9✔
1076
        const changedTable = table.clone()
9✔
1077
        foreignKeys.forEach((foreignKey) =>
9✔
1078
            changedTable.removeForeignKey(foreignKey),
9✔
1079
        )
9✔
1080

9✔
1081
        await this.recreateTable(changedTable, table)
9✔
1082
    }
63✔
1083

26✔
1084
    /**
26✔
1085
     * Creates a new index.
26✔
1086
     */
26✔
1087
    async createIndex(
26✔
1088
        tableOrName: Table | string,
126✔
1089
        index: TableIndex,
126✔
1090
    ): Promise<void> {
126✔
1091
        const table = InstanceChecker.isTable(tableOrName)
126✔
1092
            ? tableOrName
126✔
1093
            : await this.getCachedTable(tableOrName)
126✔
1094

63✔
1095
        // new index may be passed without name. In this case we generate index name manually.
63✔
1096
        if (!index.name) index.name = this.generateIndexName(table, index)
126✔
1097

126✔
1098
        const up = this.createIndexSql(table, index)
126✔
1099
        const down = this.dropIndexSql(index)
126✔
1100
        await this.executeQueries(up, down)
126✔
1101
        table.addIndex(index)
126✔
1102
    }
126✔
1103

26✔
1104
    /**
26✔
1105
     * Creates a new indices
26✔
1106
     */
26✔
1107
    async createIndices(
26✔
1108
        tableOrName: Table | string,
63✔
1109
        indices: TableIndex[],
63✔
1110
    ): Promise<void> {
63✔
1111
        const promises = indices.map((index) =>
63✔
1112
            this.createIndex(tableOrName, index),
63✔
1113
        )
63✔
1114
        await Promise.all(promises)
63✔
1115
    }
63✔
1116

26✔
1117
    /**
26✔
1118
     * Drops an index from the table.
26✔
1119
     */
26✔
1120
    async dropIndex(
26✔
1121
        tableOrName: Table | string,
138✔
1122
        indexOrName: TableIndex | string,
138✔
1123
    ): Promise<void> {
138✔
1124
        const table = InstanceChecker.isTable(tableOrName)
138✔
1125
            ? tableOrName
138✔
1126
            : await this.getCachedTable(tableOrName)
138✔
1127
        const index = InstanceChecker.isTableIndex(indexOrName)
45✔
1128
            ? indexOrName
138✔
1129
            : table.indices.find((i) => i.name === indexOrName)
138!
1130
        if (!index)
138✔
1131
            throw new TypeORMError(
138!
1132
                `Supplied index ${indexOrName} was not found in table ${table.name}`,
×
1133
            )
×
1134

138✔
1135
        // old index may be passed without name. In this case we generate index name manually.
138✔
1136
        if (!index.name) index.name = this.generateIndexName(table, index)
138✔
1137

138✔
1138
        const up = this.dropIndexSql(index)
138✔
1139
        const down = this.createIndexSql(table, index)
138✔
1140
        await this.executeQueries(up, down)
138✔
1141
        table.removeIndex(index)
138✔
1142
    }
138✔
1143

26✔
1144
    /**
26✔
1145
     * Drops an indices from the table.
26✔
1146
     */
26✔
1147
    async dropIndices(
26✔
1148
        tableOrName: Table | string,
15✔
1149
        indices: TableIndex[],
15✔
1150
    ): Promise<void> {
15✔
1151
        const promises = indices.map((index) =>
15✔
1152
            this.dropIndex(tableOrName, index),
15✔
1153
        )
15✔
1154
        await Promise.all(promises)
15✔
1155
    }
15✔
1156

26✔
1157
    /**
26✔
1158
     * Clears all table contents.
26✔
1159
     * Note: this operation uses SQL's TRUNCATE query which cannot be reverted in transactions.
26✔
1160
     */
26✔
1161
    async clearTable(tableName: string): Promise<void> {
26✔
1162
        await this.query(`DELETE FROM ${this.escapePath(tableName)}`)
18✔
1163
    }
18✔
1164

26✔
1165
    /**
26✔
1166
     * Removes all tables from the currently connected database.
26✔
1167
     */
26✔
1168
    async clearDatabase(database?: string): Promise<void> {
26✔
1169
        let dbPath: string | undefined = undefined
11,979✔
1170
        if (
11,979✔
1171
            database &&
11,979✔
1172
            this.driver.getAttachedDatabaseHandleByRelativePath(database)
8,235✔
1173
        ) {
11,979!
1174
            dbPath =
30✔
1175
                this.driver.getAttachedDatabaseHandleByRelativePath(database)
30✔
1176
        }
30✔
1177

11,979✔
1178
        await this.query(`PRAGMA foreign_keys = OFF`)
11,979✔
1179

11,979✔
1180
        const isAnotherTransactionActive = this.isTransactionActive
11,979✔
1181
        if (!isAnotherTransactionActive) await this.startTransaction()
11,979✔
1182
        try {
11,979✔
1183
            const selectViewDropsQuery = dbPath
11,979✔
1184
                ? `SELECT 'DROP VIEW "${dbPath}"."' || name || '";' as query FROM "${dbPath}"."sqlite_master" WHERE "type" = 'view'`
11,979!
1185
                : `SELECT 'DROP VIEW "' || name || '";' as query FROM "sqlite_master" WHERE "type" = 'view'`
11,979✔
1186
            const dropViewQueries: ObjectLiteral[] = await this.query(
11,979✔
1187
                selectViewDropsQuery,
11,979✔
1188
            )
11,979✔
1189
            await Promise.all(
11,979✔
1190
                dropViewQueries.map((q) => this.query(q["query"])),
11,979✔
1191
            )
11,979✔
1192

11,979✔
1193
            const selectTableDropsQuery = dbPath
11,979✔
1194
                ? `SELECT 'DROP TABLE "${dbPath}"."' || name || '";' as query FROM "${dbPath}"."sqlite_master" WHERE "type" = 'table' AND "name" != 'sqlite_sequence'`
11,979!
1195
                : `SELECT 'DROP TABLE "' || name || '";' as query FROM "sqlite_master" WHERE "type" = 'table' AND "name" != 'sqlite_sequence'`
11,979✔
1196
            const dropTableQueries: ObjectLiteral[] = await this.query(
11,979✔
1197
                selectTableDropsQuery,
11,979✔
1198
            )
11,979✔
1199
            await Promise.all(
11,979✔
1200
                dropTableQueries.map((q) => this.query(q["query"])),
11,979✔
1201
            )
11,979✔
1202

11,979✔
1203
            if (!isAnotherTransactionActive) await this.commitTransaction()
11,979✔
1204
        } catch (error) {
11,979!
1205
            try {
×
1206
                // we throw original error even if rollback thrown an error
×
1207
                if (!isAnotherTransactionActive)
×
1208
                    await this.rollbackTransaction()
×
1209
            } catch (rollbackError) {}
×
1210
            throw error
×
1211
        } finally {
11,979✔
1212
            await this.query(`PRAGMA foreign_keys = ON`)
11,979✔
1213
        }
11,979✔
1214
    }
11,979✔
1215

26✔
1216
    // -------------------------------------------------------------------------
26✔
1217
    // Protected Methods
26✔
1218
    // -------------------------------------------------------------------------
26✔
1219

26✔
1220
    protected async loadViews(viewNames?: string[]): Promise<View[]> {
26✔
1221
        const hasTable = await this.hasTable(this.getTypeormMetadataTableName())
12,435✔
1222
        if (!hasTable) {
12,435✔
1223
            return []
12,357✔
1224
        }
12,357✔
1225

78✔
1226
        if (!viewNames) {
12,435!
1227
            viewNames = []
×
1228
        }
×
1229

78✔
1230
        const viewNamesString = viewNames
78✔
1231
            .map((name) => "'" + name + "'")
78✔
1232
            .join(", ")
78✔
1233
        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" = '${
78✔
1234
            MetadataTableType.VIEW
78✔
1235
        }'`
78✔
1236
        if (viewNamesString.length > 0)
78✔
1237
            query += ` AND "t"."name" IN (${viewNamesString})`
4,455✔
1238
        const dbViews = await this.query(query)
78✔
1239
        return dbViews.map((dbView: any) => {
78✔
1240
            const view = new View()
24✔
1241
            view.name = dbView["name"]
24✔
1242
            view.expression = dbView["value"]
24✔
1243
            return view
24✔
1244
        })
78✔
1245
    }
78✔
1246

26✔
1247
    protected async loadTableRecords(
26✔
1248
        tablePath: string,
96✔
1249
        tableOrIndex: "table" | "index",
96✔
1250
    ) {
96✔
1251
        let database: string | undefined = undefined
96✔
1252
        const [schema, tableName] = this.splitTablePath(tablePath)
96✔
1253
        if (
96✔
1254
            schema &&
96✔
1255
            this.driver.getAttachedDatabasePathRelativeByHandle(schema)
96✔
1256
        ) {
96✔
1257
            database =
96✔
1258
                this.driver.getAttachedDatabasePathRelativeByHandle(schema)
96✔
1259
        }
96✔
1260
        return this.query(
96✔
1261
            `SELECT ${database ? `'${database}'` : null} as database, ${
96!
1262
                schema ? `'${schema}'` : null
96!
1263
            } as schema, * FROM ${
96✔
1264
                schema ? `"${schema}".` : ""
96!
1265
            }${this.escapePath(
96✔
1266
                `sqlite_master`,
96✔
1267
            )} WHERE "type" = '${tableOrIndex}' AND "${
96✔
1268
                tableOrIndex === "table" ? "name" : "tbl_name"
96✔
1269
            }" IN ('${tableName}')`,
96✔
1270
        )
96✔
1271
    }
96✔
1272

26✔
1273
    protected async loadPragmaRecords(tablePath: string, pragma: string) {
26✔
1274
        const [, tableName] = this.splitTablePath(tablePath)
7,479✔
1275
        return this.query(`PRAGMA ${pragma}("${tableName}")`)
7,479✔
1276
    }
7,479✔
1277

26✔
1278
    /**
26✔
1279
     * Loads all tables (with given names) from the database and creates a Table from them.
26✔
1280
     */
26✔
1281
    protected async loadTables(tableNames?: string[]): Promise<Table[]> {
26✔
1282
        // if no tables given then no need to proceed
14,580✔
1283
        if (tableNames && tableNames.length === 0) {
14,580✔
1284
            return []
117✔
1285
        }
117✔
1286

14,463✔
1287
        let dbTables: { database?: string; name: string; sql: string }[] = []
14,463✔
1288
        let dbIndicesDef: ObjectLiteral[]
14,463✔
1289

14,463✔
1290
        if (!tableNames) {
14,580!
1291
            const tablesSql = `SELECT * FROM "sqlite_master" WHERE "type" = 'table'`
6✔
1292
            dbTables.push(...(await this.query(tablesSql)))
6✔
1293

6✔
1294
            const tableNamesString = dbTables
6✔
1295
                .map(({ name }) => `'${name}'`)
6✔
1296
                .join(", ")
6✔
1297
            dbIndicesDef = await this.query(
6✔
1298
                `SELECT * FROM "sqlite_master" WHERE "type" = 'index' AND "tbl_name" IN (${tableNamesString})`,
6✔
1299
            )
6✔
1300
        } else {
14,580✔
1301
            const tableNamesWithoutDot = tableNames
14,457✔
1302
                .filter((tableName) => {
14,457✔
1303
                    return tableName.split(".").length === 1
44,139✔
1304
                })
14,457✔
1305
                .map((tableName) => `'${tableName}'`)
14,457✔
1306

14,457✔
1307
            const tableNamesWithDot = tableNames.filter((tableName) => {
14,457✔
1308
                return tableName.split(".").length > 1
44,139✔
1309
            })
14,457✔
1310

14,457✔
1311
            const queryPromises = (type: "table" | "index") => {
14,457✔
1312
                const promises = [
28,914✔
1313
                    ...tableNamesWithDot.map((tableName) =>
28,914✔
1314
                        this.loadTableRecords(tableName, type),
28,914✔
1315
                    ),
28,914✔
1316
                ]
28,914✔
1317

28,914✔
1318
                if (tableNamesWithoutDot.length) {
28,914✔
1319
                    promises.push(
28,884✔
1320
                        this.query(
28,884✔
1321
                            `SELECT * FROM "sqlite_master" WHERE "type" = '${type}' AND "${
28,884✔
1322
                                type === "table" ? "name" : "tbl_name"
28,884✔
1323
                            }" IN (${tableNamesWithoutDot})`,
28,884✔
1324
                        ),
28,884✔
1325
                    )
28,884✔
1326
                }
28,884✔
1327

28,914✔
1328
                return promises
28,914✔
1329
            }
28,914✔
1330
            dbTables = (await Promise.all(queryPromises("table")))
14,457✔
1331
                .reduce((acc, res) => [...acc, ...res], [])
14,457✔
1332
                .filter(Boolean)
14,457✔
1333
            dbIndicesDef = (await Promise.all(queryPromises("index")))
14,457✔
1334
                .reduce((acc, res) => [...acc, ...res], [])
14,457✔
1335
                .filter(Boolean)
14,457✔
1336
        }
14,457✔
1337

14,463✔
1338
        // if tables were not found in the db, no need to proceed
14,463✔
1339
        if (dbTables.length === 0) {
14,580✔
1340
            return []
12,012✔
1341
        }
12,012✔
1342

2,451✔
1343
        // create table schemas for loaded tables
2,451✔
1344
        return Promise.all(
2,451✔
1345
            dbTables.map(async (dbTable) => {
2,451✔
1346
                const tablePath =
3,753✔
1347
                    dbTable["database"] &&
3,753!
1348
                    this.driver.getAttachedDatabaseHandleByRelativePath(
9✔
1349
                        dbTable["database"],
9✔
1350
                    )
3,753✔
1351
                        ? `${this.driver.getAttachedDatabaseHandleByRelativePath(
3,753!
1352
                              dbTable["database"],
9✔
1353
                          )}.${dbTable["name"]}`
9✔
1354
                        : dbTable["name"]
3,753✔
1355

3,753✔
1356
                const sql = dbTable["sql"]
3,753✔
1357

3,753✔
1358
                const withoutRowid = sql.includes("WITHOUT ROWID")
3,753✔
1359
                const table = new Table({ name: tablePath, withoutRowid })
3,753✔
1360

3,753✔
1361
                // load columns and indices
3,753✔
1362
                const [dbColumns, dbIndices, dbForeignKeys]: ObjectLiteral[][] =
3,753✔
1363
                    await Promise.all([
3,753✔
1364
                        this.loadPragmaRecords(tablePath, `table_xinfo`),
3,753✔
1365
                        this.loadPragmaRecords(tablePath, `index_list`),
3,753✔
1366
                        this.loadPragmaRecords(tablePath, `foreign_key_list`),
3,753✔
1367
                    ])
3,753✔
1368

3,753✔
1369
                // find column name with auto increment
3,753✔
1370
                let autoIncrementColumnName: string | undefined = undefined
3,753✔
1371
                const tableSql: string = dbTable["sql"]
3,753✔
1372
                const autoIncrementIndex = tableSql
3,753✔
1373
                    .toUpperCase()
3,753✔
1374
                    .indexOf("AUTOINCREMENT")
3,753✔
1375
                if (autoIncrementIndex !== -1) {
3,753✔
1376
                    autoIncrementColumnName = tableSql.substr(
2,100✔
1377
                        0,
2,100✔
1378
                        autoIncrementIndex,
2,100✔
1379
                    )
2,100✔
1380
                    const comma = autoIncrementColumnName.lastIndexOf(",")
2,100✔
1381
                    const bracket = autoIncrementColumnName.lastIndexOf("(")
2,100✔
1382
                    if (comma !== -1) {
2,100!
1383
                        autoIncrementColumnName =
×
1384
                            autoIncrementColumnName.substr(comma)
×
1385
                        autoIncrementColumnName =
×
1386
                            autoIncrementColumnName.substr(
×
1387
                                0,
×
1388
                                autoIncrementColumnName.lastIndexOf('"'),
×
1389
                            )
×
1390
                        autoIncrementColumnName =
×
1391
                            autoIncrementColumnName.substr(
×
1392
                                autoIncrementColumnName.indexOf('"') + 1,
×
1393
                            )
×
1394
                    } else if (bracket !== -1) {
2,100✔
1395
                        autoIncrementColumnName =
2,100✔
1396
                            autoIncrementColumnName.substr(bracket)
2,100✔
1397
                        autoIncrementColumnName =
2,100✔
1398
                            autoIncrementColumnName.substr(
2,100✔
1399
                                0,
2,100✔
1400
                                autoIncrementColumnName.lastIndexOf('"'),
2,100✔
1401
                            )
2,100✔
1402
                        autoIncrementColumnName =
2,100✔
1403
                            autoIncrementColumnName.substr(
2,100✔
1404
                                autoIncrementColumnName.indexOf('"') + 1,
2,100✔
1405
                            )
2,100✔
1406
                    }
2,100✔
1407
                }
2,100✔
1408

3,753✔
1409
                // create columns from the loaded columns
3,753✔
1410
                table.columns = await Promise.all(
3,753✔
1411
                    dbColumns.map(async (dbColumn) => {
3,753✔
1412
                        const tableColumn = new TableColumn()
13,050✔
1413
                        tableColumn.name = dbColumn["name"]
13,050✔
1414
                        tableColumn.type = dbColumn["type"].toLowerCase()
13,050✔
1415
                        tableColumn.default =
13,050✔
1416
                            dbColumn["dflt_value"] !== null &&
13,050✔
1417
                            dbColumn["dflt_value"] !== undefined
846✔
1418
                                ? dbColumn["dflt_value"]
13,050✔
1419
                                : undefined
13,050✔
1420
                        tableColumn.isNullable = dbColumn["notnull"] === 0
13,050✔
1421
                        // primary keys are numbered starting with 1, columns that aren't primary keys are marked with 0
13,050✔
1422
                        tableColumn.isPrimary = dbColumn["pk"] > 0
13,050✔
1423
                        tableColumn.comment = "" // SQLite does not support column comments
13,050✔
1424
                        tableColumn.isGenerated =
13,050✔
1425
                            autoIncrementColumnName === dbColumn["name"]
13,050✔
1426
                        if (tableColumn.isGenerated) {
13,050✔
1427
                            tableColumn.generationStrategy = "increment"
2,100✔
1428
                        }
2,100✔
1429

13,050✔
1430
                        if (
13,050✔
1431
                            dbColumn["hidden"] === 2 ||
13,050✔
1432
                            dbColumn["hidden"] === 3
13,014✔
1433
                        ) {
13,050!
1434
                            tableColumn.generatedType =
105✔
1435
                                dbColumn["hidden"] === 2 ? "VIRTUAL" : "STORED"
105✔
1436

105✔
1437
                            const asExpressionQuery =
105✔
1438
                                this.selectTypeormMetadataSql({
105✔
1439
                                    table: table.name,
105✔
1440
                                    type: MetadataTableType.GENERATED_COLUMN,
105✔
1441
                                    name: tableColumn.name,
105✔
1442
                                })
105✔
1443

105✔
1444
                            const results = await this.query(
105✔
1445
                                asExpressionQuery.query,
105✔
1446
                                asExpressionQuery.parameters,
105✔
1447
                            )
105✔
1448
                            if (results[0] && results[0].value) {
105✔
1449
                                tableColumn.asExpression = results[0].value
105✔
1450
                            } else {
105!
1451
                                tableColumn.asExpression = ""
×
1452
                            }
×
1453
                        }
105✔
1454

13,050✔
1455
                        if (tableColumn.type === "varchar") {
13,050✔
1456
                            tableColumn.enum = OrmUtils.parseSqlCheckExpression(
6,027✔
1457
                                sql,
6,027✔
1458
                                tableColumn.name,
6,027✔
1459
                            )
6,027✔
1460
                        }
6,027✔
1461

13,050✔
1462
                        // parse datatype and attempt to retrieve length, precision and scale
13,050✔
1463
                        const pos = tableColumn.type.indexOf("(")
13,050✔
1464
                        if (pos !== -1) {
13,050✔
1465
                            const fullType = tableColumn.type
912✔
1466
                            const dataType = fullType.substr(0, pos)
912✔
1467
                            if (
912✔
1468
                                this.driver.withLengthColumnTypes.find(
912✔
1469
                                    (col) => col === dataType,
912✔
1470
                                )
912✔
1471
                            ) {
912✔
1472
                                const len = parseInt(
864✔
1473
                                    fullType.substring(
864✔
1474
                                        pos + 1,
864✔
1475
                                        fullType.length - 1,
864✔
1476
                                    ),
864✔
1477
                                )
864✔
1478
                                if (len) {
864✔
1479
                                    tableColumn.length = len.toString()
864✔
1480
                                    tableColumn.type = dataType // remove the length part from the datatype
864✔
1481
                                }
864✔
1482
                            }
864✔
1483
                            if (
912✔
1484
                                this.driver.withPrecisionColumnTypes.find(
912✔
1485
                                    (col) => col === dataType,
912✔
1486
                                )
912✔
1487
                            ) {
912✔
1488
                                const re = new RegExp(
48✔
1489
                                    `^${dataType}\\((\\d+),?\\s?(\\d+)?\\)`,
48✔
1490
                                )
48✔
1491
                                const matches = fullType.match(re)
48✔
1492
                                if (matches && matches[1]) {
48✔
1493
                                    tableColumn.precision = +matches[1]
48✔
1494
                                }
48✔
1495
                                if (
48✔
1496
                                    this.driver.withScaleColumnTypes.find(
48✔
1497
                                        (col) => col === dataType,
48✔
1498
                                    )
48✔
1499
                                ) {
48✔
1500
                                    if (matches && matches[2]) {
30✔
1501
                                        tableColumn.scale = +matches[2]
24✔
1502
                                    }
24✔
1503
                                }
30✔
1504
                                tableColumn.type = dataType // remove the precision/scale part from the datatype
48✔
1505
                            }
48✔
1506
                        }
912✔
1507

13,050✔
1508
                        return tableColumn
13,050✔
1509
                    }),
3,753✔
1510
                )
3,753✔
1511

3,753✔
1512
                // find foreign key constraints from CREATE TABLE sql
3,753✔
1513
                let fkResult
3,753✔
1514
                const fkMappings: {
3,753✔
1515
                    name: string
3,753✔
1516
                    columns: string[]
3,753✔
1517
                    referencedTableName: string
3,753✔
1518
                }[] = []
3,753✔
1519
                const fkRegex =
3,753✔
1520
                    /CONSTRAINT "([^"]*)" FOREIGN KEY ?\((.*?)\) REFERENCES "([^"]*)"/g
3,753✔
1521
                while ((fkResult = fkRegex.exec(sql)) !== null) {
3,753✔
1522
                    fkMappings.push({
1,542✔
1523
                        name: fkResult[1],
1,542✔
1524
                        columns: fkResult[2]
1,542✔
1525
                            .substr(1, fkResult[2].length - 2)
1,542✔
1526
                            .split(`", "`),
1,542✔
1527
                        referencedTableName: fkResult[3],
1,542✔
1528
                    })
1,542✔
1529
                }
1,542✔
1530

3,753✔
1531
                // build foreign keys
3,753✔
1532
                const tableForeignKeyConstraints = OrmUtils.uniq(
3,753✔
1533
                    dbForeignKeys,
3,753✔
1534
                    (dbForeignKey) => dbForeignKey["id"],
3,753✔
1535
                )
3,753✔
1536

3,753✔
1537
                table.foreignKeys = tableForeignKeyConstraints.map(
3,753✔
1538
                    (foreignKey) => {
3,753✔
1539
                        const ownForeignKeys = dbForeignKeys.filter(
1,548✔
1540
                            (dbForeignKey) =>
1,548✔
1541
                                dbForeignKey["id"] === foreignKey["id"] &&
3,306✔
1542
                                dbForeignKey["table"] === foreignKey["table"],
1,548✔
1543
                        )
1,548✔
1544
                        const columnNames = ownForeignKeys.map(
1,548✔
1545
                            (dbForeignKey) => dbForeignKey["from"],
1,548✔
1546
                        )
1,548✔
1547
                        const referencedColumnNames = ownForeignKeys.map(
1,548✔
1548
                            (dbForeignKey) => dbForeignKey["to"],
1,548✔
1549
                        )
1,548✔
1550

1,548✔
1551
                        // find related foreign key mapping
1,548✔
1552
                        const fkMapping = fkMappings.find(
1,548✔
1553
                            (it) =>
1,548✔
1554
                                it.referencedTableName ===
2,340✔
1555
                                    foreignKey["table"] &&
2,340✔
1556
                                it.columns.every(
1,659✔
1557
                                    (column) =>
1,659✔
1558
                                        columnNames.indexOf(column) !== -1,
1,659✔
1559
                                ),
1,548✔
1560
                        )
1,548✔
1561

1,548✔
1562
                        return new TableForeignKey({
1,548✔
1563
                            name: fkMapping?.name,
1,548✔
1564
                            columnNames: columnNames,
1,548✔
1565
                            referencedTableName: foreignKey["table"],
1,548✔
1566
                            referencedColumnNames: referencedColumnNames,
1,548✔
1567
                            onDelete: foreignKey["on_delete"],
1,548✔
1568
                            onUpdate: foreignKey["on_update"],
1,548✔
1569
                        })
1,548✔
1570
                    },
3,753✔
1571
                )
3,753✔
1572

3,753✔
1573
                // find unique constraints from CREATE TABLE sql
3,753✔
1574
                let uniqueRegexResult
3,753✔
1575
                const uniqueMappings: { name: string; columns: string[] }[] = []
3,753✔
1576
                const uniqueRegex = /CONSTRAINT "([^"]*)" UNIQUE ?\((.*?)\)/g
3,753✔
1577
                while ((uniqueRegexResult = uniqueRegex.exec(sql)) !== null) {
3,753✔
1578
                    uniqueMappings.push({
1,893✔
1579
                        name: uniqueRegexResult[1],
1,893✔
1580
                        columns: uniqueRegexResult[2]
1,893✔
1581
                            .substr(1, uniqueRegexResult[2].length - 2)
1,893✔
1582
                            .split(`", "`),
1,893✔
1583
                    })
1,893✔
1584
                }
1,893✔
1585

3,753✔
1586
                // build unique constraints
3,753✔
1587
                const tableUniquePromises = dbIndices
3,753✔
1588
                    .filter((dbIndex) => dbIndex["origin"] === "u")
3,753✔
1589
                    .map((dbIndex) => dbIndex["name"])
3,753✔
1590
                    .filter(
3,753✔
1591
                        (value, index, self) => self.indexOf(value) === index,
3,753✔
1592
                    )
3,753✔
1593
                    .map(async (dbIndexName) => {
3,753✔
1594
                        const dbIndex = dbIndices.find(
1,893✔
1595
                            (dbIndex) => dbIndex["name"] === dbIndexName,
1,893✔
1596
                        )
1,893✔
1597
                        const indexInfos: ObjectLiteral[] = await this.query(
1,893✔
1598
                            `PRAGMA index_info("${dbIndex!["name"]}")`,
1,893✔
1599
                        )
1,893✔
1600
                        const indexColumns = indexInfos
1,893✔
1601
                            .sort(
1,893✔
1602
                                (indexInfo1, indexInfo2) =>
1,893✔
1603
                                    parseInt(indexInfo1["seqno"]) -
750✔
1604
                                    parseInt(indexInfo2["seqno"]),
1,893✔
1605
                            )
1,893✔
1606
                            .map((indexInfo) => indexInfo["name"])
1,893✔
1607
                        if (indexColumns.length === 1) {
1,893✔
1608
                            const column = table.columns.find((column) => {
1,146✔
1609
                                return !!indexColumns.find(
2,628✔
1610
                                    (indexColumn) =>
2,628✔
1611
                                        indexColumn === column.name,
2,628✔
1612
                                )
2,628✔
1613
                            })
1,146✔
1614
                            if (column) column.isUnique = true
1,146✔
1615
                        }
1,146✔
1616

1,893✔
1617
                        // find existent mapping by a column names
1,893✔
1618
                        const foundMapping = uniqueMappings.find((mapping) => {
1,893✔
1619
                            return mapping!.columns.every(
2,772✔
1620
                                (column) => indexColumns.indexOf(column) !== -1,
2,772✔
1621
                            )
2,772✔
1622
                        })
1,893✔
1623

1,893✔
1624
                        return new TableUnique({
1,893✔
1625
                            name: foundMapping
1,893✔
1626
                                ? foundMapping.name
1,893✔
1627
                                : this.connection.namingStrategy.uniqueConstraintName(
1,893!
1628
                                      table,
×
1629
                                      indexColumns,
×
1630
                                  ),
1,893✔
1631
                            columnNames: indexColumns,
1,893✔
1632
                        })
1,893✔
1633
                    })
3,753✔
1634
                table.uniques = (await Promise.all(
3,753✔
1635
                    tableUniquePromises,
3,753✔
1636
                )) as TableUnique[]
3,753✔
1637

3,753✔
1638
                // build checks
3,753✔
1639
                let result
3,753✔
1640
                const regexp =
3,753✔
1641
                    /CONSTRAINT "([^"]*)" CHECK ?(\(.*?\))([,]|[)]$)/g
3,753✔
1642
                while ((result = regexp.exec(sql)) !== null) {
3,753✔
1643
                    table.checks.push(
732✔
1644
                        new TableCheck({
732✔
1645
                            name: result[1],
732✔
1646
                            expression: result[2],
732✔
1647
                        }),
732✔
1648
                    )
732✔
1649
                }
732✔
1650

3,753✔
1651
                // build indices
3,753✔
1652
                const indicesPromises = dbIndices
3,753✔
1653
                    .filter((dbIndex) => dbIndex["origin"] === "c")
3,753✔
1654
                    .map((dbIndex) => dbIndex["name"])
3,753✔
1655
                    .filter(
3,753✔
1656
                        (value, index, self) => self.indexOf(value) === index,
3,753✔
1657
                    ) // unqiue
3,753✔
1658
                    .map(async (dbIndexName) => {
3,753✔
1659
                        const indexDef = dbIndicesDef.find(
1,065✔
1660
                            (dbIndexDef) => dbIndexDef["name"] === dbIndexName,
1,065✔
1661
                        )
1,065✔
1662
                        const condition = /WHERE (.*)/.exec(indexDef!["sql"])
1,065✔
1663
                        const dbIndex = dbIndices.find(
1,065✔
1664
                            (dbIndex) => dbIndex["name"] === dbIndexName,
1,065✔
1665
                        )
1,065✔
1666
                        const indexInfos: ObjectLiteral[] = await this.query(
1,065✔
1667
                            `PRAGMA index_info("${dbIndex!["name"]}")`,
1,065✔
1668
                        )
1,065✔
1669
                        const indexColumns = indexInfos
1,065✔
1670
                            .sort(
1,065✔
1671
                                (indexInfo1, indexInfo2) =>
1,065✔
1672
                                    parseInt(indexInfo1["seqno"]) -
126✔
1673
                                    parseInt(indexInfo2["seqno"]),
1,065✔
1674
                            )
1,065✔
1675
                            .map((indexInfo) => indexInfo["name"])
1,065✔
1676
                        const dbIndexPath = `${
1,065✔
1677
                            dbTable["database"] ? `${dbTable["database"]}.` : ""
1,065!
1678
                        }${dbIndex!["name"]}`
1,065✔
1679

1,065✔
1680
                        const isUnique =
1,065✔
1681
                            dbIndex!["unique"] === "1" ||
1,065✔
1682
                            dbIndex!["unique"] === 1
1,065✔
1683
                        return new TableIndex(<TableIndexOptions>{
1,065✔
1684
                            table: table,
1,065✔
1685
                            name: dbIndexPath,
1,065✔
1686
                            columnNames: indexColumns,
1,065✔
1687
                            isUnique: isUnique,
1,065✔
1688
                            where: condition ? condition[1] : undefined,
1,065!
1689
                        })
1,065✔
1690
                    })
3,753✔
1691
                const indices = await Promise.all(indicesPromises)
3,753✔
1692
                table.indices = indices.filter(
3,753✔
1693
                    (index) => !!index,
3,753✔
1694
                ) as TableIndex[]
3,753✔
1695

3,753✔
1696
                return table
3,753✔
1697
            }),
2,451✔
1698
        )
2,451✔
1699
    }
2,451✔
1700

26✔
1701
    /**
26✔
1702
     * Builds create table sql.
26✔
1703
     */
26✔
1704
    protected createTableSql(
26✔
1705
        table: Table,
72,240✔
1706
        createForeignKeys?: boolean,
72,240✔
1707
        temporaryTable?: boolean,
72,240✔
1708
    ): Query {
72,240✔
1709
        const primaryColumns = table.columns.filter(
72,240✔
1710
            (column) => column.isPrimary,
72,240✔
1711
        )
72,240✔
1712
        const hasAutoIncrement = primaryColumns.find(
72,240✔
1713
            (column) =>
72,240✔
1714
                column.isGenerated && column.generationStrategy === "increment",
72,240✔
1715
        )
72,240✔
1716
        const skipPrimary = primaryColumns.length > 1
72,240✔
1717
        if (skipPrimary && hasAutoIncrement)
72,240✔
1718
            throw new TypeORMError(
72,240!
1719
                `Sqlite does not support AUTOINCREMENT on composite primary key`,
×
1720
            )
×
1721

72,240✔
1722
        const columnDefinitions = table.columns
72,240✔
1723
            .map((column) => this.buildCreateColumnSql(column, skipPrimary))
72,240✔
1724
            .join(", ")
72,240✔
1725
        const [database] = this.splitTablePath(table.name)
72,240✔
1726
        let sql = `CREATE TABLE ${this.escapePath(
72,240✔
1727
            table.name,
72,240✔
1728
        )} (${columnDefinitions}`
72,240✔
1729

72,240✔
1730
        const [databaseNew, tableName] = this.splitTablePath(table.name)
72,240✔
1731
        const newTableName = temporaryTable
72,240✔
1732
            ? `${databaseNew ? `${databaseNew}.` : ""}${tableName.replace(
72,240!
1733
                  /^temporary_/,
15,747✔
1734
                  "",
15,747✔
1735
              )}`
15,747✔
1736
            : table.name
72,240✔
1737

72,240✔
1738
        // need for `addColumn()` method, because it recreates table.
72,240✔
1739
        table.columns
72,240✔
1740
            .filter((column) => column.isUnique)
72,240✔
1741
            .forEach((column) => {
72,240✔
1742
                const isUniqueExist = table.uniques.some(
13,350✔
1743
                    (unique) =>
13,350✔
1744
                        unique.columnNames.length === 1 &&
18,786✔
1745
                        unique.columnNames[0] === column.name,
13,350✔
1746
                )
13,350✔
1747
                if (!isUniqueExist)
13,350✔
1748
                    table.uniques.push(
13,350✔
1749
                        new TableUnique({
72✔
1750
                            name: this.connection.namingStrategy.uniqueConstraintName(
72✔
1751
                                table,
72✔
1752
                                [column.name],
72✔
1753
                            ),
72✔
1754
                            columnNames: [column.name],
72✔
1755
                        }),
72✔
1756
                    )
72✔
1757
            })
72,240✔
1758

72,240✔
1759
        if (table.uniques.length > 0) {
72,240✔
1760
            const uniquesSql = table.uniques
11,895✔
1761
                .map((unique) => {
11,895✔
1762
                    const uniqueName = unique.name
17,460✔
1763
                        ? unique.name
17,460✔
1764
                        : this.connection.namingStrategy.uniqueConstraintName(
17,460✔
1765
                              newTableName,
33✔
1766
                              unique.columnNames,
33✔
1767
                          )
17,460✔
1768
                    const columnNames = unique.columnNames
17,460✔
1769
                        .map((columnName) => `"${columnName}"`)
17,460✔
1770
                        .join(", ")
17,460✔
1771
                    return `CONSTRAINT "${uniqueName}" UNIQUE (${columnNames})`
17,460✔
1772
                })
11,895✔
1773
                .join(", ")
11,895✔
1774

11,895✔
1775
            sql += `, ${uniquesSql}`
11,895✔
1776
        }
11,895✔
1777

72,240✔
1778
        if (table.checks.length > 0) {
72,240✔
1779
            const checksSql = table.checks
1,266✔
1780
                .map((check) => {
1,266✔
1781
                    const checkName = check.name
1,302✔
1782
                        ? check.name
1,302✔
1783
                        : this.connection.namingStrategy.checkConstraintName(
1,302✔
1784
                              newTableName,
93✔
1785
                              check.expression!,
93✔
1786
                          )
1,302✔
1787
                    return `CONSTRAINT "${checkName}" CHECK (${check.expression})`
1,302✔
1788
                })
1,266✔
1789
                .join(", ")
1,266✔
1790

1,266✔
1791
            sql += `, ${checksSql}`
1,266✔
1792
        }
1,266✔
1793

72,240✔
1794
        if (table.foreignKeys.length > 0 && createForeignKeys) {
72,240✔
1795
            const foreignKeysSql = table.foreignKeys
15,231✔
1796
                .filter((fk) => {
15,231✔
1797
                    const [referencedDatabase] = this.splitTablePath(
26,430✔
1798
                        fk.referencedTableName,
26,430✔
1799
                    )
26,430✔
1800
                    if (referencedDatabase !== database) {
26,430!
1801
                        return false
×
1802
                    }
×
1803
                    return true
26,430✔
1804
                })
15,231✔
1805
                .map((fk) => {
15,231✔
1806
                    const [, referencedTable] = this.splitTablePath(
26,430✔
1807
                        fk.referencedTableName,
26,430✔
1808
                    )
26,430✔
1809
                    const columnNames = fk.columnNames
26,430✔
1810
                        .map((columnName) => `"${columnName}"`)
26,430✔
1811
                        .join(", ")
26,430✔
1812
                    if (!fk.name)
26,430✔
1813
                        fk.name = this.connection.namingStrategy.foreignKeyName(
26,430✔
1814
                            newTableName,
48✔
1815
                            fk.columnNames,
48✔
1816
                            this.getTablePath(fk),
48✔
1817
                            fk.referencedColumnNames,
48✔
1818
                        )
48✔
1819
                    const referencedColumnNames = fk.referencedColumnNames
26,430✔
1820
                        .map((columnName) => `"${columnName}"`)
26,430✔
1821
                        .join(", ")
26,430✔
1822

26,430✔
1823
                    let constraint = `CONSTRAINT "${fk.name}" FOREIGN KEY (${columnNames}) REFERENCES "${referencedTable}" (${referencedColumnNames})`
26,430✔
1824
                    if (fk.onDelete) constraint += ` ON DELETE ${fk.onDelete}`
26,430✔
1825
                    if (fk.onUpdate) constraint += ` ON UPDATE ${fk.onUpdate}`
26,430✔
1826
                    if (fk.deferrable)
26,430✔
1827
                        constraint += ` DEFERRABLE ${fk.deferrable}`
26,430!
1828

26,430✔
1829
                    return constraint
26,430✔
1830
                })
15,231✔
1831
                .join(", ")
15,231✔
1832

15,231✔
1833
            sql += `, ${foreignKeysSql}`
15,231✔
1834
        }
15,231✔
1835

72,240✔
1836
        if (primaryColumns.length > 1) {
72,240✔
1837
            const columnNames = primaryColumns
21,570✔
1838
                .map((column) => `"${column.name}"`)
21,570✔
1839
                .join(", ")
21,570✔
1840
            sql += `, PRIMARY KEY (${columnNames})`
21,570✔
1841
        }
21,570✔
1842

72,240✔
1843
        sql += `)`
72,240✔
1844

72,240✔
1845
        if (table.withoutRowid) {
72,240✔
1846
            sql += " WITHOUT ROWID"
330✔
1847
        }
330✔
1848

72,240✔
1849
        return new Query(sql)
72,240✔
1850
    }
72,240✔
1851

26✔
1852
    /**
26✔
1853
     * Builds drop table sql.
26✔
1854
     */
26✔
1855
    protected dropTableSql(
26✔
1856
        tableOrName: Table | string,
72,240✔
1857
        ifExist?: boolean,
72,240✔
1858
    ): Query {
72,240✔
1859
        const tableName = InstanceChecker.isTable(tableOrName)
72,240✔
1860
            ? tableOrName.name
72,240✔
1861
            : tableOrName
72,240!
1862
        const query = ifExist
72,240✔
1863
            ? `DROP TABLE IF EXISTS ${this.escapePath(tableName)}`
72,240!
1864
            : `DROP TABLE ${this.escapePath(tableName)}`
72,240✔
1865
        return new Query(query)
72,240✔
1866
    }
72,240✔
1867

26✔
1868
    protected createViewSql(view: View): Query {
26✔
1869
        if (typeof view.expression === "string") {
72!
1870
            return new Query(`CREATE VIEW "${view.name}" AS ${view.expression}`)
18✔
1871
        } else {
72✔
1872
            return new Query(
54✔
1873
                `CREATE VIEW "${view.name}" AS ${view
54✔
1874
                    .expression(this.connection)
54✔
1875
                    .getQuery()}`,
54✔
1876
            )
54✔
1877
        }
54✔
1878
    }
72✔
1879

26✔
1880
    protected insertViewDefinitionSql(view: View): Query {
26✔
1881
        const expression =
66✔
1882
            typeof view.expression === "string"
66✔
1883
                ? view.expression.trim()
66!
1884
                : view.expression(this.connection).getQuery()
66✔
1885
        return this.insertTypeormMetadataSql({
66✔
1886
            type: MetadataTableType.VIEW,
66✔
1887
            name: view.name,
66✔
1888
            value: expression,
66✔
1889
        })
66✔
1890
    }
66✔
1891

26✔
1892
    /**
26✔
1893
     * Builds drop view sql.
26✔
1894
     */
26✔
1895
    protected dropViewSql(viewOrPath: View | string): Query {
26✔
1896
        const viewName = InstanceChecker.isView(viewOrPath)
72✔
1897
            ? viewOrPath.name
72✔
1898
            : viewOrPath
72!
1899
        return new Query(`DROP VIEW "${viewName}"`)
72✔
1900
    }
72✔
1901

26✔
1902
    /**
26✔
1903
     * Builds remove view sql.
26✔
1904
     */
26✔
1905
    protected deleteViewDefinitionSql(viewOrPath: View | string): Query {
26✔
1906
        const viewName = InstanceChecker.isView(viewOrPath)
66✔
1907
            ? viewOrPath.name
66✔
1908
            : viewOrPath
66!
1909
        return this.deleteTypeormMetadataSql({
66✔
1910
            type: MetadataTableType.VIEW,
66✔
1911
            name: viewName,
66✔
1912
        })
66✔
1913
    }
66✔
1914

26✔
1915
    /**
26✔
1916
     * Builds create index sql.
26✔
1917
     */
26✔
1918
    protected createIndexSql(table: Table, index: TableIndex): Query {
26✔
1919
        const columns = index.columnNames
40,713✔
1920
            .map((columnName) => `"${columnName}"`)
40,713✔
1921
            .join(", ")
40,713✔
1922
        const [database, tableName] = this.splitTablePath(table.name)
40,713✔
1923
        return new Query(
40,713✔
1924
            `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX ${
40,713✔
1925
                database ? `"${database}".` : ""
40,713!
1926
            }${this.escapePath(index.name!)} ON "${tableName}" (${columns}) ${
40,713✔
1927
                index.where ? "WHERE " + index.where : ""
40,713!
1928
            }`,
40,713✔
1929
        )
40,713✔
1930
    }
40,713✔
1931

26✔
1932
    /**
26✔
1933
     * Builds drop index sql.
26✔
1934
     */
26✔
1935
    protected dropIndexSql(indexOrName: TableIndex | string): Query {
26✔
1936
        const indexName = InstanceChecker.isTableIndex(indexOrName)
40,713✔
1937
            ? indexOrName.name
40,713✔
1938
            : indexOrName
40,713!
1939
        return new Query(`DROP INDEX ${this.escapePath(indexName!)}`)
40,713✔
1940
    }
40,713✔
1941

26✔
1942
    /**
26✔
1943
     * Builds a query for create column.
26✔
1944
     */
26✔
1945
    protected buildCreateColumnSql(
26✔
1946
        column: TableColumn,
237,105✔
1947
        skipPrimary?: boolean,
237,105✔
1948
    ): string {
237,105✔
1949
        let c = '"' + column.name + '"'
237,105✔
1950
        if (InstanceChecker.isColumnMetadata(column)) {
237,105!
1951
            c += " " + this.driver.normalizeType(column)
×
1952
        } else {
237,105✔
1953
            c += " " + this.connection.driver.createFullType(column)
237,105✔
1954
        }
237,105✔
1955

237,105✔
1956
        if (column.enum)
237,105✔
1957
            c +=
237,105!
1958
                ' CHECK( "' +
144✔
1959
                column.name +
144✔
1960
                '" IN (' +
144✔
1961
                column.enum.map((val) => "'" + val + "'").join(",") +
144✔
1962
                ") )"
144✔
1963
        if (column.isPrimary && !skipPrimary) c += " PRIMARY KEY"
237,105✔
1964
        if (
237,105✔
1965
            column.isGenerated === true &&
237,105✔
1966
            column.generationStrategy === "increment"
37,776✔
1967
        )
237,105✔
1968
            // don't use skipPrimary here since updates can update already exist primary without auto inc.
237,105✔
1969
            c += " AUTOINCREMENT"
237,105✔
1970
        if (column.collation) c += " COLLATE " + column.collation
237,105!
1971
        if (column.isNullable !== true) c += " NOT NULL"
237,105✔
1972

237,105✔
1973
        if (column.asExpression) {
237,105!
1974
            c += ` AS (${column.asExpression}) ${
159✔
1975
                column.generatedType ? column.generatedType : "VIRTUAL"
159!
1976
            }`
159✔
1977
        } else {
237,105✔
1978
            if (column.default !== undefined && column.default !== null)
236,946✔
1979
                c += " DEFAULT (" + column.default + ")"
236,946✔
1980
        }
236,946✔
1981

237,105✔
1982
        return c
237,105✔
1983
    }
237,105✔
1984

26✔
1985
    protected async recreateTable(
26✔
1986
        newTable: Table,
15,747✔
1987
        oldTable: Table,
15,747✔
1988
        migrateData = true,
15,747✔
1989
    ): Promise<void> {
15,747✔
1990
        const upQueries: Query[] = []
15,747✔
1991
        const downQueries: Query[] = []
15,747✔
1992

15,747✔
1993
        // drop old table indices
15,747✔
1994
        oldTable.indices.forEach((index) => {
15,747✔
1995
            upQueries.push(this.dropIndexSql(index))
13,083✔
1996
            downQueries.push(this.createIndexSql(oldTable, index))
13,083✔
1997
        })
15,747✔
1998

15,747✔
1999
        // change table name into 'temporary_table'
15,747✔
2000
        let [databaseNew, tableNameNew] = this.splitTablePath(newTable.name)
15,747✔
2001
        const [, tableNameOld] = this.splitTablePath(oldTable.name)
15,747✔
2002
        newTable.name = tableNameNew = `${
15,747✔
2003
            databaseNew ? `${databaseNew}.` : ""
15,747!
2004
        }temporary_${tableNameNew}`
15,747✔
2005

15,747✔
2006
        // create new table
15,747✔
2007
        upQueries.push(this.createTableSql(newTable, true, true))
15,747✔
2008
        downQueries.push(this.dropTableSql(newTable))
15,747✔
2009

15,747✔
2010
        // migrate all data from the old table into new table
15,747✔
2011
        if (migrateData) {
15,747✔
2012
            let newColumnNames = newTable.columns
15,747✔
2013
                .filter((column) => !column.generatedType)
15,747✔
2014
                .map((column) => `"${column.name}"`)
15,747✔
2015

15,747✔
2016
            let oldColumnNames = oldTable.columns
15,747✔
2017
                .filter((column) => !column.generatedType)
15,747✔
2018
                .map((column) => `"${column.name}"`)
15,747✔
2019

15,747✔
2020
            if (oldColumnNames.length < newColumnNames.length) {
15,747✔
2021
                newColumnNames = newTable.columns
75✔
2022
                    .filter((column) => {
75✔
2023
                        const oldColumn = oldTable.columns.find(
378✔
2024
                            (c) => c.name === column.name,
378✔
2025
                        )
378✔
2026
                        if (oldColumn && oldColumn.generatedType) return false
378!
2027
                        return !column.generatedType && oldColumn
369✔
2028
                    })
75✔
2029
                    .map((column) => `"${column.name}"`)
75✔
2030
            } else if (oldColumnNames.length > newColumnNames.length) {
15,747✔
2031
                oldColumnNames = oldTable.columns
57✔
2032
                    .filter((column) => {
57✔
2033
                        return (
231✔
2034
                            !column.generatedType &&
231✔
2035
                            newTable.columns.find((c) => c.name === column.name)
231✔
2036
                        )
231✔
2037
                    })
57✔
2038
                    .map((column) => `"${column.name}"`)
57✔
2039
            }
57✔
2040

15,747✔
2041
            upQueries.push(
15,747✔
2042
                new Query(
15,747✔
2043
                    `INSERT INTO ${this.escapePath(
15,747✔
2044
                        newTable.name,
15,747✔
2045
                    )}(${newColumnNames.join(
15,747✔
2046
                        ", ",
15,747✔
2047
                    )}) SELECT ${oldColumnNames.join(
15,747✔
2048
                        ", ",
15,747✔
2049
                    )} FROM ${this.escapePath(oldTable.name)}`,
15,747✔
2050
                ),
15,747✔
2051
            )
15,747✔
2052
            downQueries.push(
15,747✔
2053
                new Query(
15,747✔
2054
                    `INSERT INTO ${this.escapePath(
15,747✔
2055
                        oldTable.name,
15,747✔
2056
                    )}(${oldColumnNames.join(
15,747✔
2057
                        ", ",
15,747✔
2058
                    )}) SELECT ${newColumnNames.join(
15,747✔
2059
                        ", ",
15,747✔
2060
                    )} FROM ${this.escapePath(newTable.name)}`,
15,747✔
2061
                ),
15,747✔
2062
            )
15,747✔
2063
        }
15,747✔
2064

15,747✔
2065
        // drop old table
15,747✔
2066
        upQueries.push(this.dropTableSql(oldTable))
15,747✔
2067
        downQueries.push(this.createTableSql(oldTable, true))
15,747✔
2068

15,747✔
2069
        // rename old table
15,747✔
2070
        upQueries.push(
15,747✔
2071
            new Query(
15,747✔
2072
                `ALTER TABLE ${this.escapePath(
15,747✔
2073
                    newTable.name,
15,747✔
2074
                )} RENAME TO ${this.escapePath(tableNameOld)}`,
15,747✔
2075
            ),
15,747✔
2076
        )
15,747✔
2077
        downQueries.push(
15,747✔
2078
            new Query(
15,747✔
2079
                `ALTER TABLE ${this.escapePath(
15,747✔
2080
                    oldTable.name,
15,747✔
2081
                )} RENAME TO ${this.escapePath(tableNameNew)}`,
15,747✔
2082
            ),
15,747✔
2083
        )
15,747✔
2084

15,747✔
2085
        newTable.name = oldTable.name
15,747✔
2086

15,747✔
2087
        // recreate table indices
15,747✔
2088
        newTable.indices.forEach((index) => {
15,747✔
2089
            // new index may be passed without name. In this case we generate index name manually.
13,083✔
2090
            if (!index.name)
13,083✔
2091
                index.name = this.connection.namingStrategy.indexName(
13,083!
2092
                    newTable,
×
2093
                    index.columnNames,
×
2094
                    index.where,
×
2095
                )
×
2096
            upQueries.push(this.createIndexSql(newTable, index))
13,083✔
2097
            downQueries.push(this.dropIndexSql(index))
13,083✔
2098
        })
15,747✔
2099

15,747✔
2100
        // update generated columns in "typeorm_metadata" table
15,747✔
2101
        // Step 1: clear data for removed generated columns
15,747✔
2102
        oldTable.columns
15,747✔
2103
            .filter((column) => {
15,747✔
2104
                const newTableColumn = newTable.columns.find(
55,254✔
2105
                    (c) => c.name === column.name,
55,254✔
2106
                )
55,254✔
2107
                // we should delete record from "typeorm_metadata" if generated column was removed
55,254✔
2108
                // or it was changed to non-generated
55,254✔
2109
                return (
55,254✔
2110
                    column.generatedType &&
55,254!
2111
                    column.asExpression &&
55,254!
2112
                    (!newTableColumn ||
45✔
2113
                        (!newTableColumn.generatedType &&
39✔
2114
                            !newTableColumn.asExpression))
45✔
2115
                )
55,254✔
2116
            })
15,747✔
2117
            .forEach((column) => {
15,747✔
2118
                const deleteQuery = this.deleteTypeormMetadataSql({
9✔
2119
                    table: oldTable.name,
9✔
2120
                    type: MetadataTableType.GENERATED_COLUMN,
9✔
2121
                    name: column.name,
9✔
2122
                })
9✔
2123

9✔
2124
                const insertQuery = this.insertTypeormMetadataSql({
9✔
2125
                    table: oldTable.name,
9✔
2126
                    type: MetadataTableType.GENERATED_COLUMN,
9✔
2127
                    name: column.name,
9✔
2128
                    value: column.asExpression,
9✔
2129
                })
9✔
2130

9✔
2131
                upQueries.push(deleteQuery)
9✔
2132
                downQueries.push(insertQuery)
9✔
2133
            })
15,747✔
2134

15,747✔
2135
        // Step 2: add data for new generated columns
15,747✔
2136
        newTable.columns
15,747✔
2137
            .filter(
15,747✔
2138
                (column) =>
15,747✔
2139
                    column.generatedType &&
55,224!
2140
                    column.asExpression &&
55,224!
2141
                    !oldTable.columns.some((c) => c.name === column.name),
15,747✔
2142
            )
15,747✔
2143
            .forEach((column) => {
15,747✔
2144
                const insertQuery = this.insertTypeormMetadataSql({
6✔
2145
                    table: newTable.name,
6✔
2146
                    type: MetadataTableType.GENERATED_COLUMN,
6✔
2147
                    name: column.name,
6✔
2148
                    value: column.asExpression,
6✔
2149
                })
6✔
2150

6✔
2151
                const deleteQuery = this.deleteTypeormMetadataSql({
6✔
2152
                    table: newTable.name,
6✔
2153
                    type: MetadataTableType.GENERATED_COLUMN,
6✔
2154
                    name: column.name,
6✔
2155
                })
6✔
2156

6✔
2157
                upQueries.push(insertQuery)
6✔
2158
                downQueries.push(deleteQuery)
6✔
2159
            })
15,747✔
2160

15,747✔
2161
        // Step 3: update changed expressions
15,747✔
2162
        newTable.columns
15,747✔
2163
            .filter((column) => column.generatedType && column.asExpression)
15,747!
2164
            .forEach((column) => {
15,747✔
2165
                const oldColumn = oldTable.columns.find(
42✔
2166
                    (c) =>
42✔
2167
                        c.name === column.name &&
345✔
2168
                        c.generatedType &&
345✔
2169
                        column.generatedType &&
345✔
2170
                        c.asExpression !== column.asExpression,
42✔
2171
                )
42✔
2172

42✔
2173
                if (!oldColumn) return
42✔
2174

3✔
2175
                // update expression
3✔
2176
                const deleteQuery = this.deleteTypeormMetadataSql({
3✔
2177
                    table: oldTable.name,
3✔
2178
                    type: MetadataTableType.GENERATED_COLUMN,
3✔
2179
                    name: oldColumn.name,
3✔
2180
                })
3✔
2181

3✔
2182
                const insertQuery = this.insertTypeormMetadataSql({
3✔
2183
                    table: newTable.name,
3✔
2184
                    type: MetadataTableType.GENERATED_COLUMN,
3✔
2185
                    name: column.name,
3✔
2186
                    value: column.asExpression,
3✔
2187
                })
3✔
2188

3✔
2189
                upQueries.push(deleteQuery)
3✔
2190
                upQueries.push(insertQuery)
3✔
2191

3✔
2192
                // revert update
3✔
2193
                const revertInsertQuery = this.insertTypeormMetadataSql({
3✔
2194
                    table: newTable.name,
3✔
2195
                    type: MetadataTableType.GENERATED_COLUMN,
3✔
2196
                    name: oldColumn.name,
3✔
2197
                    value: oldColumn.asExpression,
3✔
2198
                })
3✔
2199

3✔
2200
                const revertDeleteQuery = this.deleteTypeormMetadataSql({
3✔
2201
                    table: oldTable.name,
3✔
2202
                    type: MetadataTableType.GENERATED_COLUMN,
3✔
2203
                    name: column.name,
3✔
2204
                })
3✔
2205

3✔
2206
                downQueries.push(revertInsertQuery)
3✔
2207
                downQueries.push(revertDeleteQuery)
3✔
2208
            })
15,747✔
2209

15,747✔
2210
        await this.executeQueries(upQueries, downQueries)
15,747✔
2211
        this.replaceCachedTable(oldTable, newTable)
15,738✔
2212
    }
15,738✔
2213

26✔
2214
    /**
26✔
2215
     * tablePath e.g. "myDB.myTable", "myTable"
26✔
2216
     */
26✔
2217
    protected splitTablePath(tablePath: string): [string | undefined, string] {
26✔
2218
        return (
280,908✔
2219
            tablePath.indexOf(".") !== -1
280,908✔
2220
                ? tablePath.split(".")
280,908!
2221
                : [undefined, tablePath]
280,908✔
2222
        ) as [string | undefined, string]
280,908✔
2223
    }
280,908✔
2224

26✔
2225
    /**
26✔
2226
     * Escapes given table or view path. Tolerates leading/trailing dots
26✔
2227
     */
26✔
2228
    protected escapePath(
26✔
2229
        target: Table | View | string,
352,506✔
2230
        disableEscape?: boolean,
352,506✔
2231
    ): string {
352,506✔
2232
        const tableName =
352,506✔
2233
            InstanceChecker.isTable(target) || InstanceChecker.isView(target)
352,506✔
2234
                ? target.name
352,506!
2235
                : target
352,506✔
2236
        return tableName
352,506✔
2237
            .replace(/^\.+|\.+$/g, "")
352,506✔
2238
            .split(".")
352,506✔
2239
            .map((i) => (disableEscape ? i : `"${i}"`))
352,506!
2240
            .join(".")
352,506✔
2241
    }
352,506✔
2242

26✔
2243
    /**
26✔
2244
     * Change table comment.
26✔
2245
     */
26✔
2246
    changeTableComment(
26✔
2247
        tableOrName: Table | string,
×
2248
        comment?: string,
×
2249
    ): Promise<void> {
×
2250
        throw new TypeORMError(`sqlit driver does not support change comment.`)
×
2251
    }
×
2252
}
26✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc