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

typeorm / typeorm / 12968994847

25 Jan 2025 10:33PM UTC coverage: 71.671% (-0.7%) from 72.369%
12968994847

Pull #11262

github

web-flow
Merge f05a3d4e8 into 79960e136
Pull Request #11262: ci(PR): adjust timeouts and add concurrency

8567 of 12650 branches covered (67.72%)

Branch coverage included in aggregate %.

17727 of 24037 relevant lines covered (73.75%)

135711.91 hits per line

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

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

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

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

39
    protected transactionPromise: Promise<any> | null = null
2,586✔
40

41
    // -------------------------------------------------------------------------
42
    // Constructor
43
    // -------------------------------------------------------------------------
44

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

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

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

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

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

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

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

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

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

118
        await this.broadcaster.broadcast("AfterTransactionStart")
53,208✔
119
    }
120

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

128
        await this.broadcaster.broadcast("BeforeTransactionCommit")
53,073✔
129

130
        if (this.transactionDepth > 1) {
53,073✔
131
            this.transactionDepth -= 1
78✔
132
            await this.query(
78✔
133
                `RELEASE SAVEPOINT typeorm_${this.transactionDepth}`,
134
            )
135
        } else {
136
            this.transactionDepth -= 1
52,995✔
137
            await this.query("COMMIT")
52,995✔
138
            this.isTransactionActive = false
52,995✔
139
        }
140

141
        await this.broadcaster.broadcast("AfterTransactionCommit")
53,073✔
142
    }
143

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

151
        await this.broadcaster.broadcast("BeforeTransactionRollback")
135✔
152

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

164
        await this.broadcaster.broadcast("AfterTransactionRollback")
135✔
165
    }
166

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

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

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

194
    /**
195
     * Checks if database with the given name exist.
196
     */
197
    async hasDatabase(database: string): Promise<boolean> {
198
        return Promise.resolve(false)
×
199
    }
200

201
    /**
202
     * Loads currently using database
203
     */
204
    async getCurrentDatabase(): Promise<undefined> {
205
        return Promise.resolve(undefined)
×
206
    }
207

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

215
    /**
216
     * Loads currently using database schema
217
     */
218
    async getCurrentSchema(): Promise<undefined> {
219
        return Promise.resolve(undefined)
×
220
    }
221

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

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

249
    /**
250
     * Creates a new database.
251
     */
252
    async createDatabase(
253
        database: string,
254
        ifNotExist?: boolean,
255
    ): Promise<void> {
256
        return Promise.resolve()
6✔
257
    }
258

259
    /**
260
     * Drops database.
261
     */
262
    async dropDatabase(database: string, ifExist?: boolean): Promise<void> {
263
        return Promise.resolve()
×
264
    }
265

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

276
    /**
277
     * Drops table schema.
278
     */
279
    async dropSchema(schemaPath: string, ifExist?: boolean): Promise<void> {
280
        return Promise.resolve()
×
281
    }
282

283
    /**
284
     * Creates a new table.
285
     */
286
    async createTable(
287
        table: Table,
288
        ifNotExist: boolean = false,
150✔
289
        createForeignKeys: boolean = true,
264✔
290
        createIndices: boolean = true,
25,344✔
291
    ): Promise<void> {
292
        const upQueries: Query[] = []
25,344✔
293
        const downQueries: Query[] = []
25,344✔
294

295
        if (ifNotExist) {
25,344✔
296
            const isTableExist = await this.hasTable(table)
114✔
297
            if (isTableExist) return Promise.resolve()
114✔
298
        }
299

300
        upQueries.push(this.createTableSql(table, createForeignKeys))
25,338✔
301
        downQueries.push(this.dropTableSql(table))
25,338✔
302

303
        if (createIndices) {
25,338✔
304
            table.indices.forEach((index) => {
25,338✔
305
                // new index may be passed without name. In this case we generate index name manually.
306
                if (!index.name)
9,237✔
307
                    index.name = this.connection.namingStrategy.indexName(
24✔
308
                        table,
309
                        index.columnNames,
310
                        index.where,
311
                    )
312
                upQueries.push(this.createIndexSql(table, index))
9,237✔
313
                downQueries.push(this.dropIndexSql(index))
9,237✔
314
            })
315
        }
316

317
        // if table have column with generated type, we must add the expression to the metadata table
318
        const generatedColumns = table.columns.filter(
25,338✔
319
            (column) => column.generatedType && column.asExpression,
78,324!
320
        )
321

322
        for (const column of generatedColumns) {
25,338✔
323
            const insertQuery = this.insertTypeormMetadataSql({
×
324
                table: table.name,
325
                type: MetadataTableType.GENERATED_COLUMN,
326
                name: column.name,
327
                value: column.asExpression,
328
            })
329

330
            const deleteQuery = this.deleteTypeormMetadataSql({
×
331
                table: table.name,
332
                type: MetadataTableType.GENERATED_COLUMN,
333
                name: column.name,
334
            })
335

336
            upQueries.push(insertQuery)
×
337
            downQueries.push(deleteQuery)
×
338
        }
339

340
        await this.executeQueries(upQueries, downQueries)
25,338✔
341
    }
