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

typeorm / typeorm / 23390157208

21 Mar 2026 10:26PM UTC coverage: 56.678% (-16.6%) from 73.277%
23390157208

Pull #12252

github

web-flow
Merge 5b60ba41c into 7038fa166
Pull Request #12252: fix: unskip cascade soft remove test

17767 of 26580 branches covered (66.84%)

Branch coverage included in aggregate %.

64033 of 117744 relevant lines covered (54.38%)

1514.83 hits per line

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

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

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

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

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

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

28✔
45
    constructor() {
28✔
46
        super()
6✔
47
    }
6✔
48

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

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

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

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

60✔
81
        if (
60✔
82
            this.isTransactionActive &&
60!
83
            this.driver.transactionSupport === "simple"
×
84
        )
60✔
85
            throw new TransactionAlreadyStartedError()
60!
86

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

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

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

60✔
118
        await this.broadcaster.broadcast("AfterTransactionStart")
60✔
119
    }
60✔
120

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

60✔
128
        await this.broadcaster.broadcast("BeforeTransactionCommit")
60✔
129

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

60✔
140
        await this.broadcaster.broadcast("AfterTransactionCommit")
60✔
141
    }
60✔
142

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

×
150
        await this.broadcaster.broadcast("BeforeTransactionRollback")
×
151

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

36✔
315
        if (ifNotExists) {
36!
316
            const isTableExist = await this.hasTable(table)
×
317
            if (isTableExist) return Promise.resolve()
×
318
        }
×
319

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

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

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

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

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

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

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

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

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

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

×
396
        upQueries.push(this.dropTableSql(table, ifExists))
×
397
        downQueries.push(this.createTableSql(table, createForeignKeys))
×
398

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

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

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

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

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

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

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

×
453
        await this.executeQueries(
×
454
            [
×
455
                this.deleteViewDefinitionSql(view),
×
456
                this.dropViewSql(view, ifExists),
×
457
            ],
×
458
            [this.insertViewDefinitionSql(view), this.createViewSql(view)],
×
459
        )
×
460
    }
×
461

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

×
476
        newTable.name = newTableName
×
477

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

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

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

×
502
            unique.name = this.connection.namingStrategy.uniqueConstraintName(
×
503
                newTable,
×
504
                unique.columnNames,
×
505
            )
×
506
        })
×
507

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

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

×
521
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
×
522
                newTable,
×
523
                foreignKey.columnNames,
×
524
                this.getTablePath(foreignKey),
×
525
                foreignKey.referencedColumnNames,
×
526
            )
×
527
        })
×
528

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

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

×
540
            index.name = this.connection.namingStrategy.indexName(
×
541
                newTable,
×
542
                index.columnNames,
×
543
                index.where,
×
544
            )
×
545
        })
×
546

×
547
        // rename old table;
×
548
        oldTable.name = newTable.name
×
549

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

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

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

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

×
608
        let newColumn: TableColumn
×
609
        if (InstanceChecker.isTableColumn(newTableColumnOrName)) {
×
610
            newColumn = newTableColumnOrName
×
611
        } else {
×
612
            newColumn = oldColumn.clone()
×
613
            newColumn.name = newTableColumnOrName
×
614
        }
×
615

×
616
        return this.changeColumn(table, oldColumn, newColumn)
×
617
    }
×
618

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

×
641
        await this.changeColumns(table, [{ oldColumn, newColumn }])
×
642
    }
×
643

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

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

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

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

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

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

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

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

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

×
761
        await this.recreateTable(changedTable, table)
×
762
    }
×
763

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

×
788
        await this.dropColumns(table, [column])
×
789
    }
×
790

28✔
791
    /**
28✔
792
     * Drops the columns in the table.
28✔
793
     * @param tableOrName
28✔
794
     * @param columns
28✔
795
     * @param ifExists
28✔
796
     */
28✔
797
    async dropColumns(
28✔
798
        tableOrName: Table | string,
×
799
        columns: TableColumn[] | string[],
×
800
        ifExists?: boolean,
×
801
    ): Promise<void> {
×
802
        const table = InstanceChecker.isTable(tableOrName)
×
803
            ? tableOrName
×
804
            : await this.getCachedTable(tableOrName)
×
805

×
806
        // clone original table and remove column and its constraints from cloned table
×
807
        const changedTable = table.clone()
×
808
        columns.forEach((column: TableColumn | string) => {
×
809
            const columnInstance = InstanceChecker.isTableColumn(column)
×
810
                ? column
×
811
                : table.findColumnByName(column)
×
812
            if (!columnInstance) {
×
813
                if (ifExists) return
×
814
                throw new Error(
×
815
                    `Column "${column}" was not found in table "${table.name}"`,
×
816
                )
×
817
            }
×
818

×
819
            changedTable.removeColumn(columnInstance)
×
820
            changedTable
×
821
                .findColumnUniques(columnInstance)
×
822
                .forEach((unique) =>
×
823
                    changedTable.removeUniqueConstraint(unique),
×
824
                )
×
825
            changedTable
×
826
                .findColumnIndices(columnInstance)
×
827
                .forEach((index) => changedTable.removeIndex(index))
×
828
            changedTable
×
829
                .findColumnForeignKeys(columnInstance)
×
830
                .forEach((fk) => changedTable.removeForeignKey(fk))
×
831
        })
×
832

×
833
        await this.recreateTable(changedTable, table)
×
834
    }
×
835

28✔
836
    /**
28✔
837
     * Creates a new primary key.
28✔
838
     * @param tableOrName
28✔
839
     * @param columnNames
28✔
840
     */
28✔
841
    async createPrimaryKey(
28✔
842
        tableOrName: Table | string,
×
843
        columnNames: string[],
×
844
    ): Promise<void> {
×
845
        const table = InstanceChecker.isTable(tableOrName)
×
846
            ? tableOrName
×
847
            : await this.getCachedTable(tableOrName)
×
848
        // clone original table and mark columns as primary
×
849
        const changedTable = table.clone()
×
850
        changedTable.columns.forEach((column) => {
×
851
            if (columnNames.find((columnName) => columnName === column.name))
×
852
                column.isPrimary = true
×
853
        })
×
854

×
855
        await this.recreateTable(changedTable, table)
×
856
        // mark columns as primary in original table
×
857
        table.columns.forEach((column) => {
×
858
            if (columnNames.find((columnName) => columnName === column.name))
×
859
                column.isPrimary = true
×
860
        })
×
861
    }
×
862

28✔
863
    /**
28✔
864
     * Updates composite primary keys.
28✔
865
     * @param tableOrName
28✔
866
     * @param columns
28✔
867
     */
28✔
868
    async updatePrimaryKeys(
28✔
869
        tableOrName: Table | string,
×
870
        columns: TableColumn[],
×
871
    ): Promise<void> {
×
872
        await Promise.resolve()
×
873
    }
×
874

28✔
875
    /**
28✔
876
     * Drops a primary key.
28✔
877
     * @param tableOrName
28✔
878
     * @param constraintName
28✔
879
     * @param ifExists
28✔
880
     */
28✔
881
    async dropPrimaryKey(
28✔
882
        tableOrName: Table | string,
×
883
        constraintName?: string,
×
884
        ifExists?: boolean,
×
885
    ): Promise<void> {
×
886
        const table = InstanceChecker.isTable(tableOrName)
×
887
            ? tableOrName
×
888
            : await this.getCachedTable(tableOrName)
×
889

×
890
        if (ifExists && table.primaryColumns.length === 0) return
×
891

×
892
        // clone original table and mark primary columns as non-primary
×
893
        const changedTable = table.clone()
×
894
        changedTable.primaryColumns.forEach((column) => {
×
895
            column.isPrimary = false
×
896
        })
×
897

×
898
        await this.recreateTable(changedTable, table)
×
899
        // mark primary columns as non-primary in original table
×
900
        table.primaryColumns.forEach((column) => {
×
901
            column.isPrimary = false
×
902
        })
×
903
    }
×
904

28✔
905
    /**
28✔
906
     * Creates a new unique constraint.
28✔
907
     * @param tableOrName
28✔
908
     * @param uniqueConstraint
28✔
909
     */
28✔
910
    async createUniqueConstraint(
28✔
911
        tableOrName: Table | string,
×
912
        uniqueConstraint: TableUnique,
×
913
    ): Promise<void> {
×
914
        await this.createUniqueConstraints(tableOrName, [uniqueConstraint])
×
915
    }
×
916

28✔
917
    /**
28✔
918
     * Creates a new unique constraints.
28✔
919
     * @param tableOrName
28✔
920
     * @param uniqueConstraints
28✔
921
     */
28✔
922
    async createUniqueConstraints(
28✔
923
        tableOrName: Table | string,
×
924
        uniqueConstraints: TableUnique[],
×
925
    ): Promise<void> {
×
926
        const table = InstanceChecker.isTable(tableOrName)
×
927
            ? tableOrName
×
928
            : await this.getCachedTable(tableOrName)
×
929

×
930
        // clone original table and add unique constraints in to cloned table
×
931
        const changedTable = table.clone()
×
932
        uniqueConstraints.forEach((uniqueConstraint) =>
×
933
            changedTable.addUniqueConstraint(uniqueConstraint),
×
934
        )
×
935
        await this.recreateTable(changedTable, table)
×
936
    }
×
937

28✔
938
    /**
28✔
939
     * Drops a unique constraint.
28✔
940
     * @param tableOrName
28✔
941
     * @param uniqueOrName
28✔
942
     * @param ifExists
28✔
943
     */
