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

typeorm / typeorm / 22831820825

08 Mar 2026 10:57PM UTC coverage: 74.734% (+0.07%) from 74.664%
22831820825

Pull #12121

github

web-flow
Merge 1a6f8d65b into cd72d2a03
Pull Request #12121: feat(QueryRunner): add ifExists parameter to all drop methods

26613 of 32154 branches covered (82.77%)

Branch coverage included in aggregate %.

631 of 907 new or added lines in 11 files covered. (69.57%)

5 existing lines in 4 files now uncovered.

84313 of 116273 relevant lines covered (72.51%)

65684.32 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

58,194✔
118
        await this.broadcaster.broadcast("AfterTransactionStart")
58,194✔
119
    }
58,194✔
120

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

58,035✔
128
        await this.broadcaster.broadcast("BeforeTransactionCommit")
58,035✔
129

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

58,032✔
140
        await this.broadcaster.broadcast("AfterTransactionCommit")
58,032✔
141
    }
58,032✔
142

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

66✔
476
        newTable.name = newTableName
66✔
477

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

23✔
791
    /**
23✔
792
     * Drops the columns in the table.
23✔
793
     * @param tableOrName
23✔
794
     * @param columns
23✔
795
     * @param ifExists
23✔
796
     */
23✔
797
    async dropColumns(
23✔
798
        tableOrName: Table | string,
42✔
799
        columns: TableColumn[] | string[],
42✔
800
        ifExists?: boolean,
42✔
801
    ): Promise<void> {
42✔
802
        const table = InstanceChecker.isTable(tableOrName)
42✔
803
            ? tableOrName
42✔
804
            : await this.getCachedTable(tableOrName)
42!
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)
72✔
810
                ? column
72✔
811
                : table.findColumnByName(column)
72✔
812
            if (!columnInstance) {
72!
NEW
813
                if (ifExists) return
×
814
                throw new Error(
×
815
                    `Column "${column}" was not found in table "${table.name}"`,
×
816
                )
×
NEW
817
            }
×
818

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

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

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

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

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

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

6✔
890
        if (ifExists && table.primaryColumns.length === 0) return
24!
891

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

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

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

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

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

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

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

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

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

3✔
986
        await this.recreateTable(changedTable, table)
3✔
987
    }
18✔
988

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

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

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

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

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

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

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

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

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

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

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

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

15✔
1155
        await this.recreateTable(changedTable, table)
15✔
1156
    }
10,980✔
1157

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

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

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

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

6✔
1206
        await this.recreateTable(changedTable, table)
6✔
1207
    }
42✔
1208

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

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

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

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

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

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

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

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

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

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

8,460✔
1329
        await this.query(`PRAGMA foreign_keys = OFF`)
8,460✔
1330

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

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

8,460✔
1353
            if (!isAnotherTransactionActive) await this.commitTransaction()
8,460✔
1354
        } catch (error) {
8,460!
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 {
8,460✔
1362
            await this.query(`PRAGMA foreign_keys = ON`)
8,460✔
1363
        }
8,460✔
1364
    }
8,460✔
1365

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

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

57✔
1376
        if (!viewNames) {
8,751!
1377
            viewNames = []
×
1378
        }
×
1379

57✔
1380
        const viewNamesString = viewNames
57✔
1381
            .map((name) => "'" + name + "'")
57✔
1382
            .join(", ")
57✔
1383
        let query = `SELECT "t".* FROM "${this.getTypeormMetadataTableName()}" "t" INNER JOIN "sqlite_master" s ON "s"."name" = "t"."name" AND "s"."type" = 'view' WHERE "t"."type" = '${
57✔
1384
            MetadataTableType.VIEW
57✔
1385
        }'`
57✔
1386
        if (viewNamesString.length > 0)
57✔
1387
            query += ` AND "t"."name" IN (${viewNamesString})`
4,683✔
1388
        const dbViews = await this.query(query)
57✔
1389
        return dbViews.map((dbView: any) => {
57✔
1390
            const view = new View()
15✔
1391
            view.name = dbView["name"]
15✔
1392
            view.expression = dbView["value"]
15✔
1393
            return view
15✔
1394
        })
57✔
1395
    }
57✔
1396

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

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

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

10,125✔
1438
        let dbTables: { database?: string; name: string; sql: string }[] = []
10,125✔
1439
        let dbIndicesDef: ObjectLiteral[]
10,125✔
1440