342

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

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

365
        if (dropIndices) {
36✔
366
            table.indices.forEach((index) => {
36✔
367
                upQueries.push(this.dropIndexSql(index))
6✔
368
                downQueries.push(this.createIndexSql(table, index))
6✔
369
            })
370
        }
371

372
        upQueries.push(this.dropTableSql(table, ifExist))
36✔
373
        downQueries.push(this.createTableSql(table, createForeignKeys))
36✔
374

375
        // if table had columns with generated type, we must remove the expression from the metadata table
376
        const generatedColumns = table.columns.filter(
36✔
377
            (column) => column.generatedType && column.asExpression,
90!
378
        )
379

380
        for (const column of generatedColumns) {
36✔
381
            const deleteQuery = this.deleteTypeormMetadataSql({
×
382
                table: table.name,
383
                type: MetadataTableType.GENERATED_COLUMN,
384
                name: column.name,
385
            })
386

387
            const insertQuery = this.insertTypeormMetadataSql({
×
388
                table: table.name,
389
                type: MetadataTableType.GENERATED_COLUMN,
390
                name: column.name,
391
                value: column.asExpression,
392
            })
393

394
            upQueries.push(deleteQuery)
×
395
            downQueries.push(insertQuery)
×
396
        }
397

398
        await this.executeQueries(upQueries, downQueries)
36✔
399
    }
400

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

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

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

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

446
        newTable.name = newTableName
78✔
447

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

461
        // rename unique constraints
462
        newTable.uniques.forEach((unique) => {
78✔
463
            const oldUniqueName =
464
                this.connection.namingStrategy.uniqueConstraintName(
42✔
465
                    oldTable,
466
                    unique.columnNames,
467
                )
468

469
            // Skip renaming if Unique has user defined constraint name
470
            if (unique.name !== oldUniqueName) return
42✔
471

472
            unique.name = this.connection.namingStrategy.uniqueConstraintName(
18✔
473
                newTable,
474
                unique.columnNames,
475
            )
476
        })
477

478
        // rename foreign key constraints
479
        newTable.foreignKeys.forEach((foreignKey) => {
78✔
480
            const oldForeignKeyName =
481
                this.connection.namingStrategy.foreignKeyName(
54✔
482
                    oldTable,
483
                    foreignKey.columnNames,
484
                    this.getTablePath(foreignKey),
485
                    foreignKey.referencedColumnNames,
486
                )
487

488
            // Skip renaming if foreign key has user defined constraint name
489
            if (foreignKey.name !== oldForeignKeyName) return
54✔
490

491
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
6✔
492
                newTable,
493
                foreignKey.columnNames,
494
                this.getTablePath(foreignKey),
495
                foreignKey.referencedColumnNames,
496
            )
497
        })
498

499
        // rename indices
500
        newTable.indices.forEach((index) => {
78✔
501
            const oldIndexName = this.connection.namingStrategy.indexName(
54✔
502
                oldTable,
503
                index.columnNames,
504
                index.where,
505
            )
506

507
            // Skip renaming if Index has user defined constraint name
508
            if (index.name !== oldIndexName) return
54✔
509

510
            index.name = this.connection.namingStrategy.indexName(
30✔
511
                newTable,
512
                index.columnNames,
513
                index.where,
514
            )
515
        })
516

517
        // rename old table;
518
        oldTable.name = newTable.name
78✔
519

520
        // recreate table with new constraint names
521
        await this.recreateTable(newTable, oldTable)
78✔
522
    }
523

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

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

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

571
        let newColumn: TableColumn | undefined = undefined
78✔
572
        if (InstanceChecker.isTableColumn(newTableColumnOrName)) {
78✔
573
            newColumn = newTableColumnOrName
48✔
574
        } else {
575
            newColumn = oldColumn.clone()
30✔
576
            newColumn.name = newTableColumnOrName
30✔
577
        }
578

579
        return this.changeColumn(table, oldColumn, newColumn)
78✔
580
    }
581

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

601
        await this.changeColumns(table, [{ oldColumn, newColumn }])
117✔
602
    }
603

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

630
                        unique.columnNames.splice(
48✔
631
                            unique.columnNames.indexOf(
632
                                changedColumnSet.oldColumn.name,
633
                            ),
634
                            1,
635
                        )
636
                        unique.columnNames.push(changedColumnSet.newColumn.name)
48✔
637

638
                        // rename Unique only if it has default constraint name
639
                        if (unique.name === uniqueName) {
48✔
640
                            unique.name =
24✔
641
                                this.connection.namingStrategy.uniqueConstraintName(
642
                                    changedTable,
643
                                    unique.columnNames,
644
                                )
645
                        }
646
                    })
647

648
                changedTable
123✔
649
                    .findColumnForeignKeys(changedColumnSet.oldColumn)