28✔
944
    async dropUniqueConstraint(
28✔
945
        tableOrName: Table | string,
×
946
        uniqueOrName: TableUnique | string,
×
947
        ifExists?: boolean,
×
948
    ): Promise<void> {
×
949
        const table = InstanceChecker.isTable(tableOrName)
×
950
            ? tableOrName
×
951
            : await this.getCachedTable(tableOrName)
×
952
        const uniqueConstraint = InstanceChecker.isTableUnique(uniqueOrName)
×
953
            ? uniqueOrName
×
954
            : table.uniques.find((u) => u.name === uniqueOrName)
×
955
        if (!uniqueConstraint) {
×
956
            if (ifExists) return
×
957
            throw new TypeORMError(
×
958
                `Supplied unique constraint was not found in table ${table.name}`,
×
959
            )
×
960
        }
×
961

×
962
        await this.dropUniqueConstraints(table, [uniqueConstraint])
×
963
    }
×
964

28✔
965
    /**
28✔
966
     * Drops unique constraints.
28✔
967
     * @param tableOrName
28✔
968
     * @param uniqueConstraints
28✔
969
     * @param ifExists
28✔
970
     */
28✔
971
    async dropUniqueConstraints(
28✔
972
        tableOrName: Table | string,
×
973
        uniqueConstraints: TableUnique[],
×
974
        ifExists?: boolean,
×
975
    ): Promise<void> {
×
976
        const table = InstanceChecker.isTable(tableOrName)
×
977
            ? tableOrName
×
978
            : await this.getCachedTable(tableOrName)
×
979

×
980
        // clone original table and remove unique constraints from cloned table
×
981
        const changedTable = table.clone()
×
982
        uniqueConstraints.forEach((uniqueConstraint) =>
×
983
            changedTable.removeUniqueConstraint(uniqueConstraint),
×
984
        )
×
985

×
986
        await this.recreateTable(changedTable, table)
×
987
    }
×
988

28✔
989
    /**
28✔
990
     * Creates new check constraint.
28✔
991
     * @param tableOrName
28✔
992
     * @param checkConstraint
28✔
993
     */
28✔
994
    async createCheckConstraint(
28✔
995
        tableOrName: Table | string,
×
996
        checkConstraint: TableCheck,
×
997
    ): Promise<void> {
×
998
        await this.createCheckConstraints(tableOrName, [checkConstraint])
×
999
    }
×
1000

28✔
1001
    /**
28✔
1002
     * Creates new check constraints.
28✔
1003
     * @param tableOrName
28✔
1004
     * @param checkConstraints
28✔
1005
     */
28✔
1006
    async createCheckConstraints(
28✔
1007
        tableOrName: Table | string,
×
1008
        checkConstraints: TableCheck[],
×
1009
    ): Promise<void> {
×
1010
        const table = InstanceChecker.isTable(tableOrName)
×
1011
            ? tableOrName
×
1012
            : await this.getCachedTable(tableOrName)
×
1013

×
1014
        // clone original table and add check constraints in to cloned table
×
1015
        const changedTable = table.clone()
×
1016
        checkConstraints.forEach((checkConstraint) =>
×
1017
            changedTable.addCheckConstraint(checkConstraint),
×
1018
        )
×
1019
        await this.recreateTable(changedTable, table)
×
1020
    }
×
1021

28✔
1022
    /**
28✔
1023
     * Drops check constraint.
28✔
1024
     * @param tableOrName
28✔
1025
     * @param checkOrName
28✔
1026
     * @param ifExists
28✔
1027
     */
28✔
1028
    async dropCheckConstraint(
28✔
1029
        tableOrName: Table | string,
×
1030
        checkOrName: TableCheck | string,
×
1031
        ifExists?: boolean,
×
1032
    ): Promise<void> {
×
1033
        const table = InstanceChecker.isTable(tableOrName)
×
1034
            ? tableOrName
×
1035
            : await this.getCachedTable(tableOrName)
×
1036
        const checkConstraint = InstanceChecker.isTableCheck(checkOrName)
×
1037
            ? checkOrName
×
1038
            : table.checks.find((c) => c.name === checkOrName)
×
1039
        if (!checkConstraint) {
×
1040
            if (ifExists) return
×
1041
            throw new TypeORMError(
×
1042
                `Supplied check constraint was not found in table ${table.name}`,
×
1043
            )
×
1044
        }
×
1045

×
1046
        await this.dropCheckConstraints(table, [checkConstraint])
×
1047
    }
×
1048

28✔
1049
    /**
28✔
1050
     * Drops check constraints.
28✔
1051
     * @param tableOrName
28✔
1052
     * @param checkConstraints
28✔
1053
     * @param ifExists
28✔
1054
     */
28✔
1055
    async dropCheckConstraints(
28✔
1056
        tableOrName: Table | string,
×
1057
        checkConstraints: TableCheck[],
×
1058
        ifExists?: boolean,
×
1059
    ): Promise<void> {
×
1060
        const table = InstanceChecker.isTable(tableOrName)
×
1061
            ? tableOrName
×
1062
            : await this.getCachedTable(tableOrName)
×
1063

×
1064
        // clone original table and remove check constraints from cloned table
×
1065
        const changedTable = table.clone()
×
1066
        checkConstraints.forEach((checkConstraint) =>
×
1067
            changedTable.removeCheckConstraint(checkConstraint),
×
1068
        )
×
1069

×
1070
        await this.recreateTable(changedTable, table)
×
1071
    }
×
1072

28✔
1073
    /**
28✔
1074
     * Creates a new exclusion constraint.
28✔
1075
     * @param tableOrName
28✔
1076
     * @param exclusionConstraint
28✔
1077
     */
28✔
1078
    async createExclusionConstraint(
28✔
1079
        tableOrName: Table | string,
×
1080
        exclusionConstraint: TableExclusion,
×
1081
    ): Promise<void> {
×
1082
        throw new TypeORMError(`Sqlite does not support exclusion constraints.`)
×
1083
    }
×
1084

28✔
1085
    /**
28✔
1086
     * Creates a new exclusion constraints.
28✔
1087
     * @param tableOrName
28✔
1088
     * @param exclusionConstraints
28✔
1089
     */
28✔
1090
    async createExclusionConstraints(
28✔
1091
        tableOrName: Table | string,
×
1092
        exclusionConstraints: TableExclusion[],
×
1093
    ): Promise<void> {
×
1094
        throw new TypeORMError(`Sqlite does not support exclusion constraints.`)
×
1095
    }
×
1096

28✔
1097
    /**
28✔
1098
     * Drops exclusion constraint.
28✔
1099
     * @param tableOrName
28✔
1100
     * @param exclusionOrName
28✔
1101
     * @param ifExists
28✔
1102
     */
28✔
1103
    async dropExclusionConstraint(
28✔
1104
        tableOrName: Table | string,
×
1105
        exclusionOrName: TableExclusion | string,
×
1106
        ifExists?: boolean,
×
1107
    ): Promise<void> {
×
1108
        throw new TypeORMError(`Sqlite does not support exclusion constraints.`)
×
1109
    }
×
1110

28✔
1111
    /**
28✔
1112
     * Drops exclusion constraints.
28✔
1113
     * @param tableOrName
28✔
1114
     * @param exclusionConstraints
28✔
1115
     * @param ifExists
28✔
1116
     */
28✔
1117
    async dropExclusionConstraints(
28✔
1118
        tableOrName: Table | string,
×
1119
        exclusionConstraints: TableExclusion[],
×
1120
        ifExists?: boolean,
×
1121
    ): Promise<void> {
×
1122
        throw new TypeORMError(`Sqlite does not support exclusion constraints.`)
×
1123
    }
×
1124

28✔
1125
    /**
28✔
1126
     * Creates a new foreign key.
28✔
1127
     * @param tableOrName
28✔
1128
     * @param foreignKey
28✔
1129
     */
28✔
1130
    async createForeignKey(
28✔
1131
        tableOrName: Table | string,
×
1132
        foreignKey: TableForeignKey,
×
1133
    ): Promise<void> {
×
1134
        await this.createForeignKeys(tableOrName, [foreignKey])
×
1135
    }
×
1136

28✔
1137
    /**
28✔
1138
     * Creates a new foreign keys.
28✔
1139
     * @param tableOrName
28✔
1140
     * @param foreignKeys
28✔
1141
     */
28✔
1142
    async createForeignKeys(
28✔
1143
        tableOrName: Table | string,
24✔
1144
        foreignKeys: TableForeignKey[],
24✔
1145
    ): Promise<void> {
24✔
1146
        const table = InstanceChecker.isTable(tableOrName)
24✔
1147
            ? tableOrName
24✔
1148
            : await this.getCachedTable(tableOrName)
24!
1149
        // clone original table and add foreign keys in to cloned table
×
1150
        const changedTable = table.clone()
×
1151
        foreignKeys.forEach((foreignKey) =>
✔
1152
            changedTable.addForeignKey(foreignKey),
×
1153
        )
×
1154

×
1155
        await this.recreateTable(changedTable, table)
×
1156
    }
24✔
1157

28✔
1158
    /**
28✔
1159
     * Drops a foreign key from the table.
28✔
1160
     * @param tableOrName
28✔
1161
     * @param foreignKeyOrName
28✔
1162
     * @param ifExists
28✔
1163
     */