10,125✔
1441
        if (!tableNames) {
10,200!
1442
            const tablesSql = `SELECT * FROM "sqlite_master" WHERE "type" = 'table'`
3✔
1443
            dbTables.push(...(await this.query(tablesSql)))
3✔
1444

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

10,122✔
1458
            const tableNamesWithDot = tableNames.filter((tableName) => {
10,122✔
1459
                return tableName.split(".").length > 1
31,734✔
1460
            })
10,122✔
1461

10,122✔
1462
            const queryPromises = (type: "table" | "index") => {
10,122✔
1463
                const promises = [
20,244✔
1464
                    ...tableNamesWithDot.map((tableName) =>
20,244✔
1465
                        this.loadTableRecords(tableName, type),
20,244✔
1466
                    ),
20,244✔
1467
                ]
20,244✔
1468

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

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

10,125✔
1489
        // if tables were not found in the db, no need to proceed
10,125✔
1490
        if (dbTables.length === 0) {
10,200✔
1491
            return []
8,469✔
1492
        }
8,469✔
1493

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

2,523✔
1507
                const sql = dbTable["sql"]
2,523✔
1508

2,523✔
1509
                const withoutRowid = sql.includes("WITHOUT ROWID")
2,523✔
1510
                const table = new Table({ name: tablePath, withoutRowid })
2,523✔
1511

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

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

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

8,832✔
1581
                        if (
8,832✔
1582
                            dbColumn["hidden"] === 2 ||
8,832✔
1583
                            dbColumn["hidden"] === 3
8,796✔
1584
                        ) {
8,832!
1585
                            tableColumn.generatedType =
105✔
1586
                                dbColumn["hidden"] === 2 ? "VIRTUAL" : "STORED"
105✔
1587

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

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

8,832✔
1606
                        if (tableColumn.type === "varchar") {
8,832✔
1607
                            tableColumn.enum = OrmUtils.parseSqlCheckExpression(
4,155✔
1608
                                sql,
4,155✔
1609
                                tableColumn.name,
4,155✔
1610
                            )
4,155✔
1611
                        }
4,155✔
1612

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

8,832✔
1659
                        return tableColumn
8,832✔
1660
                    }),
2,523✔
1661
                )
2,523✔
1662

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

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

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

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

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

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

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

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

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

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

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

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

2,523✔
1847
                return table
2,523✔
1848
            }),
1,656✔
1849
        )
1,656✔
1850
    }
1,656✔
1851

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

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

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

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

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

8,265✔
1929
            sql += `, ${uniquesSql}`
8,265✔
1930
        }
8,265✔
1931

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

888✔
1945
            sql += `, ${checksSql}`
888✔
1946
        }
888✔
1947

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

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

19,014✔
1983
                    return constraint
19,014✔
1984
                })
11,220✔
1985
                .join(", ")
11,220✔
1986

11,220✔
1987
            sql += `, ${foreignKeysSql}`
11,220✔
1988
        }
11,220✔
1989

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

52,599✔
1997
        sql += `)`
52,599✔
1998

52,599✔
1999
        if (table.withoutRowid) {
52,599✔
2000
            sql += " WITHOUT ROWID"
246✔
2001
        }
246✔
2002

52,599✔
2003
        return new Query(sql)
52,599✔
2004
    }
52,599✔
2005

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

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

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

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

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

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

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

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

172,353✔
2153
        if (column.asExpression) {
172,353!
2154
            c += ` AS (${column.asExpression}) ${
159✔
2155
                column.generatedType ? column.generatedType : "VIRTUAL"
159!
2156
            }`
159✔
2157
        } else {
172,353✔
2158
            if (column.default !== undefined && column.default !== null)
172,194✔
2159
                c += " DEFAULT (" + column.default + ")"
172,194✔
2160
        }
172,194✔
2161

172,353✔
2162
        return c
172,353✔
2163
    }
172,353✔
2164

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

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

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

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

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

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

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

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

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

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

11,574✔
2265
        newTable.name = oldTable.name
11,574✔
2266

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

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

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

9✔
2311
                upQueries.push(deleteQuery)
9✔
2312
                downQueries.push(insertQuery)
9✔
2313
            })
11,574✔
2314

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

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

6✔
2337
                upQueries.push(insertQuery)
6✔
2338
                downQueries.push(deleteQuery)
6✔
2339
            })
11,574✔
2340

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

42✔
2353
                if (!oldColumn) return
42✔
2354

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

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

3✔
2369
                upQueries.push(deleteQuery)
3✔
2370
                upQueries.push(insertQuery)
3✔
2371

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

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

3✔
2386
                downQueries.push(revertInsertQuery)
3✔
2387
                downQueries.push(revertDeleteQuery)
3✔
2388
            })
11,574✔
2389

11,574✔
2390
        await this.executeQueries(upQueries, downQueries)
11,574✔
2391
        this.replaceCachedTable(oldTable, newTable)
11,568✔
2392
    }
11,568✔
2393

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

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

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

© 2026 Coveralls, Inc