650
                    .forEach((foreignKey) => {
651
                        const foreignKeyName =
652
                            this.connection.namingStrategy.foreignKeyName(
54✔
653
                                table,
654
                                foreignKey.columnNames,
655
                                this.getTablePath(foreignKey),
656
                                foreignKey.referencedColumnNames,
657
                            )
658

659
                        foreignKey.columnNames.splice(
54✔
660
                            foreignKey.columnNames.indexOf(
661
                                changedColumnSet.oldColumn.name,
662
                            ),
663
                            1,
664
                        )
665
                        foreignKey.columnNames.push(
54✔
666
                            changedColumnSet.newColumn.name,
667
                        )
668

669
                        // rename FK only if it has default constraint name
670
                        if (foreignKey.name === foreignKeyName) {
54✔
671
                            foreignKey.name =
6✔
672
                                this.connection.namingStrategy.foreignKeyName(
673
                                    changedTable,
674
                                    foreignKey.columnNames,
675
                                    this.getTablePath(foreignKey),
676
                                    foreignKey.referencedColumnNames,
677
                                )
678
                        }
679
                    })
680

681
                changedTable
123✔
682
                    .findColumnIndices(changedColumnSet.oldColumn)
683
                    .forEach((index) => {
684
                        const indexName =
685
                            this.connection.namingStrategy.indexName(
42✔
686
                                table,
687
                                index.columnNames,
688
                                index.where,
689
                            )
690

691
                        index.columnNames.splice(
42✔
692
                            index.columnNames.indexOf(
693
                                changedColumnSet.oldColumn.name,
694
                            ),
695
                            1,
696
                        )
697
                        index.columnNames.push(changedColumnSet.newColumn.name)
42✔
698

699
                        // rename Index only if it has default constraint name
700
                        if (index.name === indexName) {
42✔
701
                            index.name =
30✔
702
                                this.connection.namingStrategy.indexName(
703
                                    changedTable,
704
                                    index.columnNames,
705
                                    index.where,
706
                                )
707
                        }
708
                    })
709
            }
710
            const originalColumn = changedTable.columns.find(
285✔
711
                (column) => column.name === changedColumnSet.oldColumn.name,
669✔
712
            )
713
            if (originalColumn)
285✔
714
                changedTable.columns[
285✔
715
                    changedTable.columns.indexOf(originalColumn)
716
                ] = changedColumnSet.newColumn
717
        })
718

719
        await this.recreateTable(changedTable, table)
240✔
720
    }
721

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

740
        await this.dropColumns(table, [column])
12✔
741
    }
742

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

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

765
            changedTable.removeColumn(columnInstance)
66✔
766
            changedTable
66✔
767
                .findColumnUniques(columnInstance)
768
                .forEach((unique) =>
769
                    changedTable.removeUniqueConstraint(unique),
12✔
770
                )
771
            changedTable
66✔
772
                .findColumnIndices(columnInstance)
773
                .forEach((index) => changedTable.removeIndex(index))
×
774
            changedTable
66✔
775
                .findColumnForeignKeys(columnInstance)
776
                .forEach((fk) => changedTable.removeForeignKey(fk))
×
777
        })
778

779
        await this.recreateTable(changedTable, table)
36✔
780
    }
781

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

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

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

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

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

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

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

858
        // clone original table and add unique constraints in to cloned table
859
        const changedTable = table.clone()
12✔
860
        uniqueConstraints.forEach((uniqueConstraint) =>
12✔
861
            changedTable.addUniqueConstraint(uniqueConstraint),
12✔
862
        )
863
        await this.recreateTable(changedTable, table)
12✔
864
    }
865

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

884
        await this.dropUniqueConstraints(table, [uniqueConstraint])
3✔
885
    }
886

887
    /**
888
     * Creates an unique constraints.
889
     */
890
    async dropUniqueConstraints(
891
        tableOrName: Table | string,
892
        uniqueConstraints: TableUnique[],
893
    ): Promise<void> {
894
        const table = InstanceChecker.isTable(tableOrName)
15!
895
            ? tableOrName
896
            : await this.getCachedTable(tableOrName)
897

898
        // clone original table and remove unique constraints from cloned table
899
        const changedTable = table.clone()
15✔
900
        uniqueConstraints.forEach((uniqueConstraint) =>
15✔
901
            changedTable.removeUniqueConstraint(uniqueConstraint),
15✔
902
        )
903

904
        await this.recreateTable(changedTable, table)
15✔
905
    }
906

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

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

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

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

954
        await this.dropCheckConstraints(table, [checkConstraint])
6✔
955
    }
956

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

968
        // clone original table and remove check constraints from cloned table
969
        const changedTable = table.clone()
24✔
970
        checkConstraints.forEach((checkConstraint) =>
24✔
971
            changedTable.removeCheckConstraint(checkConstraint),
24✔
972
        )
973

974
        await this.recreateTable(changedTable, table)
24✔
975
    }
976

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

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

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

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

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

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

1043
        await this.recreateTable(changedTable, table)
9,294✔
1044
    }
1045

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

1064
        await this.dropForeignKeys(tableOrName, [foreignKey])
6✔
1065
    }
1066

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

1078
        // clone original table and remove foreign keys from cloned table
1079
        const changedTable = table.clone()