28✔
1164
    async dropForeignKey(
28✔
1165
        tableOrName: Table | string,
×
1166
        foreignKeyOrName: TableForeignKey | string,
×
1167
        ifExists?: boolean,
×
1168
    ): Promise<void> {
×
1169
        const table = InstanceChecker.isTable(tableOrName)
×
1170
            ? tableOrName
×
1171
            : await this.getCachedTable(tableOrName)
×
1172
        const foreignKey = InstanceChecker.isTableForeignKey(foreignKeyOrName)
×
1173
            ? foreignKeyOrName
×
1174
            : table.foreignKeys.find((fk) => fk.name === foreignKeyOrName)
×
1175
        if (!foreignKey) {
×
1176
            if (ifExists) return
×
1177
            throw new TypeORMError(
×
1178
                `Supplied foreign key was not found in table ${table.name}`,
×
1179
            )
×
1180
        }
×
1181

×
1182
        await this.dropForeignKeys(tableOrName, [foreignKey])
×
1183
    }
×
1184

28✔
1185
    /**
28✔
1186
     * Drops a foreign keys from the table.
28✔
1187
     * @param tableOrName
28✔
1188
     * @param foreignKeys
28✔
1189
     * @param ifExists
28✔
1190
     */
28✔
1191
    async dropForeignKeys(
28✔
1192
        tableOrName: Table | string,
×
1193
        foreignKeys: TableForeignKey[],
×
1194
        ifExists?: boolean,
×
1195
    ): Promise<void> {
×
1196
        const table = InstanceChecker.isTable(tableOrName)
×
1197
            ? tableOrName
×
1198
            : await this.getCachedTable(tableOrName)
×
1199

×
1200
        // clone original table and remove foreign keys from cloned table
×
1201
        const changedTable = table.clone()
×
1202
        foreignKeys.forEach((foreignKey) =>
×
1203
            changedTable.removeForeignKey(foreignKey),
×
1204
        )
×
1205

×
1206
        await this.recreateTable(changedTable, table)
×
1207
    }
×
1208

28✔
1209
    /**
28✔
1210
     * Creates a new index.
28✔
1211
     * @param tableOrName
28✔
1212
     * @param index
28✔
1213
     */
28✔
1214
    async createIndex(
28✔
1215
        tableOrName: Table | string,
×
1216
        index: TableIndex,
×
1217
    ): Promise<void> {
×
1218
        const table = InstanceChecker.isTable(tableOrName)
×
1219
            ? tableOrName
×
1220
            : await this.getCachedTable(tableOrName)
×
1221

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

×
1225
        const up = this.createIndexSql(table, index)
×
1226
        const down = this.dropIndexSql(index)
×
1227
        await this.executeQueries(up, down)
×
1228
        table.addIndex(index)
×
1229
    }
×
1230

28✔
1231
    /**
28✔
1232
     * Creates a new indices
28✔
1233
     * @param tableOrName
28✔
1234
     * @param indices
28✔
1235
     */
28✔
1236
    async createIndices(
28✔
1237
        tableOrName: Table | string,
×
1238
        indices: TableIndex[],
×
1239
    ): Promise<void> {
×
1240
        const promises = indices.map((index) =>
×
1241
            this.createIndex(tableOrName, index),
×
1242
        )
×
1243
        await Promise.all(promises)
×
1244
    }
×
1245

28✔
1246
    /**
28✔
1247
     * Drops an index from the table.
28✔
1248
     * @param tableOrName
28✔
1249
     * @param indexOrName
28✔
1250
     * @param ifExists
28✔
1251
     */
28✔
1252
    async dropIndex(
28✔
1253
        tableOrName: Table | string,
×
1254
        indexOrName: TableIndex | string,
×
1255
        ifExists?: boolean,
×
1256
    ): Promise<void> {
×
1257
        const table = InstanceChecker.isTable(tableOrName)
×
1258
            ? tableOrName
×
1259
            : await this.getCachedTable(tableOrName)
×
1260
        const index = InstanceChecker.isTableIndex(indexOrName)
×
1261
            ? indexOrName
×
1262
            : table.indices.find((i) => i.name === indexOrName)
×
1263
        if (!index) {
×
1264
            if (ifExists) return
×
1265
            throw new TypeORMError(
×
1266
                `Supplied index ${indexOrName} was not found in table ${table.name}`,
×
1267
            )
×
1268
        }
×
1269

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

×
1273
        const up = this.dropIndexSql(index, ifExists)
×
1274
        const down = this.createIndexSql(table, index)
×
1275
        await this.executeQueries(up, down)
×
1276
        table.removeIndex(index)
×
1277
    }
×
1278

28✔
1279
    /**
28✔
1280
     * Drops an indices from the table.
28✔
1281
     * @param tableOrName
28✔
1282
     * @param indices
28✔
1283
     * @param ifExists
28✔
1284
     */
28✔
1285
    async dropIndices(
28✔
1286
        tableOrName: Table | string,
×
1287
        indices: TableIndex[],
×
1288
        ifExists?: boolean,
×
1289
    ): Promise<void> {
×
1290
        const promises = indices.map((index) =>
×
1291
            this.dropIndex(tableOrName, index, ifExists),
×
1292
        )
×
1293
        await Promise.all(promises)
×
1294
    }
×
1295

28✔
1296
    /**
28✔
1297
     * Clears all table contents.
28✔
1298
     * Note: this operation uses SQL's TRUNCATE query which cannot be reverted in transactions.
28✔
1299
     * @param tableName
28✔
1300
     * @param options
28✔
1301
     * @param options.cascade
28✔
1302
     */
28✔
1303
    async clearTable(
28✔
1304
        tableName: string,
×
1305
        options?: { cascade?: boolean },
×
1306
    ): Promise<void> {
×
1307
        if (options?.cascade) {
×
1308
            throw new TypeORMError(
×
1309
                `SQLite does not support clearing table with cascade option`,
×
1310
            )
×
1311
        }
×
1312
        await this.query(`DELETE FROM ${this.escapePath(tableName)}`)
×
1313
    }
×
1314

28✔
1315
    /**
28✔
1316
     * Removes all tables from the currently connected database.
28✔
1317
     * @param database
28✔
1318
     */
28✔
1319
    async clearDatabase(database?: string): Promise<void> {
28✔
1320
        let dbPath: string | undefined = undefined
12✔
1321
        if (
12✔
1322
            database &&
12!
1323
            this.driver.getAttachedDatabaseHandleByRelativePath(database)
6✔
1324
        ) {
12!
1325
            dbPath =
×
1326
                this.driver.getAttachedDatabaseHandleByRelativePath(database)
×
1327
        }
×
1328

12✔
1329
        await this.query(`PRAGMA foreign_keys = OFF`)
12✔
1330

12✔
1331
        const isAnotherTransactionActive = this.isTransactionActive
12✔
1332
        if (!isAnotherTransactionActive) await this.startTransaction()
12✔
1333
        try {
12✔
1334
            const selectViewDropsQuery = dbPath
12✔
1335
                ? `SELECT 'DROP VIEW "${dbPath}"."' || name || '";' as query FROM "${dbPath}"."sqlite_master" WHERE "type" = 'view'`
12!
1336
                : `SELECT 'DROP VIEW "' || name || '";' as query FROM "sqlite_master" WHERE "type" = 'view'`
12✔
1337
            const dropViewQueries: ObjectLiteral[] =
12✔
1338
                await this.query(selectViewDropsQuery)
12✔
1339
            await Promise.all(
12✔
1340
                dropViewQueries.map((q) => this.query(q["query"])),
12✔
1341
            )
12✔
1342

12✔
1343
            const selectTableDropsQuery = dbPath
12✔
1344
                ? `SELECT 'DROP TABLE "${dbPath}"."' || name || '";' as query FROM "${dbPath}"."sqlite_master" WHERE "type" = 'table' AND "name" != 'sqlite_sequence'`
12!
1345
                : `SELECT 'DROP TABLE "' || name || '";' as query FROM "sqlite_master" WHERE "type" = 'table' AND "name" != 'sqlite_sequence'`
12✔
1346
            const dropTableQueries: ObjectLiteral[] = await this.query(
12✔
1347
                selectTableDropsQuery,
12✔
1348
            )
12✔
1349
            await Promise.all(
12✔
1350
                dropTableQueries.map((q) => this.query(q["query"])),
12✔
1351
            )
12✔
1352

12✔
1353
            if (!isAnotherTransactionActive) await this.commitTransaction()
12✔
1354
        } catch (error) {
12!
1355
            try {
×
1356
                // we throw original error even if rollback thrown an error
×
1357
                if (!isAnotherTransactionActive)
×
1358
                    await this.rollbackTransaction()
×
1359
            } catch (rollbackError) {}
×
1360
            throw error
×
1361
        } finally {
12✔
1362
            await this.query(`PRAGMA foreign_keys = ON`)
12✔
1363
        }
12✔
1364
    }
12✔
1365

28✔
1366
    // -------------------------------------------------------------------------
28✔
1367
    // Protected Methods
28✔
1368
    // -------------------------------------------------------------------------
28✔
1369

28✔
1370
    protected async loadViews(viewNames?: string[]): Promise<View[]> {
28✔
1371
        const hasTable = await this.hasTable(this.getTypeormMetadataTableName())
12✔
1372
        if (!hasTable) {
12✔
1373
            return []
12✔
1374
        }
12✔
1375

×
1376
        if (!viewNames) {
×
1377
            viewNames = []
×
1378
        }
×
1379

×
1380
        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" = '${
×
1381
            MetadataTableType.VIEW
×
1382
        }'`
×
1383
        const parameters: string[] = []
×
1384
        if (viewNames.length > 0) {
×
1385
            const placeholders = viewNames.map(() => "?").join(", ")
×
1386
            query += ` AND "t"."name" IN (${placeholders})`
×
1387
            parameters.push(...viewNames)
×
1388
        }
×
1389
        const dbViews = await this.query(query, parameters)
×
1390
        return dbViews.map((dbView: any) => {
×
1391
            const view = new View()
×
1392
            view.name = dbView["name"]
×
1393
            view.expression = dbView["value"]
×
1394
            return view
×
1395
        })
×
1396
    }
×
1397

28✔
1398
    protected async loadTableRecords(
28✔
1399
        tablePath: string,
×
1400
        tableOrIndex: "table" | "index",
×
1401
    ) {
×
1402
        let database: string | undefined = undefined
×
1403
        const [schema, tableName] = this.splitTablePath(tablePath)
×
1404
        if (
×
1405
            schema &&
×
1406
            this.driver.getAttachedDatabasePathRelativeByHandle(schema)
×
1407
        ) {
×
1408
            database =
×
1409
                this.driver.getAttachedDatabasePathRelativeByHandle(schema)
×
1410
        }
×
1411
        return this.query(
×
1412
            `SELECT ${database ? `'${database}'` : null} as database, ${
×
1413
                schema ? `'${schema}'` : null
×
1414
            } as schema, * FROM ${
×
1415
                schema ? `"${schema}".` : ""
×
1416
            }${this.escapePath(
×
1417
                `sqlite_master`,
×
1418
            )} WHERE "type" = '${tableOrIndex}' AND "${
×
1419
                tableOrIndex === "table" ? "name" : "tbl_name"
×
1420
            }" IN ('${tableName}')`,
×
1421
        )
×
1422
    }
×
1423

28✔
1424
    protected async loadPragmaRecords(tablePath: string, pragma: string) {
28✔
1425
        const [, tableName] = this.splitTablePath(tablePath)
×
1426
        return this.query(`PRAGMA ${pragma}("${tableName}")`)
×
1427
    }
×
1428

28✔
1429
    /**
28✔
1430
     * Loads all tables (with given names) from the database and creates a Table from them.
28✔
1431
     * @param tableNames
28✔
1432
     */