36✔
1080
        foreignKeys.forEach((foreignKey) =>
36✔
1081
            changedTable.removeForeignKey(foreignKey),
36✔
1082
        )
1083

1084
        await this.recreateTable(changedTable, table)
36✔
1085
    }
1086

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

1098
        // new index may be passed without name. In this case we generate index name manually.
1099
        if (!index.name) index.name = this.generateIndexName(table, index)
60✔
1100

1101
        const up = this.createIndexSql(table, index)
60✔
1102
        const down = this.dropIndexSql(index)
60✔
1103
        await this.executeQueries(up, down)
60✔
1104
        table.addIndex(index)
60✔
1105
    }
1106

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

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

1138
        // old index may be passed without name. In this case we generate index name manually.
1139
        if (!index.name) index.name = this.generateIndexName(table, index)
66✔
1140

1141
        const up = this.dropIndexSql(index)
66✔
1142
        const down = this.createIndexSql(table, index)
66✔
1143
        await this.executeQueries(up, down)
66✔
1144
        table.removeIndex(index)
66✔
1145
    }
1146

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

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

1168
    /**
1169
     * Removes all tables from the currently connected database.
1170
     */
1171
    async clearDatabase(database?: string): Promise<void> {
1172
        let dbPath: string | undefined = undefined
7,314✔
1173
        if (
7,314✔
1174
            database &&
11,067✔
1175
            this.driver.getAttachedDatabaseHandleByRelativePath(database)
1176
        ) {
1177
            dbPath =
3✔
1178
                this.driver.getAttachedDatabaseHandleByRelativePath(database)
1179
        }
1180

1181
        await this.query(`PRAGMA foreign_keys = OFF`)
7,314✔
1182

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

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

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

1219
    // -------------------------------------------------------------------------
1220
    // Protected Methods
1221
    // -------------------------------------------------------------------------
1222

1223
    protected async loadViews(viewNames?: string[]): Promise<View[]> {
1224
        const hasTable = await this.hasTable(this.getTypeormMetadataTableName())
7,602✔
1225
        if (!hasTable) {
7,602✔
1226
            return []
7,569✔
1227
        }
1228

1229
        if (!viewNames) {
33!
1230
            viewNames = []
×
1231
        }
1232

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

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

1276
    protected async loadPragmaRecords(tablePath: string, pragma: string) {
1277
        const [, tableName] = this.splitTablePath(tablePath)
3,249✔
1278
        return this.query(`PRAGMA ${pragma}("${tableName}")`)
3,249✔
1279
    }
1280

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

1290
        let dbTables: { database?: string; name: string; sql: string }[] = []
8,814✔
1291
        let dbIndicesDef: ObjectLiteral[]
1292

1293
        if (!tableNames) {
8,814✔
1294
            const tablesSql = `SELECT * FROM "sqlite_master" WHERE "type" = 'table'`
3✔
1295
            dbTables.push(...(await this.query(tablesSql)))
3✔
1296

1297
            const tableNamesString = dbTables
3✔
1298
                .map(({ name }) => `'${name}'`)
12✔
1299
                .join(", ")
1300
            dbIndicesDef = await this.query(
3✔
1301
                `SELECT * FROM "sqlite_master" WHERE "type" = 'index' AND "tbl_name" IN (${tableNamesString})`,
1302
            )
1303
        } else {
1304
            const tableNamesWithoutDot = tableNames
8,811✔
1305
                .filter((tableName) => {
1306
                    return tableName.split(".").length === 1
27,516✔
1307
                })
1308
                .map((tableName) => `'${tableName}'`)
27,513✔
1309

1310
            const tableNamesWithDot = tableNames.filter((tableName) => {
8,811✔
1311
                return tableName.split(".").length > 1
27,516✔
1312
            })
1313

1314
            const queryPromises = (type: "table" | "index") => {
8,811✔
1315
                const promises = [
17,622✔
1316
                    ...tableNamesWithDot.map((tableName) =>
1317
                        this.loadTableRecords(tableName, type),
6✔
1318
                    ),
1319
                ]
1320

1321
                if (tableNamesWithoutDot.length) {
17,622✔
1322
                    promises.push(
17,616✔
1323
                        this.query(
1324
                            `SELECT * FROM "sqlite_master" WHERE "type" = '${type}' AND "${
1325
                                type === "table" ? "name" : "tbl_name"
17,616✔
1326
                            }" IN (${tableNamesWithoutDot})`,
1327
                        ),
1328
                    )
1329
                }
1330

1331
                return promises
17,622✔
1332
            }
1333
            dbTables = (await Promise.all(queryPromises("table")))
8,811✔
1334
                .reduce((acc, res) => [...acc, ...res], [])
8,811✔
1335
                .filter(Boolean)
1336
            dbIndicesDef = (await Promise.all(queryPromises("index")))
8,811✔
1337
                .reduce((acc, res) => [...acc, ...res], [])
8,811✔
1338
                .filter(Boolean)
1339
        }
1340

1341
        // if tables were not found in the db, no need to proceed
1342
        if (dbTables.length === 0) {
8,814✔
1343
            return []
7,377✔
1344
        }
1345

1346
        // create table schemas for loaded tables
1347
        return Promise.all(
1,437✔
1348
            dbTables.map(async (dbTable) => {
1349
                const tablePath =
1350
                    dbTable["database"] &&
2,286!
1351
                    this.driver.getAttachedDatabaseHandleByRelativePath(
1352
                        dbTable["database"],
1353
                    )
1354
                        ? `${this.driver.getAttachedDatabaseHandleByRelativePath(
1355
                              dbTable["database"],
1356
                          )}.${dbTable["name"]}`
1357
                        : dbTable["name"]
1358

1359
                const sql = dbTable["sql"]
2,286✔
1360

1361
                const withoutRowid = sql.includes("WITHOUT ROWID")
2,286✔
1362
                const table = new Table({ name: tablePath, withoutRowid })
2,286✔
1363

1364
                // load columns and indices
1365
                const [dbColumns, dbIndices, dbForeignKeys]: ObjectLiteral[][] =
1366
                    await Promise.all([
2,286✔
1367
                        this.loadPragmaRecords(tablePath, `table_xinfo`),
1368
                        this.loadPragmaRecords(tablePath, `index_list`),
1369
                        this.loadPragmaRecords(tablePath, `foreign_key_list`),
1370
                    ])
1371

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

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

1433
                        if (
7,716!
1434
                            dbColumn["hidden"] === 2 ||
15,432✔
1435
                            dbColumn["hidden"] === 3
1436
                        ) {
1437
                            tableColumn.generatedType =
×
1438
                                dbColumn["hidden"] === 2 ? "VIRTUAL" : "STORED"
×
1439

1440
                            const asExpressionQuery =
1441
                                this.selectTypeormMetadataSql({
×
1442
                                    table: table.name,
1443
                                    type: MetadataTableType.GENERATED_COLUMN,
1444
                                    name: tableColumn.name,
1445
                                })
1446

1447
                            const results = await this.query(
×
1448
                                asExpressionQuery.query,
1449
                                asExpressionQuery.parameters,
1450
                            )
1451
                            if (results[0] && results[0].value) {
×
1452
                                tableColumn.asExpression = results[0].value
×
1453
                            } else {
1454
                                tableColumn.asExpression = ""
×
1455
                            }
1456
                        }
1457

1458
                        if (tableColumn.type === "varchar") {
7,716✔
1459
                            tableColumn.enum = OrmUtils.parseSqlCheckExpression(
3,735✔
1460
                                sql,
1461
                                tableColumn.name,
1462
                            )
1463
                        }
1464

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

1511
                        return tableColumn
7,716✔
1512
                    }),
1513
                )
1514

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

1534
                // build foreign keys
1535
                const tableForeignKeyConstraints = OrmUtils.uniq(
2,286✔
1536
                    dbForeignKeys,
1537
                    (dbForeignKey) => dbForeignKey["id"],
1,266✔
1538
                )
1539

1540
                table.foreignKeys = tableForeignKeyConstraints.map(
2,286✔
1541
                    (foreignKey) => {
1542
                        const ownForeignKeys = dbForeignKeys.filter(
915✔
1543
                            (dbForeignKey) =>
1544
                                dbForeignKey["id"] === foreignKey["id"] &&
1,581✔
1545
                                dbForeignKey["table"] === foreignKey["table"],
1546
                        )
1547
                        const columnNames = ownForeignKeys.map(
915✔
1548
                            (dbForeignKey) => dbForeignKey["from"],
927✔
1549
                        )
1550
                        const referencedColumnNames = ownForeignKeys.map(
915✔
1551
                            (dbForeignKey) => dbForeignKey["to"],
927✔
1552
                        )
1553

1554
                        // find related foreign key mapping
1555
                        const fkMapping = fkMappings.find(
915✔
1556
                            (it) =>
1557
                                it.referencedTableName ===
1,239✔
1558
                                    foreignKey["table"] &&
1559
                                it.columns.every(
1560
                                    (column) =>
1561
                                        columnNames.indexOf(column) !== -1,
930✔
1562
                                ),
1563
                        )
1564

1565
                        return new TableForeignKey({
915✔
1566
                            name: fkMapping?.name,
1567
                            columnNames: columnNames,
1568
                            referencedTableName: foreignKey["table"],
1569
                            referencedColumnNames: referencedColumnNames,
1570
                            onDelete: foreignKey["on_delete"],
1571
                            onUpdate: foreignKey["on_update"],
1572
                        })
1573
                    },
1574
                )
1575

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

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

1620
                        // find existent mapping by a column names
1621
                        const foundMapping = uniqueMappings.find((mapping) => {
1,209✔
1622
                            return mapping!.columns.every(
1,767✔
1623
                                (column) => indexColumns.indexOf(column) !== -1,
2,259✔
1624
                            )
1625
                        })
1626

1627
                        return new TableUnique({
1,209✔
1628
                            name: foundMapping
1,209!
1629
                                ? foundMapping.name
1630
                                : this.connection.namingStrategy.uniqueConstraintName(
1631
                                      table,
1632
                                      indexColumns,
1633
                                  ),
1634
                            columnNames: indexColumns,
1635
                        })
1636
                    })