28✔
1433
    protected async loadTables(tableNames?: string[]): Promise<Table[]> {
28✔
1434
        // if no tables given then no need to proceed
12✔
1435
        if (tableNames && tableNames.length === 0) {
12!
1436
            return []
×
1437
        }
×
1438

12✔
1439
        let dbTables: { database?: string; name: string; sql: string }[] = []
12✔
1440
        let dbIndicesDef: ObjectLiteral[]
12✔
1441

12✔
1442
        if (!tableNames) {
12!
1443
            const tablesSql = `SELECT * FROM "sqlite_master" WHERE "type" = 'table'`
×
1444
            dbTables.push(...(await this.query(tablesSql)))
×
1445

×
1446
            const tableNamesString = dbTables
×
1447
                .map(({ name }) => `'${name}'`)
×
1448
                .join(", ")
×
1449
            dbIndicesDef = await this.query(
×
1450
                `SELECT * FROM "sqlite_master" WHERE "type" = 'index' AND "tbl_name" IN (${tableNamesString})`,
×
1451
            )
×
1452
        } else {
12✔
1453
            const tableNamesWithoutDot = tableNames
12✔
1454
                .filter((tableName) => {
12✔
1455
                    return tableName.split(".").length === 1
36✔
1456
                })
12✔
1457
                .map((tableName) => `'${tableName}'`)
12✔
1458

12✔
1459
            const tableNamesWithDot = tableNames.filter((tableName) => {
12✔
1460
                return tableName.split(".").length > 1
36✔
1461
            })
12✔
1462

12✔
1463
            const queryPromises = (type: "table" | "index") => {
12✔
1464
                const promises = [
24✔
1465
                    ...tableNamesWithDot.map((tableName) =>
24✔
1466
                        this.loadTableRecords(tableName, type),
24✔
1467
                    ),
24✔
1468
                ]
24✔
1469

24✔
1470
                if (tableNamesWithoutDot.length) {
24✔
1471
                    promises.push(
24✔
1472
                        this.query(
24✔
1473
                            `SELECT * FROM "sqlite_master" WHERE "type" = '${type}' AND "${
24✔
1474
                                type === "table" ? "name" : "tbl_name"
24✔
1475
                            }" IN (${tableNamesWithoutDot})`,
24✔
1476
                        ),
24✔
1477
                    )
24✔
1478
                }
24✔
1479

24✔
1480
                return promises
24✔
1481
            }
24✔
1482
            dbTables = (await Promise.all(queryPromises("table")))
12✔
1483
                .reduce((acc, res) => [...acc, ...res], [])
12✔
1484
                .filter(Boolean)
12✔
1485
            dbIndicesDef = (await Promise.all(queryPromises("index")))
12✔
1486
                .reduce((acc, res) => [...acc, ...res], [])
12✔
1487
                .filter(Boolean)
12✔
1488
        }
12✔
1489

12✔
1490
        // if tables were not found in the db, no need to proceed
12✔
1491
        if (dbTables.length === 0) {
12✔
1492
            return []
12✔
1493
        }
12✔
1494

×
1495
        // create table schemas for loaded tables
×
1496
        return Promise.all(
×
1497
            dbTables.map(async (dbTable) => {
×
1498
                const tablePath =
×
1499
                    dbTable["database"] &&
×
1500
                    this.driver.getAttachedDatabaseHandleByRelativePath(
×
1501
                        dbTable["database"],
×
1502
                    )
×
1503
                        ? `${this.driver.getAttachedDatabaseHandleByRelativePath(
×
1504
                              dbTable["database"],
×
1505
                          )}.${dbTable["name"]}`
×
1506
                        : dbTable["name"]
×
1507

×
1508
                const sql = dbTable["sql"]
×
1509

×
1510
                const withoutRowid = sql.includes("WITHOUT ROWID")
×
1511
                const table = new Table({ name: tablePath, withoutRowid })
×
1512

×
1513
                // load columns and indices
×
1514
                const [dbColumns, dbIndices, dbForeignKeys]: ObjectLiteral[][] =
×
1515
                    await Promise.all([
×
1516
                        this.loadPragmaRecords(tablePath, `table_xinfo`),
×
1517
                        this.loadPragmaRecords(tablePath, `index_list`),
×
1518
                        this.loadPragmaRecords(tablePath, `foreign_key_list`),
×
1519
                    ])
×
1520

×
1521
                // find column name with auto increment
×
1522
                let autoIncrementColumnName: string | undefined = undefined
×
1523
                const tableSql: string = dbTable["sql"]
×
1524
                const autoIncrementIndex = tableSql
×
1525
                    .toUpperCase()
×
1526
                    .indexOf("AUTOINCREMENT")
×
1527
                if (autoIncrementIndex !== -1) {
×
1528
                    autoIncrementColumnName = tableSql.substr(
×
1529
                        0,
×
1530
                        autoIncrementIndex,
×
1531
                    )
×
1532
                    const comma = autoIncrementColumnName.lastIndexOf(",")
×
1533
                    const bracket = autoIncrementColumnName.lastIndexOf("(")
×
1534
                    if (comma !== -1) {
×
1535
                        autoIncrementColumnName =
×
1536
                            autoIncrementColumnName.substr(comma)
×
1537
                        autoIncrementColumnName =
×
1538
                            autoIncrementColumnName.substr(
×
1539
                                0,
×
1540
                                autoIncrementColumnName.lastIndexOf('"'),
×
1541
                            )
×
1542
                        autoIncrementColumnName =
×
1543
                            autoIncrementColumnName.substr(
×
1544
                                autoIncrementColumnName.indexOf('"') + 1,
×
1545
                            )
×
1546
                    } else if (bracket !== -1) {
×
1547
                        autoIncrementColumnName =
×
1548
                            autoIncrementColumnName.substr(bracket)
×
1549
                        autoIncrementColumnName =
×
1550
                            autoIncrementColumnName.substr(
×
1551
                                0,
×
1552
                                autoIncrementColumnName.lastIndexOf('"'),
×
1553
                            )
×
1554
                        autoIncrementColumnName =
×
1555
                            autoIncrementColumnName.substr(
×
1556
                                autoIncrementColumnName.indexOf('"') + 1,
×
1557
                            )
×
1558
                    }
×
1559
                }
×
1560

×
1561
                // create columns from the loaded columns
×
1562
                table.columns = await Promise.all(
×
1563
                    dbColumns.map(async (dbColumn) => {
×
1564
                        const tableColumn = new TableColumn()
×
1565
                        tableColumn.name = dbColumn["name"]
×
1566
                        tableColumn.type = dbColumn["type"].toLowerCase()
×
1567
                        tableColumn.default =
×
1568
                            dbColumn["dflt_value"] !== null &&
×
1569
                            dbColumn["dflt_value"] !== undefined
×
1570
                                ? dbColumn["dflt_value"]
×
1571
                                : undefined
×
1572
                        tableColumn.isNullable = dbColumn["notnull"] === 0
×
1573
                        // primary keys are numbered starting with 1, columns that aren't primary keys are marked with 0
×
1574
                        tableColumn.isPrimary = dbColumn["pk"] > 0
×
1575
                        tableColumn.comment = "" // SQLite does not support column comments
×
1576
                        tableColumn.isGenerated =
×
1577
                            autoIncrementColumnName === dbColumn["name"]
×
1578
                        if (tableColumn.isGenerated) {
×
1579
                            tableColumn.generationStrategy = "increment"
×
1580
                        }
×
1581

×
1582
                        if (
×
1583
                            dbColumn["hidden"] === 2 ||
×
1584
                            dbColumn["hidden"] === 3
×
1585
                        ) {
×
1586
                            tableColumn.generatedType =
×
1587
                                dbColumn["hidden"] === 2 ? "VIRTUAL" : "STORED"
×
1588

×
1589
                            const asExpressionQuery =
×
1590
                                this.selectTypeormMetadataSql({
×
1591
                                    table: table.name,
×
1592
                                    type: MetadataTableType.GENERATED_COLUMN,
×
1593
                                    name: tableColumn.name,
×
1594
                                })
×
1595

×
1596
                            const results = await this.query(
×
1597
                                asExpressionQuery.query,
×
1598
                                asExpressionQuery.parameters,
×
1599
                            )
×
1600
                            if (results[0] && results[0].value) {
×
1601
                                tableColumn.asExpression = results[0].value
×
1602
                            } else {
×
1603
                                tableColumn.asExpression = ""
×
1604
                            }
×
1605
                        }
×
1606

×
1607
                        if (tableColumn.type === "varchar") {
×
1608
                            tableColumn.enum = OrmUtils.parseSqlCheckExpression(
×
1609
                                sql,
×
1610
                                tableColumn.name,
×
1611
                            )
×
1612
                        }
×
1613

×
1614
                        // parse datatype and attempt to retrieve length, precision and scale
×
1615
                        const pos = tableColumn.type.indexOf("(")
×
1616
                        if (pos !== -1) {
×
1617
                            const fullType = tableColumn.type
×
1618
                            const dataType = fullType.substr(0, pos)
×
1619
                            if (
×
1620
                                this.driver.withLengthColumnTypes.find(
×
1621
                                    (col) => col === dataType,
×
1622
                                )
×
1623
                            ) {
×
1624
                                const len = parseInt(
×
1625
                                    fullType.substring(
×
1626
                                        pos + 1,
×
1627
                                        fullType.length - 1,
×
1628
                                    ),
×
1629
                                )
×
1630
                                if (len) {
×
1631
                                    tableColumn.length = len.toString()
×
1632
                                    tableColumn.type = dataType // remove the length part from the datatype
×
1633
                                }
×
1634
                            }
×
1635
                            if (
×
1636
                                this.driver.withPrecisionColumnTypes.find(
×
1637
                                    (col) => col === dataType,
×
1638
                                )
×
1639
                            ) {
×
1640
                                const re = new RegExp(
×
1641
                                    `^${dataType}\\((\\d+),?\\s?(\\d+)?\\)`,
×
1642
                                )
×
1643
                                const matches = fullType.match(re)
×
1644
                                if (matches && matches[1]) {
×
1645
                                    tableColumn.precision = +matches[1]
×
1646
                                }
×
1647
                                if (
×
1648
                                    this.driver.withScaleColumnTypes.find(
×
1649
                                        (col) => col === dataType,
×
1650
                                    )
×
1651
                                ) {
×
1652
                                    if (matches && matches[2]) {
×
1653
                                        tableColumn.scale = +matches[2]
×
1654
                                    }
×
1655
                                }
×
1656
                                tableColumn.type = dataType // remove the precision/scale part from the datatype
×
1657
                            }
×
1658
                        }
×
1659

×
1660
                        return tableColumn
×
1661
                    }),
×
1662
                )
×
1663

×
1664
                // find foreign key constraints from CREATE TABLE sql
×
1665
                let fkResult
×
1666
                const fkMappings: {
×
1667
                    name: string
×
1668
                    columns: string[]
×
1669
                    referencedTableName: string
×
1670
                }[] = []
×
1671
                const fkRegex =
×
1672
                    /CONSTRAINT "([^"]*)" FOREIGN KEY ?\((.*?)\) REFERENCES "([^"]*)"/g
×
1673
                while ((fkResult = fkRegex.exec(sql)) !== null) {
×
1674
                    fkMappings.push({
×
1675
                        name: fkResult[1],
×
1676
                        columns: fkResult[2]
×
1677
                            .substr(1, fkResult[2].length - 2)
×
1678
                            .split(`", "`),
×
1679
                        referencedTableName: fkResult[3],
×
1680
                    })
×
1681
                }
×
1682

×
1683
                // build foreign keys
×
1684
                const tableForeignKeyConstraints = OrmUtils.uniq(
×
1685
                    dbForeignKeys,
×
1686
                    (dbForeignKey) => dbForeignKey["id"],
×
1687
                )
×
1688

×
1689
                table.foreignKeys = tableForeignKeyConstraints.map(
×
1690
                    (foreignKey) => {
×
1691
                        const ownForeignKeys = dbForeignKeys.filter(
×
1692
                            (dbForeignKey) =>
×
1693
                                dbForeignKey["id"] === foreignKey["id"] &&
×
1694
                                dbForeignKey["table"] === foreignKey["table"],
×
1695
                        )
×
1696
                        const columnNames = ownForeignKeys.map(
×
1697
                            (dbForeignKey) => dbForeignKey["from"],
×
1698
                        )
×
1699
                        const referencedColumnNames = ownForeignKeys.map(
×
1700
                            (dbForeignKey) => dbForeignKey["to"],
×
1701
                        )
×
1702

×
1703
                        // find related foreign key mapping
×
1704
                        const fkMapping = fkMappings.find(
×
1705
                            (it) =>
×
1706
                                it.referencedTableName ===
×
1707
                                    foreignKey["table"] &&
×
1708
                                it.columns.every(
×
1709
                                    (column) =>
×
1710
                                        columnNames.indexOf(column) !== -1,
×
1711
                                ),
×
1712
                        )
×
1713

×
1714
                        return new TableForeignKey({
×
1715
                            name: fkMapping?.name,
×
1716
                            columnNames: columnNames,
×
1717
                            referencedTableName: foreignKey["table"],
×
1718
                            referencedColumnNames: referencedColumnNames,
×
1719
                            onDelete: foreignKey["on_delete"],
×
1720
                            onUpdate: foreignKey["on_update"],
×
1721
                        })
×
1722
                    },
×
1723
                )
×
1724

×
1725
                // find unique constraints from CREATE TABLE sql
×
1726
                let uniqueRegexResult
×
1727
                const uniqueMappings: { name: string; columns: string[] }[] = []
×
1728
                const uniqueRegex = /CONSTRAINT "([^"]*)" UNIQUE ?\((.*?)\)/g
×
1729
                while ((uniqueRegexResult = uniqueRegex.exec(sql)) !== null) {
×
1730
                    uniqueMappings.push({
×
1731
                        name: uniqueRegexResult[1],
×
1732
                        columns: uniqueRegexResult[2]
×
1733
                            .substr(1, uniqueRegexResult[2].length - 2)
×
1734
                            .split(`", "`),
×
1735
                    })
×
1736
                }
×
1737

×
1738
                // build unique constraints
×
1739
                const tableUniquePromises = dbIndices
×
1740
                    .filter((dbIndex) => dbIndex["origin"] === "u")
×
1741
                    .map((dbIndex) => dbIndex["name"])
×
1742
                    .filter(
×
1743
                        (value, index, self) => self.indexOf(value) === index,
×
1744
                    )
×
1745
                    .map(async (dbIndexName) => {
×
1746
                        const dbIndex = dbIndices.find(
×
1747
                            (dbIndex) => dbIndex["name"] === dbIndexName,
×
1748
                        )
×
1749
                        const indexInfos: ObjectLiteral[] = await this.query(
×
1750
                            `PRAGMA index_info("${dbIndex!["name"]}")`,
×
1751
                        )
×
1752
                        const indexColumns = indexInfos
×
1753
                            .sort(
×
1754
                                (indexInfo1, indexInfo2) =>
×
1755
                                    parseInt(indexInfo1["seqno"]) -
×
1756
                                    parseInt(indexInfo2["seqno"]),
×
1757
                            )
×
1758
                            .map((indexInfo) => indexInfo["name"])
×
1759
                        if (indexColumns.length === 1) {
×
1760
                            const column = table.columns.find((column) => {
×
1761
                                return !!indexColumns.find(
×
1762
                                    (indexColumn) =>
×
1763
                                        indexColumn === column.name,
×
1764
                                )
×
1765
                            })
×
1766
                            if (column) column.isUnique = true
×
1767
                        }
×
1768

×
1769
                        // find existent mapping by a column names
×
1770
                        const foundMapping = uniqueMappings.find((mapping) => {
×
1771
                            return mapping!.columns.every(
×
1772
                                (column) => indexColumns.indexOf(column) !== -1,
×
1773
                            )
×
1774
                        })
×
1775

×
1776
                        return new TableUnique({
×
1777
                            name: foundMapping
×
1778
                                ? foundMapping.name
×
1779
                                : this.connection.namingStrategy.uniqueConstraintName(
×
1780
                                      table,
×
1781
                                      indexColumns,
×
1782
                                  ),
×
1783
                            columnNames: indexColumns,
×
1784
                        })
×
1785
                    })
×
1786
                table.uniques = (await Promise.all(
×
1787
                    tableUniquePromises,
×
1788
                )) as TableUnique[]
×
1789

×
1790
                // build checks
×
1791
                let result
×
1792
                const regexp =
×
1793
                    /CONSTRAINT "([^"]*)" CHECK ?(\(.*?\))([,]|[)]$)/g
×
1794
                while ((result = regexp.exec(sql)) !== null) {
×
1795
                    table.checks.push(
×
1796
                        new TableCheck({
×
1797
                            name: result[1],
×
1798
                            expression: result[2],
×
1799
                        }),
×
1800
                    )
×
1801
                }
×
1802

×
1803
                // build indices
×
1804
                const indicesPromises = dbIndices
×
1805
                    .filter((dbIndex) => dbIndex["origin"] === "c")
×
1806
                    .map((dbIndex) => dbIndex["name"])
×
1807
                    .filter(
×
1808
                        (value, index, self) => self.indexOf(value) === index,
×
1809
                    ) // unqiue
×
1810
                    .map(async (dbIndexName) => {
×
1811
                        const indexDef = dbIndicesDef.find(
×
1812
                            (dbIndexDef) => dbIndexDef["name"] === dbIndexName,
×
1813
                        )
×
1814
                        const condition = /WHERE (.*)/.exec(indexDef!["sql"])
×
1815
                        const dbIndex = dbIndices.find(
×
1816
                            (dbIndex) => dbIndex["name"] === dbIndexName,
×
1817
                        )
×
1818
                        const indexInfos: ObjectLiteral[] = await this.query(
×
1819
                            `PRAGMA index_info("${dbIndex!["name"]}")`,
×
1820
                        )
×
1821
                        const indexColumns = indexInfos
×
1822
                            .sort(
×
1823
                                (indexInfo1, indexInfo2) =>
×
1824
                                    parseInt(indexInfo1["seqno"]) -
×
1825
                                    parseInt(indexInfo2["seqno"]),
×
1826
                            )
×
1827
                            .map((indexInfo) => indexInfo["name"])
×
1828
                        const dbIndexPath = `${
×
1829
                            dbTable["database"] ? `${dbTable["database"]}.` : ""
×
1830
                        }${dbIndex!["name"]}`
×
1831

×
1832
                        const isUnique =
×
1833
                            dbIndex!["unique"] === "1" ||
×
1834
                            dbIndex!["unique"] === 1
×
1835
                        return new TableIndex(<TableIndexOptions>{
×
1836
                            table: table,
×
1837
                            name: dbIndexPath,
×
1838
                            columnNames: indexColumns,
×
1839
                            isUnique: isUnique,
×
1840
                            where: condition ? condition[1] : undefined,
×
1841
                        })
×
1842
                    })
×
1843
                const indices = await Promise.all(indicesPromises)
×
1844
                table.indices = indices.filter(
×
1845
                    (index) => !!index,
×
1846
                ) as TableIndex[]
×
1847

×
1848
                return table
×
1849
            }),
×
1850
        )
×
1851
    }
×
1852

28✔
1853
    /**
28✔
1854
     * Builds create table sql.
28✔
1855
     * @param table
28✔
1856
     * @param createForeignKeys
28✔
1857
     * @param temporaryTable
28✔
1858
     */
28✔
1859
    protected createTableSql(
28✔
1860
        table: Table,
84✔
1861
        createForeignKeys?: boolean,
84✔
1862
        temporaryTable?: boolean,
84✔
1863
    ): Query {
84✔
1864
        const primaryColumns = table.columns.filter(
84✔
1865
            (column) => column.isPrimary,
84✔
1866
        )
84✔
1867
        const hasAutoIncrement = primaryColumns.find(
84✔
1868
            (column) =>
84✔
1869
                column.isGenerated && column.generationStrategy === "increment",
84✔
1870
        )
84✔
1871
        const skipPrimary = primaryColumns.length > 1
84✔
1872
        if (skipPrimary && hasAutoIncrement)
84✔
1873
            throw new TypeORMError(
84!
1874
                `Sqlite does not support AUTOINCREMENT on composite primary key`,
×
1875
            )
×
1876

84✔
1877
        const columnDefinitions = table.columns
84✔
1878
            .map((column) => this.buildCreateColumnSql(column, skipPrimary))
84✔
1879
            .join(", ")
84✔
1880
        const [database] = this.splitTablePath(table.name)
84✔
1881
        let sql = `CREATE TABLE ${this.escapePath(
84✔
1882
            table.name,
84✔
1883
        )} (${columnDefinitions}`
84✔
1884

84✔
1885
        const [databaseNew, tableName] = this.splitTablePath(table.name)
84✔
1886
        const newTableName = temporaryTable
84✔
1887
            ? `${databaseNew ? `${databaseNew}.` : ""}${tableName.replace(
84!
1888
                  /^temporary_/,
24✔
1889
                  "",
24✔
1890
              )}`
24✔
1891
            : table.name
84✔
1892

84✔
1893
        // need for `addColumn()` method, because it recreates table.
84✔
1894
        table.columns
84✔
1895
            .filter((column) => column.isUnique)
84✔
1896
            .forEach((column) => {
84✔
1897
                const isUniqueExist = table.uniques.some(
×
1898
                    (unique) =>
×
1899
                        unique.columnNames.length === 1 &&
×
1900
                        unique.columnNames[0] === column.name,
×
1901
                )
×
1902
                if (!isUniqueExist)
×
1903
                    table.uniques.push(
×
1904
                        new TableUnique({
×
1905
                            name: this.connection.namingStrategy.uniqueConstraintName(
×
1906
                                table,
×
1907
                                [column.name],
×
1908
                            ),
×
1909
                            columnNames: [column.name],
×
1910
                        }),
×
1911
                    )
×
1912
            })
84✔
1913

84✔
1914
        if (table.uniques.length > 0) {
84!
1915
            const uniquesSql = table.uniques
×
1916
                .map((unique) => {
×
1917
                    const uniqueName = unique.name
×
1918
                        ? unique.name
×
1919
                        : this.connection.namingStrategy.uniqueConstraintName(
×
1920
                              newTableName,
×
1921
                              unique.columnNames,
×
1922
                          )
×
1923
                    const columnNames = unique.columnNames
×
1924
                        .map((columnName) => `"${columnName}"`)
×
1925
                        .join(", ")
×
1926
                    return `CONSTRAINT "${uniqueName}" UNIQUE (${columnNames})`
×
1927
                })
×
1928
                .join(", ")
×
1929

×
1930
            sql += `, ${uniquesSql}`
×
1931
        }
×
1932

84✔
1933
        if (table.checks.length > 0) {
84!
1934
            const checksSql = table.checks
×
1935
                .map((check) => {
×
1936
                    const checkName = check.name
×
1937
                        ? check.name
×
1938
                        : this.connection.namingStrategy.checkConstraintName(
×
1939
                              newTableName,
×
1940
                              check.expression!,
×
1941
                          )
×
1942
                    return `CONSTRAINT "${checkName}" CHECK (${check.expression})`
×
1943
                })
×
1944
                .join(", ")
×
1945

×
1946
            sql += `, ${checksSql}`
×
1947
        }
×
1948

84✔
1949
        if (table.foreignKeys.length > 0 && createForeignKeys) {
84✔
1950
            const foreignKeysSql = table.foreignKeys
24✔
1951
                .filter((fk) => {
24✔
1952
                    const [referencedDatabase] = this.splitTablePath(
36✔
1953
                        fk.referencedTableName,
36✔
1954
                    )
36✔
1955
                    if (referencedDatabase !== database) {
36!
1956
                        return false
×
1957
                    }
×
1958
                    return true
36✔
1959
                })
24✔
1960
                .map((fk) => {
24✔
1961
                    const [, referencedTable] = this.splitTablePath(
36✔
1962
                        fk.referencedTableName,
36✔
1963
                    )
36✔
1964
                    const columnNames = fk.columnNames
36✔
1965
                        .map((columnName) => `"${columnName}"`)
36✔
1966
                        .join(", ")
36✔
1967
                    if (!fk.name)
36✔
1968
                        fk.name = this.connection.namingStrategy.foreignKeyName(
36!
1969
                            newTableName,
×
1970
                            fk.columnNames,
×
1971
                            this.getTablePath(fk),
×
1972
                            fk.referencedColumnNames,
×
1973
                        )
×
1974
                    const referencedColumnNames = fk.referencedColumnNames
36✔
1975
                        .map((columnName) => `"${columnName}"`)
36✔
1976
                        .join(", ")
36✔
1977

36✔
1978
                    let constraint = `CONSTRAINT "${fk.name}" FOREIGN KEY (${columnNames}) REFERENCES "${referencedTable}" (${referencedColumnNames})`
36✔
1979
                    if (fk.onDelete) constraint += ` ON DELETE ${fk.onDelete}`
36✔
1980
                    if (fk.onUpdate) constraint += ` ON UPDATE ${fk.onUpdate}`
36✔
1981
                    if (fk.deferrable)
36✔
1982
                        constraint += ` DEFERRABLE ${fk.deferrable}`
36!
1983

36✔
1984
                    return constraint
36✔
1985
                })
24✔
1986
                .join(", ")
24✔
1987

24✔
1988
            sql += `, ${foreignKeysSql}`
24✔
1989
        }
24✔
1990

84✔
1991
        if (primaryColumns.length > 1) {
84✔
1992
            const columnNames = primaryColumns
36✔
1993
                .map((column) => `"${column.name}"`)
36✔
1994
                .join(", ")
36✔
1995
            sql += `, PRIMARY KEY (${columnNames})`
36✔
1996
        }
36✔
1997

84✔
1998
        sql += `)`
84✔
1999

84✔
2000
        if (table.withoutRowid) {
84!
2001
            sql += " WITHOUT ROWID"
×
2002
        }
×
2003

84✔
2004
        return new Query(sql)
84✔
2005
    }
84✔
2006