1637
                table.uniques = (await Promise.all(
2,286✔
1638
                    tableUniquePromises,
1639
                )) as TableUnique[]
1640

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

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

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

1699
                return table
2,286✔
1700
            }),
1701
        )
1702
    }
1703

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

1725
        const columnDefinitions = table.columns
45,054✔
1726
            .map((column) => this.buildCreateColumnSql(column, skipPrimary))
147,009✔
1727
            .join(", ")
1728
        const [database] = this.splitTablePath(table.name)
45,054✔
1729
        let sql = `CREATE TABLE ${this.escapePath(
45,054✔
1730
            table.name,
1731
        )} (${columnDefinitions}`
1732

1733
        let [databaseNew, tableName] = this.splitTablePath(table.name)
45,054✔
1734
        const newTableName = temporaryTable
45,054✔
1735
            ? `${databaseNew ? `${databaseNew}.` : ""}${tableName.replace(
9,840!
1736
                  /^temporary_/,
1737
                  "",
1738
              )}`
1739
            : table.name
1740

1741
        // need for `addColumn()` method, because it recreates table.
1742
        table.columns
45,054✔
1743
            .filter((column) => column.isUnique)
147,009✔
1744
            .forEach((column) => {
1745
                const isUniqueExist = table.uniques.some(
8,493✔
1746
                    (unique) =>
1747
                        unique.columnNames.length === 1 &&
12,051✔
1748
                        unique.columnNames[0] === column.name,
1749
                )
1750
                if (!isUniqueExist)
8,493✔
1751
                    table.uniques.push(
48✔
1752
                        new TableUnique({
1753
                            name: this.connection.namingStrategy.uniqueConstraintName(
1754
                                table,
1755
                                [column.name],
1756
                            ),
1757
                            columnNames: [column.name],
1758
                        }),
1759
                    )
1760
            })
1761

1762
        if (table.uniques.length > 0) {
45,054✔
1763
            const uniquesSql = table.uniques
7,455✔
1764
                .map((unique) => {
1765
                    const uniqueName = unique.name
11,106✔
1766
                        ? unique.name
1767
                        : this.connection.namingStrategy.uniqueConstraintName(
1768
                              newTableName,
1769
                              unique.columnNames,
1770
                          )
1771
                    const columnNames = unique.columnNames
11,106✔
1772
                        .map((columnName) => `"${columnName}"`)
14,343✔
1773
                        .join(", ")
1774
                    return `CONSTRAINT "${uniqueName}" UNIQUE (${columnNames})`
11,106✔
1775
                })
1776
                .join(", ")
1777

1778
            sql += `, ${uniquesSql}`
7,455✔
1779
        }
1780

1781
        if (table.checks.length > 0) {
45,054✔
1782
            const checksSql = table.checks
819✔
1783
                .map((check) => {
1784
                    const checkName = check.name
843✔
1785
                        ? check.name
1786
                        : this.connection.namingStrategy.checkConstraintName(
1787
                              newTableName,
1788
                              check.expression!,
1789
                          )
1790
                    return `CONSTRAINT "${checkName}" CHECK (${check.expression})`
843✔
1791
                })
1792
                .join(", ")
1793

1794
            sql += `, ${checksSql}`
819✔
1795
        }
1796

1797
        if (table.foreignKeys.length > 0 && createForeignKeys) {
45,054✔
1798
            const foreignKeysSql = table.foreignKeys
9,528✔
1799
                .filter((fk) => {
1800
                    const [referencedDatabase] = this.splitTablePath(
16,578✔
1801
                        fk.referencedTableName,
1802
                    )
1803
                    if (referencedDatabase !== database) {
16,578!
1804
                        return false
×
1805
                    }
1806
                    return true
16,578✔
1807
                })
1808
                .map((fk) => {
1809
                    const [, referencedTable] = this.splitTablePath(
16,578✔
1810
                        fk.referencedTableName,
1811
                    )
1812
                    const columnNames = fk.columnNames
16,578✔
1813
                        .map((columnName) => `"${columnName}"`)
18,900✔
1814
                        .join(", ")
1815
                    if (!fk.name)
16,578✔
1816
                        fk.name = this.connection.namingStrategy.foreignKeyName(
30✔
1817
                            newTableName,
1818
                            fk.columnNames,
1819
                            this.getTablePath(fk),
1820
                            fk.referencedColumnNames,
1821
                        )
1822
                    const referencedColumnNames = fk.referencedColumnNames
16,578✔
1823
                        .map((columnName) => `"${columnName}"`)
18,900✔
1824
                        .join(", ")
1825

1826
                    let constraint = `CONSTRAINT "${fk.name}" FOREIGN KEY (${columnNames}) REFERENCES "${referencedTable}" (${referencedColumnNames})`
16,578✔
1827
                    if (fk.onDelete) constraint += ` ON DELETE ${fk.onDelete}`
16,578✔
1828
                    if (fk.onUpdate) constraint += ` ON UPDATE ${fk.onUpdate}`
16,578✔
1829
                    if (fk.deferrable)
16,578!
1830
                        constraint += ` DEFERRABLE ${fk.deferrable}`
×
1831

1832
                    return constraint
16,578✔
1833
                })
1834
                .join(", ")
1835

1836
            sql += `, ${foreignKeysSql}`
9,528✔
1837
        }
1838

1839
        if (primaryColumns.length > 1) {
45,054✔
1840
            const columnNames = primaryColumns
13,935✔
1841
                .map((column) => `"${column.name}"`)
31,788✔
1842
                .join(", ")
1843
            sql += `, PRIMARY KEY (${columnNames})`
13,935✔
1844
        }
1845

1846
        sql += `)`
45,054✔
1847

1848
        if (table.withoutRowid) {
45,054✔
1849
            sql += " WITHOUT ROWID"
198✔
1850
        }
1851

1852
        return new Query(sql)
45,054✔
1853
    }
1854

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

1871
    protected createViewSql(view: View): Query {
1872
        if (typeof view.expression === "string") {
45✔
1873
            return new Query(`CREATE VIEW "${view.name}" AS ${view.expression}`)
9✔
1874
        } else {
1875
            return new Query(
36✔
1876
                `CREATE VIEW "${view.name}" AS ${view
1877
                    .expression(this.connection)
1878
                    .getQuery()}`,
1879
            )
1880
        }
1881
    }
1882

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

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

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

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

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

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

1959
        if (column.enum)
147,009✔
1960
            c +=
63✔
1961
                ' CHECK( "' +
1962
                column.name +
1963
                '" IN (' +
1964
                column.enum.map((val) => "'" + val + "'").join(",") +
198✔
1965
                ") )"
1966
        if (column.isPrimary && !skipPrimary) c += " PRIMARY KEY"
147,009✔
1967
        if (
147,009✔
1968
            column.isGenerated === true &&
170,487✔
1969
            column.generationStrategy === "increment"
1970
        )
1971
            // don't use skipPrimary here since updates can update already exist primary without auto inc.
1972
            c += " AUTOINCREMENT"
22,515✔
1973
        if (column.collation) c += " COLLATE " + column.collation
147,009!
1974
        if (column.isNullable !== true) c += " NOT NULL"
147,009✔
1975

1976
        if (column.asExpression) {
147,009!
1977
            c += ` AS (${column.asExpression}) ${
×
1978
                column.generatedType ? column.generatedType : "VIRTUAL"
×
1979
            }`
1980
        } else {
1981
            if (column.default !== undefined && column.default !== null)
147,009✔
1982
                c += " DEFAULT (" + column.default + ")"
6,279✔
1983
        }
1984

1985
        return c
147,009✔
1986
    }
1987

1988
    protected async recreateTable(
1989
        newTable: Table,
1990
        oldTable: Table,
1991
        migrateData = true,
9,840✔
1992
    ): Promise<void> {
1993
        const upQueries: Query[] = []
9,840✔
1994
        const downQueries: Query[] = []
9,840✔
1995

1996
        // drop old table indices
1997
        oldTable.indices.forEach((index) => {
9,840✔
1998
            upQueries.push(this.dropIndexSql(index))
8,493✔
1999
            downQueries.push(this.createIndexSql(oldTable, index))
8,493✔
2000
        })
2001

2002
        // change table name into 'temporary_table'
2003
        let [databaseNew, tableNameNew] = this.splitTablePath(newTable.name)
9,840✔
2004
        let [, tableNameOld] = this.splitTablePath(oldTable.name)
9,840✔
2005
        newTable.name = tableNameNew = `${
9,840✔
2006
            databaseNew ? `${databaseNew}.` : ""
9,840!
2007
        }temporary_${tableNameNew}`
2008

2009
        // create new table
2010
        upQueries.push(this.createTableSql(newTable, true, true))
9,840✔
2011
        downQueries.push(this.dropTableSql(newTable))
9,840✔
2012

2013
        // migrate all data from the old table into new table
2014
        if (migrateData) {
9,840✔
2015
            let newColumnNames = newTable.columns
9,840✔
2016
                .filter((column) => !column.generatedType)
34,290✔
2017
                .map((column) => `"${column.name}"`)
34,290✔
2018

2019
            let oldColumnNames = oldTable.columns
9,840✔
2020
                .filter((column) => !column.generatedType)
34,305✔
2021
                .map((column) => `"${column.name}"`)
34,305✔
2022

2023
            if (oldColumnNames.length < newColumnNames.length) {
9,840✔
2024
                newColumnNames = newTable.columns
45✔
2025
                    .filter((column) => {
2026
                        const oldColumn = oldTable.columns.find(
225✔
2027
                            (c) => c.name === column.name,
693✔
2028
                        )
2029
                        if (oldColumn && oldColumn.generatedType) return false
225!
2030
                        return !column.generatedType && oldColumn
225✔
2031
                    })
2032
                    .map((column) => `"${column.name}"`)
180✔
2033
            } else if (oldColumnNames.length > newColumnNames.length) {
9,795✔
2034
                oldColumnNames = oldTable.columns
36✔
2035
                    .filter((column) => {
2036
                        return (
144✔
2037
                            !column.generatedType &&
288✔
2038
                            newTable.columns.find((c) => c.name === column.name)
264✔
2039
                        )
2040
                    })
2041
                    .map((column) => `"${column.name}"`)
78✔
2042
            }
2043

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

2068
        // drop old table
2069
        upQueries.push(this.dropTableSql(oldTable))
9,840✔
2070
        downQueries.push(this.createTableSql(oldTable, true))
9,840✔
2071

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

2088
        newTable.name = oldTable.name
9,840✔
2089

2090
        // recreate table indices
2091
        newTable.indices.forEach((index) => {
9,840✔
2092
            // new index may be passed without name. In this case we generate index name manually.
2093
            if (!index.name)
8,493!
2094
                index.name = this.connection.namingStrategy.indexName(
×
2095
                    newTable,
2096
                    index.columnNames,
2097
                    index.where,
2098
                )
2099
            upQueries.push(this.createIndexSql(newTable, index))
8,493✔
2100
            downQueries.push(this.dropIndexSql(index))
8,493✔
2101
        })
2102

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

2127
                const insertQuery = this.insertTypeormMetadataSql({
×
2128
                    table: oldTable.name,
2129
                    type: MetadataTableType.GENERATED_COLUMN,
2130
                    name: column.name,
2131
                    value: column.asExpression,
2132
                })
2133

2134
                upQueries.push(deleteQuery)
×
2135
                downQueries.push(insertQuery)
×
2136
            })
2137

2138
        // Step 2: add data for new generated columns
2139
        newTable.columns
9,840✔
2140
            .filter(
2141
                (column) =>
2142
                    column.generatedType &&
34,290!
2143
                    column.asExpression &&
2144
                    !oldTable.columns.some((c) => c.name === column.name),
×
2145
            )
2146
            .forEach((column) => {
2147
                const insertQuery = this.insertTypeormMetadataSql({
×
2148
                    table: newTable.name,
2149
                    type: MetadataTableType.GENERATED_COLUMN,
2150
                    name: column.name,
2151
                    value: column.asExpression,
2152
                })
2153

2154
                const deleteQuery = this.deleteTypeormMetadataSql({
×
2155
                    table: newTable.name,
2156
                    type: MetadataTableType.GENERATED_COLUMN,
2157
                    name: column.name,
2158
                })
2159

2160
                upQueries.push(insertQuery)
×
2161
                downQueries.push(deleteQuery)
×
2162
            })
2163

2164
        // Step 3: update changed expressions
2165
        newTable.columns
9,840✔
2166
            .filter((column) => column.generatedType && column.asExpression)
34,290!
2167
            .forEach((column) => {
2168
                const oldColumn = oldTable.columns.find(
×
2169
                    (c) =>
2170
                        c.name === column.name &&
×
2171
                        c.generatedType &&
2172
                        column.generatedType &&
2173
                        c.asExpression !== column.asExpression,
2174
                )
2175

2176
                if (!oldColumn) return
×
2177

2178
                // update expression
2179
                const deleteQuery = this.deleteTypeormMetadataSql({
×
2180
                    table: oldTable.name,
2181
                    type: MetadataTableType.GENERATED_COLUMN,
2182
                    name: oldColumn.name,
2183
                })
2184

2185
                const insertQuery = this.insertTypeormMetadataSql({
×
2186
                    table: newTable.name,
2187
                    type: MetadataTableType.GENERATED_COLUMN,
2188
                    name: column.name,
2189
                    value: column.asExpression,
2190
                })
2191

2192
                upQueries.push(deleteQuery)
×
2193
                upQueries.push(insertQuery)
×
2194

2195
                // revert update
2196
                const revertInsertQuery = this.insertTypeormMetadataSql({
×
2197
                    table: newTable.name,
2198
                    type: MetadataTableType.GENERATED_COLUMN,
2199
                    name: oldColumn.name,
2200
                    value: oldColumn.asExpression,
2201
                })
2202

2203
                const revertDeleteQuery = this.deleteTypeormMetadataSql({
×
2204
                    table: oldTable.name,
2205
                    type: MetadataTableType.GENERATED_COLUMN,
2206
                    name: column.name,
2207
                })
2208

2209
                downQueries.push(revertInsertQuery)
×
2210
                downQueries.push(revertDeleteQuery)
×
2211
            })
2212

2213
        await this.executeQueries(upQueries, downQueries)
9,840✔
2214
        this.replaceCachedTable(oldTable, newTable)
9,834✔
2215
    }
2216

2217
    /**
2218
     * tablePath e.g. "myDB.myTable", "myTable"
2219
     */
2220
    protected splitTablePath(tablePath: string): [string | undefined, string] {
2221
        return (
176,163✔
2222
            tablePath.indexOf(".") !== -1
176,163✔
2223
                ? tablePath.split(".")
2224
                : [undefined, tablePath]
2225
        ) as [string | undefined, string]
2226
    }
2227

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

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

© 2025 Coveralls, Inc