28✔
2007
    /**
28✔
2008
     * Builds drop table sql.
28✔
2009
     * @param tableOrName
28✔
2010
     * @param ifExists
28✔
2011
     */
28✔
2012
    protected dropTableSql(
28✔
2013
        tableOrName: Table | string,
84✔
2014
        ifExists?: boolean,
84✔
2015
    ): Query {
84✔
2016
        const tableName = InstanceChecker.isTable(tableOrName)
84✔
2017
            ? tableOrName.name
84✔
2018
            : tableOrName
84!
2019
        const query = ifExists
84✔
2020
            ? `DROP TABLE IF EXISTS ${this.escapePath(tableName)}`
84!
2021
            : `DROP TABLE ${this.escapePath(tableName)}`
84✔
2022
        return new Query(query)
84✔
2023
    }
84✔
2024

28✔
2025
    protected createViewSql(view: View): Query {
28✔
2026
        if (typeof view.expression === "string") {
×
2027
            return new Query(`CREATE VIEW "${view.name}" AS ${view.expression}`)
×
2028
        } else {
×
2029
            return new Query(
×
2030
                `CREATE VIEW "${view.name}" AS ${view
×
2031
                    .expression(this.connection)
×
2032
                    .getQuery()}`,
×
2033
            )
×
2034
        }
×
2035
    }
×
2036

28✔
2037
    protected insertViewDefinitionSql(view: View): Query {
28✔
2038
        const expression =
×
2039
            typeof view.expression === "string"
×
2040
                ? view.expression.trim()
×
2041
                : view.expression(this.connection).getQuery()
×
2042
        return this.insertTypeormMetadataSql({
×
2043
            type: MetadataTableType.VIEW,
×
2044
            name: view.name,
×
2045
            value: expression,
×
2046
        })
×
2047
    }
×
2048

28✔
2049
    /**
28✔
2050
     * Builds drop view sql.
28✔
2051
     * @param viewOrPath
28✔
2052
     * @param ifExists
28✔
2053
     */
28✔
2054
    protected dropViewSql(
28✔
2055
        viewOrPath: View | string,
×
2056
        ifExists?: boolean,
×
2057
    ): Query {
×
2058
        const viewName = InstanceChecker.isView(viewOrPath)
×
2059
            ? viewOrPath.name
×
2060
            : viewOrPath
×
2061
        const query = ifExists
×
2062
            ? `DROP VIEW IF EXISTS "${viewName}"`
×
2063
            : `DROP VIEW "${viewName}"`
×
2064
        return new Query(query)
×
2065
    }
×
2066

28✔
2067
    /**
28✔
2068
     * Builds remove view sql.
28✔
2069
     * @param viewOrPath
28✔
2070
     */
28✔
2071
    protected deleteViewDefinitionSql(viewOrPath: View | string): Query {
28✔
2072
        const viewName = InstanceChecker.isView(viewOrPath)
×
2073
            ? viewOrPath.name
×
2074
            : viewOrPath
×
2075
        return this.deleteTypeormMetadataSql({
×
2076
            type: MetadataTableType.VIEW,
×
2077
            name: viewName,
×
2078
        })
×
2079
    }
×
2080

28✔
2081
    /**
28✔
2082
     * Builds create index sql.
28✔
2083
     * @param table
28✔
2084
     * @param index
28✔
2085
     */
28✔
2086
    protected createIndexSql(table: Table, index: TableIndex): Query {
28✔
2087
        const columns = index.columnNames
72✔
2088
            .map((columnName) => `"${columnName}"`)
72✔
2089
            .join(", ")
72✔
2090
        const [database, tableName] = this.splitTablePath(table.name)
72✔
2091
        return new Query(
72✔
2092
            `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX ${
72!
2093
                database ? `"${database}".` : ""
72!
2094
            }${this.escapePath(index.name!)} ON "${tableName}" (${columns}) ${
72✔
2095
                index.where ? "WHERE " + index.where : ""
72!
2096
            }`,
72✔
2097
        )
72✔
2098
    }
72✔
2099

28✔
2100
    /**
28✔
2101
     * Builds drop index sql.
28✔
2102
     * @param indexOrName
28✔
2103
     * @param ifExists
28✔
2104
     */
28✔
2105
    protected dropIndexSql(
28✔
2106
        indexOrName: TableIndex | string,
72✔
2107
        ifExists?: boolean,
72✔
2108
    ): Query {
72✔
2109
        const indexName = InstanceChecker.isTableIndex(indexOrName)
72✔
2110
            ? indexOrName.name
72✔
2111
            : indexOrName
72!
2112
        if (!indexName)
72✔
2113
            throw new TypeORMError(
72!
2114
                `Index name is not set. Unable to drop index.`,
×
2115
            )
×
2116
        const query = ifExists
72✔
2117
            ? `DROP INDEX IF EXISTS ${this.escapePath(indexName)}`
72!
2118
            : `DROP INDEX ${this.escapePath(indexName)}`
72✔
2119
        return new Query(query)
72✔
2120
    }
72✔
2121

28✔
2122
    /**
28✔
2123
     * Builds a query for create column.
28✔
2124
     * @param column
28✔
2125
     * @param skipPrimary
28✔
2126
     */
28✔
2127
    protected buildCreateColumnSql(
28✔
2128
        column: TableColumn,
252✔
2129
        skipPrimary?: boolean,
252✔
2130
    ): string {
252✔
2131
        let c = '"' + column.name + '"'
252✔
2132
        if (InstanceChecker.isColumnMetadata(column)) {
252!
2133
            c += " " + this.driver.normalizeType(column)
×
2134
        } else {
252✔
2135
            c += " " + this.connection.driver.createFullType(column)
252✔
2136
        }
252✔
2137
        if (column.enum && !column.isArray)
252!
2138
            c +=
252!
2139
                ' CHECK( "' +
×
2140
                column.name +
×
2141
                '" IN (' +
×
2142
                column.enum.map((val) => "'" + val + "'").join(",") +
×
2143
                ") )"
×
2144
        if (column.isPrimary && !skipPrimary) c += " PRIMARY KEY"
252✔
2145
        if (
252✔
2146
            column.isGenerated === true &&
252✔
2147
            column.generationStrategy === "increment"
36✔
2148
        )
252✔
2149
            // don't use skipPrimary here since updates can update already exist primary without auto inc.
252✔
2150
            c += " AUTOINCREMENT"
252✔
2151
        if (column.collation) c += " COLLATE " + column.collation
252!
2152
        if (column.isNullable !== true) c += " NOT NULL"
252✔
2153

252✔
2154
        if (column.asExpression) {
252!
2155
            c += ` AS (${column.asExpression}) ${
×
2156
                column.generatedType ? column.generatedType : "VIRTUAL"
×
2157
            }`
×
2158
        } else {
252✔
2159
            if (column.default !== undefined && column.default !== null)
252!
2160
                c += " DEFAULT (" + column.default + ")"
252!
2161
        }
252✔
2162

252✔
2163
        return c
252✔
2164
    }
252✔
2165

28✔
2166
    protected async recreateTable(
28✔
2167
        newTable: Table,
24✔
2168
        oldTable: Table,
24✔
2169
        migrateData = true,
24✔
2170
    ): Promise<void> {
24✔
2171
        const upQueries: Query[] = []
24✔
2172
        const downQueries: Query[] = []
24✔
2173

24✔
2174
        // drop old table indices
24✔
2175
        oldTable.indices.forEach((index) => {
24✔
2176
            upQueries.push(this.dropIndexSql(index))
24✔
2177
            downQueries.push(this.createIndexSql(oldTable, index))
24✔
2178
        })
24✔
2179

24✔
2180
        // change table name into 'temporary_table'
24✔
2181
        let [databaseNew, tableNameNew] = this.splitTablePath(newTable.name)
24✔
2182
        const [, tableNameOld] = this.splitTablePath(oldTable.name)
24✔
2183
        newTable.name = tableNameNew = `${
24✔
2184
            databaseNew ? `${databaseNew}.` : ""
24!
2185
        }temporary_${tableNameNew}`
24✔
2186

24✔
2187
        // create new table
24✔
2188
        upQueries.push(this.createTableSql(newTable, true, true))
24✔
2189
        downQueries.push(this.dropTableSql(newTable))
24✔
2190

24✔
2191
        // migrate all data from the old table into new table
24✔
2192
        if (migrateData) {
24✔
2193
            let newColumnNames = newTable.columns
24✔
2194
                .filter((column) => !column.generatedType)
24✔
2195
                .map((column) => `"${column.name}"`)
24✔
2196

24✔
2197
            let oldColumnNames = oldTable.columns
24✔
2198
                .filter((column) => !column.generatedType)
24✔
2199
                .map((column) => `"${column.name}"`)
24✔
2200

24✔
2201
            if (oldColumnNames.length < newColumnNames.length) {
24!
2202
                newColumnNames = newTable.columns
×
2203
                    .filter((column) => {
×
2204
                        const oldColumn = oldTable.columns.find(
×
2205
                            (c) => c.name === column.name,
×
2206
                        )
×
2207
                        if (oldColumn && oldColumn.generatedType) return false
×
2208
                        return !column.generatedType && oldColumn
×
2209
                    })
×
2210
                    .map((column) => `"${column.name}"`)
×
2211
            } else if (oldColumnNames.length > newColumnNames.length) {
24!
2212
                oldColumnNames = oldTable.columns
×
2213
                    .filter((column) => {
×
2214
                        return (
×
2215
                            !column.generatedType &&
×
2216
                            newTable.columns.find((c) => c.name === column.name)
×
2217
                        )
×
2218
                    })
×
2219
                    .map((column) => `"${column.name}"`)
×
2220
            }
×
2221

24✔
2222
            upQueries.push(
24✔
2223
                new Query(
24✔
2224
                    `INSERT INTO ${this.escapePath(
24✔
2225
                        newTable.name,
24✔
2226
                    )}(${newColumnNames.join(
24✔
2227
                        ", ",
24✔
2228
                    )}) SELECT ${oldColumnNames.join(
24✔
2229
                        ", ",
24✔
2230
                    )} FROM ${this.escapePath(oldTable.name)}`,
24✔
2231
                ),
24✔
2232
            )
24✔
2233
            downQueries.push(
24✔
2234
                new Query(
24✔
2235
                    `INSERT INTO ${this.escapePath(
24✔
2236
                        oldTable.name,
24✔
2237
                    )}(${oldColumnNames.join(
24✔
2238
                        ", ",
24✔
2239
                    )}) SELECT ${newColumnNames.join(
24✔
2240
                        ", ",
24✔
2241
                    )} FROM ${this.escapePath(newTable.name)}`,
24✔
2242
                ),
24✔
2243
            )
24✔
2244
        }
24✔
2245

24✔
2246
        // drop old table
24✔
2247
        upQueries.push(this.dropTableSql(oldTable))
24✔
2248
        downQueries.push(this.createTableSql(oldTable, true))
24✔
2249

24✔
2250
        // rename old table
24✔
2251
        upQueries.push(
24✔
2252
            new Query(
24✔
2253
                `ALTER TABLE ${this.escapePath(
24✔
2254
                    newTable.name,
24✔
2255
                )} RENAME TO ${this.escapePath(tableNameOld)}`,
24✔
2256
            ),
24✔
2257
        )
24✔
2258
        downQueries.push(
24✔
2259
            new Query(
24✔
2260
                `ALTER TABLE ${this.escapePath(
24✔
2261
                    oldTable.name,
24✔
2262
                )} RENAME TO ${this.escapePath(tableNameNew)}`,
24✔
2263
            ),
24✔
2264
        )
24✔
2265

24✔
2266
        newTable.name = oldTable.name
24✔
2267

24✔
2268
        // recreate table indices
24✔
2269
        newTable.indices.forEach((index) => {
24✔
2270
            // new index may be passed without name. In this case we generate index name manually.
24✔
2271
            if (!index.name)
24✔
2272
                index.name = this.connection.namingStrategy.indexName(
24!
2273
                    newTable,
×
2274
                    index.columnNames,
×
2275
                    index.where,
×
2276
                )
×
2277
            upQueries.push(this.createIndexSql(newTable, index))
24✔
2278
            downQueries.push(this.dropIndexSql(index))
24✔
2279
        })
24✔
2280

24✔
2281
        // update generated columns in "typeorm_metadata" table
24✔
2282
        // Step 1: clear data for removed generated columns
24✔
2283
        oldTable.columns
24✔
2284
            .filter((column) => {
24✔
2285
                const newTableColumn = newTable.columns.find(
72✔
2286
                    (c) => c.name === column.name,
72✔
2287
                )
72✔
2288
                // we should delete record from "typeorm_metadata" if generated column was removed
72✔
2289
                // or it was changed to non-generated
72✔
2290
                return (
72✔
2291
                    column.generatedType &&
72!
2292
                    column.asExpression &&
72!
2293
                    (!newTableColumn ||
×
2294
                        (!newTableColumn.generatedType &&
×
2295
                            !newTableColumn.asExpression))
×
2296
                )
72✔
2297
            })
24✔
2298
            .forEach((column) => {
24✔
2299
                const deleteQuery = this.deleteTypeormMetadataSql({
×
2300
                    table: oldTable.name,
×
2301
                    type: MetadataTableType.GENERATED_COLUMN,
×
2302
                    name: column.name,
×
2303
                })
×
2304

×
2305
                const insertQuery = this.insertTypeormMetadataSql({
×
2306
                    table: oldTable.name,
×
2307
                    type: MetadataTableType.GENERATED_COLUMN,
×
2308
                    name: column.name,
×
2309
                    value: column.asExpression,
×
2310
                })
×
2311

×
2312
                upQueries.push(deleteQuery)
×
2313
                downQueries.push(insertQuery)
×
2314
            })
24✔
2315

24✔
2316
        // Step 2: add data for new generated columns
24✔
2317
        newTable.columns
24✔
2318
            .filter(
24✔
2319
                (column) =>
24✔
2320
                    column.generatedType &&
72!
2321
                    column.asExpression &&
72!
2322
                    !oldTable.columns.some((c) => c.name === column.name),
24✔
2323
            )
24✔
2324
            .forEach((column) => {
24✔
2325
                const insertQuery = this.insertTypeormMetadataSql({
×
2326
                    table: newTable.name,
×
2327
                    type: MetadataTableType.GENERATED_COLUMN,
×
2328
                    name: column.name,
×
2329
                    value: column.asExpression,
×
2330
                })
×
2331

×
2332
                const deleteQuery = this.deleteTypeormMetadataSql({
×
2333
                    table: newTable.name,
×
2334
                    type: MetadataTableType.GENERATED_COLUMN,
×
2335
                    name: column.name,
×
2336
                })
×
2337

×
2338
                upQueries.push(insertQuery)
×
2339
                downQueries.push(deleteQuery)
×
2340
            })
24✔
2341

24✔
2342
        // Step 3: update changed expressions
24✔
2343
        newTable.columns
24✔
2344
            .filter((column) => column.generatedType && column.asExpression)
24!
2345
            .forEach((column) => {
24✔
2346
                const oldColumn = oldTable.columns.find(
×
2347
                    (c) =>
×
2348
                        c.name === column.name &&
×
2349
                        c.generatedType &&
×
2350
                        column.generatedType &&
×
2351
                        c.asExpression !== column.asExpression,
×
2352
                )
×
2353

×
2354
                if (!oldColumn) return
×
2355

×
2356
                // update expression
×
2357
                const deleteQuery = this.deleteTypeormMetadataSql({
×
2358
                    table: oldTable.name,
×
2359
                    type: MetadataTableType.GENERATED_COLUMN,
×
2360
                    name: oldColumn.name,
×
2361
                })
×
2362

×
2363
                const insertQuery = this.insertTypeormMetadataSql({
×
2364
                    table: newTable.name,
×
2365
                    type: MetadataTableType.GENERATED_COLUMN,
×
2366
                    name: column.name,
×
2367
                    value: column.asExpression,
×
2368
                })
×
2369

×
2370
                upQueries.push(deleteQuery)
×
2371
                upQueries.push(insertQuery)
×
2372

×
2373
                // revert update
×
2374
                const revertInsertQuery = this.insertTypeormMetadataSql({
×
2375
                    table: newTable.name,
×
2376
                    type: MetadataTableType.GENERATED_COLUMN,
×
2377
                    name: oldColumn.name,
×
2378
                    value: oldColumn.asExpression,
×
2379
                })
×
2380

×
2381
                const revertDeleteQuery = this.deleteTypeormMetadataSql({
×
2382
                    table: oldTable.name,
×
2383
                    type: MetadataTableType.GENERATED_COLUMN,
×
2384
                    name: column.name,
×
2385
                })
×
2386

×
2387
                downQueries.push(revertInsertQuery)
×
2388
                downQueries.push(revertDeleteQuery)
×
2389
            })
24✔
2390

24✔
2391
        await this.executeQueries(upQueries, downQueries)
24✔
2392
        this.replaceCachedTable(oldTable, newTable)
24✔
2393
    }
24✔
2394

28✔
2395
    /**
28✔
2396
     * tablePath e.g. "myDB.myTable", "myTable"
28✔
2397
     * @param tablePath
28✔
2398
     */
28✔
2399
    protected splitTablePath(tablePath: string): [string | undefined, string] {
28✔
2400
        return (
360✔
2401
            tablePath.indexOf(".") !== -1
360✔
2402
                ? tablePath.split(".")
360!
2403
                : [undefined, tablePath]
360✔
2404
        ) as [string | undefined, string]
360✔
2405
    }
360✔
2406

28✔
2407
    /**
28✔
2408
     * Escapes given table or view path. Tolerates leading/trailing dots
28✔
2409
     * @param target
28✔
2410
     * @param disableEscape
28✔
2411
     */
28✔
2412
    protected escapePath(
28✔
2413
        target: Table | View | string,
504✔
2414
        disableEscape?: boolean,
504✔
2415
    ): string {
504✔
2416
        const tableName =
504✔
2417
            InstanceChecker.isTable(target) || InstanceChecker.isView(target)
504✔
2418
                ? target.name
504!
2419
                : target
504✔
2420
        return tableName
504✔
2421
            .replace(/^\.+|\.+$/g, "")
504✔
2422
            .split(".")
504✔
2423
            .map((i) => (disableEscape ? i : this.driver.escape(i)))
504!
2424
            .join(".")
504✔
2425
    }
504✔
2426

28✔
2427
    /**
28✔
2428
     * Change table comment.
28✔
2429
     * @param tableOrName
28✔
2430
     * @param comment
28✔
2431
     */
28✔
2432
    changeTableComment(
28✔
2433
        tableOrName: Table | string,
×
2434
        comment?: string,
×
2435
    ): Promise<void> {
×
2436
        throw new TypeORMError(`sqlit driver does not support change comment.`)
×
2437
    }
×
2438
}
28✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc