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

typeorm / typeorm / 23390157208

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

Pull #12252

github

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

17767 of 26580 branches covered (66.84%)

Branch coverage included in aggregate %.

64033 of 117744 relevant lines covered (54.38%)

1514.83 hits per line

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

83.17
/src/query-runner/BaseQueryRunner.ts
1
import type { PostgresDataSourceOptions } from "../driver/postgres/PostgresDataSourceOptions"
28✔
2
import { Query } from "../driver/Query"
28✔
3
import { SqlInMemory } from "../driver/SqlInMemory"
28✔
4
import type { SqlServerDataSourceOptions } from "../driver/sqlserver/SqlServerDataSourceOptions"
28✔
5
import type { TableIndex } from "../schema-builder/table/TableIndex"
28✔
6
import type { View } from "../schema-builder/view/View"
28✔
7
import type { DataSource } from "../data-source/DataSource"
28✔
8
import type { Table } from "../schema-builder/table/Table"
28✔
9
import type { EntityManager } from "../entity-manager/EntityManager"
28✔
10
import type { TableColumn } from "../schema-builder/table/TableColumn"
28✔
11
import type { Broadcaster } from "../subscriber/Broadcaster"
28✔
12
import type { ReplicationMode } from "../driver/types/ReplicationMode"
28✔
13
import { TypeORMError } from "../error/TypeORMError"
28✔
14
import type { EntityMetadata } from "../metadata/EntityMetadata"
28✔
15
import type { TableForeignKey } from "../schema-builder/table/TableForeignKey"
28✔
16
import { OrmUtils } from "../util/OrmUtils"
28✔
17
import type { MetadataTableType } from "../driver/types/MetadataTableType"
28✔
18
import { InstanceChecker } from "../util/InstanceChecker"
28✔
19
import { buildSqlTag } from "../util/SqlTagUtils"
28✔
20

28✔
21
export abstract class BaseQueryRunner implements AsyncDisposable {
12,008✔
22
    // -------------------------------------------------------------------------
12,008✔
23
    // Public Properties
12,008✔
24
    // -------------------------------------------------------------------------
12,008✔
25

12,008✔
26
    /**
12,008✔
27
     * Connection used by this query runner.
12,008✔
28
     */
12,008✔
29
    connection: DataSource
12,008✔
30

12,008✔
31
    /**
12,008✔
32
     * Entity manager working only with current query runner.
12,008✔
33
     */
12,008✔
34
    manager: EntityManager
12,008✔
35

12,008✔
36
    /**
12,008✔
37
     * Indicates if connection for this query runner is released.
12,008✔
38
     * Once its released, query runner cannot run queries anymore.
12,008✔
39
     */
12,008✔
40
    isReleased = false
12,008✔
41

12,008✔
42
    /**
12,008✔
43
     * Indicates if transaction is in progress.
12,008✔
44
     */
12,008✔
45
    isTransactionActive = false
12,008✔
46

12,008✔
47
    /**
12,008✔
48
     * Stores temporarily user data.
12,008✔
49
     * Useful for sharing data with subscribers.
12,008✔
50
     */
12,008✔
51
    data = {}
12,008✔
52

12,008✔
53
    /**
12,008✔
54
     * All synchronized tables in the database.
12,008✔
55
     */
12,008✔
56
    loadedTables: Table[] = []
12,008✔
57

12,008✔
58
    /**
12,008✔
59
     * All synchronized views in the database.
12,008✔
60
     */
12,008✔
61
    loadedViews: View[] = []
12,008✔
62

12,008✔
63
    /**
12,008✔
64
     * Broadcaster used on this query runner to broadcast entity events.
12,008✔
65
     */
12,008✔
66
    broadcaster: Broadcaster
12,008✔
67

12,008✔
68
    // -------------------------------------------------------------------------
12,008✔
69
    // Protected Properties
12,008✔
70
    // -------------------------------------------------------------------------
12,008✔
71

12,008✔
72
    /**
12,008✔
73
     * Real database connection from a connection pool used to perform queries.
12,008✔
74
     */
12,008✔
75
    protected databaseConnection: any
12,008✔
76

12,008✔
77
    /**
12,008✔
78
     * Indicates if special query runner mode in which sql queries won't be executed is enabled.
12,008✔
79
     */
12,008✔
80
    protected sqlMemoryMode: boolean = false
12,008✔
81

12,008✔
82
    /**
12,008✔
83
     * Sql-s stored if "sql in memory" mode is enabled.
12,008✔
84
     */
12,008✔
85
    protected sqlInMemory: SqlInMemory = new SqlInMemory()
12,008✔
86

12,008✔
87
    /**
12,008✔
88
     * Mode in which query runner executes.
12,008✔
89
     * Used for replication.
12,008✔
90
     * If replication is not setup its value is ignored.
12,008✔
91
     */
12,008✔
92
    protected mode: ReplicationMode
12,008✔
93

12,008✔
94
    /**
12,008✔
95
     * current depth of transaction.
12,008✔
96
     * for transactionDepth > 0 will use SAVEPOINT to start and commit/rollback transaction blocks
12,008✔
97
     */
12,008✔
98
    protected transactionDepth = 0
12,008✔
99

12,008✔
100
    private cachedTablePaths: Record<string, string> = {}
12,008✔
101

12,008✔
102
    // -------------------------------------------------------------------------
12,008✔
103
    // Public Abstract Methods
12,008✔
104
    // -------------------------------------------------------------------------
12,008✔
105

12,008✔
106
    /**
12,008✔
107
     * Releases used database connection.
12,008✔
108
     * You cannot use query runner methods after connection is released.
12,008✔
109
     */
12,008✔
110
    abstract release(): Promise<void>
12,008✔
111

12,008✔
112
    async [Symbol.asyncDispose](): Promise<void> {
12,008✔
113
        try {
1✔
114
            if (this.isTransactionActive) {
1!
115
                this.transactionDepth = 1 // ignore all savepoints and commit directly
×
116
                await this.commitTransaction()
×
117
            }
×
118
        } finally {
1✔
119
            await this.release()
1✔
120
        }
1✔
121
    }
1✔
122

12,008✔
123
    /**
12,008✔
124
     * Commits transaction.
12,008✔
125
     * Error will be thrown if transaction was not started.
12,008✔
126
     */
12,008✔
127
    abstract commitTransaction(): Promise<void>
12,008✔
128

12,008✔
129
    /**
12,008✔
130
     * Executes a given SQL query.
12,008✔
131
     */
12,008✔
132
    abstract query(
12,008✔
133
        query: string,
12,008✔
134
        parameters?: any[],
12,008✔
135
        useStructuredResult?: boolean,
12,008✔
136
    ): Promise<any>
12,008✔
137

12,008✔
138
    /**
12,008✔
139
     * Tagged template function that executes raw SQL query and returns raw database results.
12,008✔
140
     * Template expressions are automatically transformed into database parameters.
12,008✔
141
     * Raw query execution is supported only by relational databases (MongoDB is not supported).
12,008✔
142
     * Note: Don't call this as a regular function, it is meant to be used with backticks to tag a template literal.
12,008✔
143
     * Example: queryRunner.sql`SELECT * FROM table_name WHERE id = ${id}`
12,008✔
144
     * @param strings
12,008✔
145
     * @param values
12,008✔
146
     */
12,008✔
147
    async sql<T = any>(
12,008✔
148
        strings: TemplateStringsArray,
×
149
        ...values: unknown[]
×
150
    ): Promise<T> {
×
151
        const { query, parameters } = buildSqlTag({
×
152
            driver: this.connection.driver,
×
153
            strings: strings,
×
154
            expressions: values,
×
155
        })
×
156

×
157
        return await this.query(query, parameters)
×
158
    }
×
159

12,008✔
160
    // -------------------------------------------------------------------------
12,008✔
161
    // Protected Abstract Methods
12,008✔
162
    // -------------------------------------------------------------------------
12,008✔
163

12,008✔
164
    protected abstract loadTables(tablePaths?: string[]): Promise<Table[]>
12,008✔
165

12,008✔
166
    protected abstract loadViews(tablePaths?: string[]): Promise<View[]>
12,008✔
167

12,008✔
168
    // -------------------------------------------------------------------------
12,008✔
169
    // Public Methods
12,008✔
170
    // -------------------------------------------------------------------------
12,008✔
171

12,008✔
172
    /**
12,008✔
173
     * Called before migrations are run.
12,008✔
174
     */
12,008✔
175
    async beforeMigration(): Promise<void> {
12,008✔
176
        // Do nothing
1,256✔
177
    }
1,256✔
178

12,008✔
179
    /**
12,008✔
180
     * Called after migrations are run.
12,008✔
181
     */
12,008✔
182
    async afterMigration(): Promise<void> {
12,008✔
183
        // Do nothing
1,253✔
184
    }
1,253✔
185

12,008✔
186
    /**
12,008✔
187
     * Loads given table's data from the database.
12,008✔
188
     * @param tablePath
12,008✔
189
     */
12,008✔
190
    async getTable(tablePath: string): Promise<Table | undefined> {
12,008✔
191
        this.loadedTables = await this.loadTables([tablePath])
215✔
192
        return this.loadedTables.length > 0 ? this.loadedTables[0] : undefined
215✔
193
    }
215✔
194

12,008✔
195
    /**
12,008✔
196
     * Loads all tables (with given names) from the database.
12,008✔
197
     * @param tableNames
12,008✔
198
     */
12,008✔
199
    async getTables(tableNames?: string[]): Promise<Table[]> {
12,008✔
200
        if (!tableNames) {
1,291!
201
            // Don't cache in this case.
×
202
            // This is the new case & isn't used anywhere else anyway.
×
203
            return await this.loadTables(tableNames)
×
204
        }
×
205

1,291✔
206
        this.loadedTables = await this.loadTables(tableNames)
1,291✔
207
        return this.loadedTables
1,291✔
208
    }
1,291✔
209

12,008✔
210
    /**
12,008✔
211
     * Loads given view's data from the database.
12,008✔
212
     * @param viewPath
12,008✔
213
     */
12,008✔
214
    async getView(viewPath: string): Promise<View | undefined> {
12,008✔
215
        this.loadedViews = await this.loadViews([viewPath])
2✔
216
        return this.loadedViews.length > 0 ? this.loadedViews[0] : undefined
2!
217
    }
2✔
218

12,008✔
219
    /**
12,008✔
220
     * Loads given view's data from the database.
12,008✔
221
     * @param viewPaths
12,008✔
222
     */
12,008✔
223
    async getViews(viewPaths?: string[]): Promise<View[]> {
12,008✔
224
        this.loadedViews = await this.loadViews(viewPaths)
1,290✔
225
        return this.loadedViews
1,290✔
226
    }
1,290✔
227

12,008✔
228
    /**
12,008✔
229
     * Enables special query runner mode in which sql queries won't be executed,
12,008✔
230
     * instead they will be memorized into a special variable inside query runner.
12,008✔
231
     * You can get memorized sql using getMemorySql() method.
12,008✔
232
     */
12,008✔
233
    enableSqlMemory(): void {
12,008✔
234
        this.sqlInMemory = new SqlInMemory()
26✔
235
        this.sqlMemoryMode = true
26✔
236
    }
26✔
237

12,008✔
238
    /**
12,008✔
239
     * Disables special query runner mode in which sql queries won't be executed
12,008✔
240
     * started by calling enableSqlMemory() method.
12,008✔
241
     *
12,008✔
242
     * Previously memorized sql will be flushed.
12,008✔
243
     */
12,008✔
244
    disableSqlMemory(): void {
12,008✔
245
        this.sqlInMemory = new SqlInMemory()
26✔
246
        this.sqlMemoryMode = false
26✔
247
    }
26✔
248

12,008✔
249
    /**
12,008✔
250
     * Flushes all memorized sqls.
12,008✔
251
     */
12,008✔
252
    clearSqlMemory(): void {
12,008✔
253
        this.sqlInMemory = new SqlInMemory()
101✔
254
    }
101✔
255

12,008✔
256
    /**
12,008✔
257
     * Gets sql stored in the memory. Parameters in the sql are already replaced.
12,008✔
258
     */
12,008✔
259
    getMemorySql(): SqlInMemory {
12,008✔
260
        return this.sqlInMemory
26✔
261
    }
26✔
262

12,008✔
263
    /**
12,008✔
264
     * Executes up sql queries.
12,008✔
265
     */
12,008✔
266
    async executeMemoryUpSql(): Promise<void> {
12,008✔
267
        for (const { query, parameters } of this.sqlInMemory.upQueries) {
×
268
            await this.query(query, parameters)
×
269
        }
×
270
    }
×
271

12,008✔
272
    /**
12,008✔
273
     * Executes down sql queries.
12,008✔
274
     */
12,008✔
275
    async executeMemoryDownSql(): Promise<void> {
12,008✔
276
        for (const {
37✔
277
            query,
37✔
278
            parameters,
37✔
279
        } of this.sqlInMemory.downQueries.reverse()) {
37✔
280
            await this.query(query, parameters)
121✔
281
        }
121✔
282
    }
37✔
283

12,008✔
284
    getReplicationMode(): ReplicationMode {
12,008✔
285
        return this.mode
13✔
286
    }
13✔
287

12,008✔
288
    // -------------------------------------------------------------------------
12,008✔
289
    // Protected Methods
12,008✔
290
    // -------------------------------------------------------------------------
12,008✔
291

12,008✔
292
    /**
12,008✔
293
     * Gets view from previously loaded views, otherwise loads it from database.
12,008✔
294
     * @param viewName
12,008✔
295
     */
12,008✔
296
    protected async getCachedView(viewName: string): Promise<View> {
12,008✔
297
        const view = this.loadedViews.find((view) => view.name === viewName)
×
298
        if (view) return view
×
299

×
300
        const foundViews = await this.loadViews([viewName])
×
301
        if (foundViews.length > 0) {
×
302
            this.loadedViews.push(foundViews[0])
×
303
            return foundViews[0]
×
304
        } else {
×
305
            throw new TypeORMError(`View "${viewName}" does not exist.`)
×
306
        }
×
307
    }
×
308

12,008✔
309
    /**
12,008✔
310
     * Gets table from previously loaded tables, otherwise loads it from database.
12,008✔
311
     * @param tableName
12,008✔
312
     */
12,008✔
313
    protected async getCachedTable(tableName: string): Promise<Table> {
12,008✔
314
        if (tableName in this.cachedTablePaths) {
69!
315
            const tablePath = this.cachedTablePaths[tableName]
25✔
316
            const table = this.loadedTables.find(
25✔
317
                (table) => this.getTablePath(table) === tablePath,
25✔
318
            )
25✔
319

25✔
320
            if (table) {
25✔
321
                return table
25✔
322
            }
25✔
323
        }
25✔
324

44✔
325
        const foundTables = await this.loadTables([tableName])
44✔
326

44✔
327
        if (foundTables.length > 0) {
44✔
328
            const foundTablePath = this.getTablePath(foundTables[0])
44✔
329

44✔
330
            const cachedTable = this.loadedTables.find(
44✔
331
                (table) => this.getTablePath(table) === foundTablePath,
44✔
332
            )
44✔
333

44✔
334
            if (!cachedTable) {
44✔
335
                this.cachedTablePaths[tableName] = this.getTablePath(
36✔
336
                    foundTables[0],
36✔
337
                )
36✔
338
                this.loadedTables.push(foundTables[0])
36✔
339
                return foundTables[0]
36✔
340
            } else {
44✔
341
                return cachedTable
8✔
342
            }
8✔
343
        } else {
69!
344
            throw new TypeORMError(`Table "${tableName}" does not exist.`)
×
345
        }
×
346
    }
69✔
347

12,008✔
348
    /**
12,008✔
349
     * Replaces loaded table with given changed table.
12,008✔
350
     * @param table
12,008✔
351
     * @param changedTable
12,008✔
352
     */
12,008✔
353
    protected replaceCachedTable(table: Table, changedTable: Table): void {
12,008✔
354
        const oldTablePath = this.getTablePath(table)
137✔
355
        const foundTable = this.loadedTables.find(
137✔
356
            (loadedTable) => this.getTablePath(loadedTable) === oldTablePath,
137✔
357
        )
137✔
358

137✔
359
        // Clean up the lookup cache..
137✔
360
        for (const [key, cachedPath] of Object.entries(this.cachedTablePaths)) {
137!
361
            if (cachedPath === oldTablePath) {
6✔
362
                this.cachedTablePaths[key] = this.getTablePath(changedTable)
5✔
363
            }
5✔
364
        }
6✔
365

137✔
366
        if (foundTable) {
137✔
367
            foundTable.database = changedTable.database
137✔
368
            foundTable.schema = changedTable.schema
137✔
369
            foundTable.name = changedTable.name
137✔
370
            foundTable.columns = changedTable.columns
137✔
371
            foundTable.indices = changedTable.indices
137✔
372
            foundTable.foreignKeys = changedTable.foreignKeys
137✔
373
            foundTable.uniques = changedTable.uniques
137✔
374
            foundTable.checks = changedTable.checks
137✔
375
            foundTable.justCreated = changedTable.justCreated
137✔
376
            foundTable.engine = changedTable.engine
137✔
377
            foundTable.comment = changedTable.comment
137✔
378
        }
137✔
379
    }
137✔
380

12,008✔
381
    protected getTablePath(
12,008✔
382
        target: EntityMetadata | Table | View | TableForeignKey | string,
3,187✔
383
    ): string {
3,187✔
384
        const parsed = this.connection.driver.parseTableName(target)
3,187✔
385

3,187✔
386
        return this.connection.driver.buildTableName(
3,187✔
387
            parsed.tableName,
3,187✔
388
            parsed.schema,
3,187✔
389
            parsed.database,
3,187✔
390
        )
3,187✔
391
    }
3,187✔
392

12,008✔
393
    protected getTypeormMetadataTableName(): string {
12,008✔
394
        const options = <
1,445✔
395
            SqlServerDataSourceOptions | PostgresDataSourceOptions
1,445✔
396
        >this.connection.driver.options
1,445✔
397
        return this.connection.driver.buildTableName(
1,445✔
398
            this.connection.metadataTableName,
1,445✔
399
            options.schema,
1,445✔
400
            options.database,
1,445✔
401
        )
1,445✔
402
    }
1,445✔
403

12,008✔
404
    /**
12,008✔
405
     * Generates SQL query to select record from typeorm metadata table.
12,008✔
406
     * @param root0
12,008✔
407
     * @param root0.database
12,008✔
408
     * @param root0.schema
12,008✔
409
     * @param root0.table
12,008✔
410
     * @param root0.type
12,008✔
411
     * @param root0.name
12,008✔
412
     */
12,008✔
413
    protected selectTypeormMetadataSql({
12,008✔
414
        database,
51✔
415
        schema,
51✔
416
        table,
51✔
417
        type,
51✔
418
        name,
51✔
419
    }: {
51✔
420
        database?: string
51✔
421
        schema?: string
51✔
422
        table?: string
51✔
423
        type: MetadataTableType
51✔
424
        name: string
51✔
425
    }): Query {
51✔
426
        const qb = this.connection.createQueryBuilder()
51✔
427
        const selectQb = qb
51✔
428
            .select()
51✔
429
            .from(this.getTypeormMetadataTableName(), "t")
51✔
430
            .where(`${qb.escape("type")} = :type`, { type })
51✔
431
            .andWhere(`${qb.escape("name")} = :name`, { name })
51✔
432

51✔
433
        if (database) {
51!
434
            selectQb.andWhere(`${qb.escape("database")} = :database`, {
×
435
                database,
×
436
            })
×
437
        }
×
438

51✔
439
        if (schema) {
51✔
440
            selectQb.andWhere(`${qb.escape("schema")} = :schema`, { schema })
51✔
441
        }
51✔
442

51✔
443
        if (table) {
51✔
444
            selectQb.andWhere(`${qb.escape("table")} = :table`, { table })
51✔
445
        }
51✔
446

51✔
447
        const [query, parameters] = selectQb.getQueryAndParameters()
51✔
448
        return new Query(query, parameters)
51✔
449
    }
51✔
450

12,008✔
451
    /**
12,008✔
452
     * Generates SQL query to insert a record into typeorm metadata table.
12,008✔
453
     * @param root0
12,008✔
454
     * @param root0.database
12,008✔
455
     * @param root0.schema
12,008✔
456
     * @param root0.table
12,008✔
457
     * @param root0.type
12,008✔
458
     * @param root0.name
12,008✔
459
     * @param root0.value
12,008✔
460
     */
12,008✔
461
    protected insertTypeormMetadataSql({
12,008✔
462
        database,
45✔
463
        schema,
45✔
464
        table,
45✔
465
        type,
45✔
466
        name,
45✔
467
        value,
45✔
468
    }: {
45✔
469
        database?: string
45✔
470
        schema?: string
45✔
471
        table?: string
45✔
472
        type: MetadataTableType
45✔
473
        name: string
45✔
474
        value?: string
45✔
475
    }): Query {
45✔
476
        const [query, parameters] = this.connection
45✔
477
            .createQueryBuilder()
45✔
478
            .insert()
45✔
479
            .into(this.getTypeormMetadataTableName())
45✔
480
            .values({
45✔
481
                database: database,
45✔
482
                schema: schema,
45✔
483
                table: table,
45✔
484
                type: type,
45✔
485
                name: name,
45✔
486
                value: value,
45✔
487
            })
45✔
488
            .getQueryAndParameters()
45✔
489

45✔
490
        return new Query(query, parameters)
45✔
491
    }
45✔
492

12,008✔
493
    /**
12,008✔
494
     * Generates SQL query to delete a record from typeorm metadata table.
12,008✔
495
     * @param root0
12,008✔
496
     * @param root0.database
12,008✔
497
     * @param root0.schema
12,008✔
498
     * @param root0.table
12,008✔
499
     * @param root0.type
12,008✔
500
     * @param root0.name
12,008✔
501
     */
12,008✔
502
    protected deleteTypeormMetadataSql({
12,008✔
503
        database,
45✔
504
        schema,
45✔
505
        table,
45✔
506
        type,
45✔
507
        name,
45✔
508
    }: {
45✔
509
        database?: string
45✔
510
        schema?: string
45✔
511
        table?: string
45✔
512
        type: MetadataTableType
45✔
513
        name: string
45✔
514
    }): Query {
45✔
515
        const qb = this.connection.createQueryBuilder()
45✔
516
        const deleteQb = qb
45✔
517
            .delete()
45✔
518
            .from(this.getTypeormMetadataTableName())
45✔
519
            .where(`${qb.escape("type")} = :type`, { type })
45✔
520
            .andWhere(`${qb.escape("name")} = :name`, { name })
45✔
521

45✔
522
        if (database) {
45!
523
            deleteQb.andWhere(`${qb.escape("database")} = :database`, {
×
524
                database,
×
525
            })
×
526
        }
×
527

45✔
528
        if (schema) {
45✔
529
            deleteQb.andWhere(`${qb.escape("schema")} = :schema`, { schema })
45✔
530
        }
45✔
531

45✔
532
        if (table) {
45!
533
            deleteQb.andWhere(`${qb.escape("table")} = :table`, { table })
39✔
534
        }
39✔
535

45✔
536
        const [query, parameters] = deleteQb.getQueryAndParameters()
45✔
537
        return new Query(query, parameters)
45✔
538
    }
45✔
539

12,008✔
540
    /**
12,008✔
541
     * Checks if at least one of column properties was changed.
12,008✔
542
     * Does not checks column type, length and autoincrement, because these properties changes separately.
12,008✔
543
     * @param oldColumn
12,008✔
544
     * @param newColumn
12,008✔
545
     * @param checkDefault
12,008✔
546
     * @param checkComment
12,008✔
547
     * @param checkEnum
12,008✔
548
     */
12,008✔
549
    protected isColumnChanged(
12,008✔
550
        oldColumn: TableColumn,
×
551
        newColumn: TableColumn,
×
552
        checkDefault?: boolean,
×
553
        checkComment?: boolean,
×
554
        checkEnum = true,
×
555
    ): boolean {
×
556
        // this logs need to debug issues in column change detection. Do not delete it!
×
557

×
558
        // console.log("charset ---------------");
×
559
        // console.log(oldColumn.charset !== newColumn.charset);
×
560
        // console.log(oldColumn.charset, newColumn.charset);
×
561
        // console.log("collation ---------------");
×
562
        // console.log(oldColumn.collation !== newColumn.collation);
×
563
        // console.log(oldColumn.collation, newColumn.collation);
×
564
        // console.log("precision ---------------");
×
565
        // console.log(oldColumn.precision !== newColumn.precision);
×
566
        // console.log(oldColumn.precision, newColumn.precision);
×
567
        // console.log("scale ---------------");
×
568
        // console.log(oldColumn.scale !== newColumn.scale);
×
569
        // console.log(oldColumn.scale, newColumn.scale);
×
570
        // console.log("default ---------------");
×
571
        // console.log((checkDefault && oldColumn.default !== newColumn.default));
×
572
        // console.log(oldColumn.default, newColumn.default);
×
573
        // console.log("isNullable ---------------");
×
574
        // console.log(oldColumn.isNullable !== newColumn.isNullable);
×
575
        // console.log(oldColumn.isNullable, newColumn.isNullable);
×
576
        // console.log("comment ---------------");
×
577
        // console.log((checkComment && oldColumn.comment !== newColumn.comment));
×
578
        // console.log(oldColumn.comment, newColumn.comment);
×
579
        // console.log("enum ---------------");
×
580
        // console.log(!OrmUtils.isArraysEqual(oldColumn.enum || [], newColumn.enum || []));
×
581
        // console.log(oldColumn.enum, newColumn.enum);
×
582

×
583
        return (
×
584
            oldColumn.charset !== newColumn.charset ||
×
585
            oldColumn.collation !== newColumn.collation ||
×
586
            oldColumn.precision !== newColumn.precision ||
×
587
            oldColumn.scale !== newColumn.scale ||
×
588
            oldColumn.unsigned !== newColumn.unsigned || // MySQL only
×
589
            oldColumn.asExpression !== newColumn.asExpression ||
×
590
            (checkDefault && oldColumn.default !== newColumn.default) ||
×
591
            oldColumn.onUpdate !== newColumn.onUpdate || // MySQL only
×
592
            oldColumn.isNullable !== newColumn.isNullable ||
×
593
            (checkComment && oldColumn.comment !== newColumn.comment) ||
×
594
            (checkEnum && this.isEnumChanged(oldColumn, newColumn))
×
595
        )
×
596
    }
×
597

12,008✔
598
    protected isEnumChanged(oldColumn: TableColumn, newColumn: TableColumn) {
12,008✔
599
        return !OrmUtils.isArraysEqual(
×
600
            oldColumn.enum || [],
×
601
            newColumn.enum || [],
×
602
        )
×
603
    }
×
604

12,008✔
605
    /**
12,008✔
606
     * Checks if column length is by default.
12,008✔
607
     * @param table
12,008✔
608
     * @param column
12,008✔
609
     * @param length
12,008✔
610
     */
12,008✔
611
    protected isDefaultColumnLength(
12,008✔
612
        table: Table,
82✔
613
        column: TableColumn,
82✔
614
        length: string,
82✔
615
    ): boolean {
82✔
616
        // if table have metadata, we check if length is specified in column metadata
82✔
617
        if (this.connection.hasMetadata(table.name)) {
82✔
618
            const metadata = this.connection.getMetadata(table.name)
45✔
619
            const columnMetadata = metadata.findColumnWithDatabaseName(
45✔
620
                column.name,
45✔
621
            )
45✔
622

45✔
623
            if (columnMetadata) {
45!
624
                const columnMetadataLength =
42✔
625
                    this.connection.driver.getColumnLength(columnMetadata)
42✔
626
                if (columnMetadataLength) return false
42✔
627
            }
42✔
628
        }
45✔
629

42✔
630
        if (
42✔
631
            this.connection.driver.dataTypeDefaults &&
42✔
632
            this.connection.driver.dataTypeDefaults[column.type] &&
82!
633
            this.connection.driver.dataTypeDefaults[column.type].length
2✔
634
        ) {
82!
635
            return (
2✔
636
                this.connection.driver.dataTypeDefaults[
2✔
637
                    column.type
2✔
638
                ].length!.toString() === length.toString()
2✔
639
            )
2✔
640
        }
2✔
641

40✔
642
        return false
40✔
643
    }
40✔
644

12,008✔
645
    /**
12,008✔
646
     * Checks if column precision is by default.
12,008✔
647
     * @param table
12,008✔
648
     * @param column
12,008✔
649
     * @param precision
12,008✔
650
     */
12,008✔
651
    protected isDefaultColumnPrecision(
12,008✔
652
        table: Table,
7✔
653
        column: TableColumn,
7✔
654
        precision: number,
7✔
655
    ): boolean {
7✔
656
        // if table have metadata, we check if length is specified in column metadata
7✔
657
        if (this.connection.hasMetadata(table.name)) {
7✔
658
            const metadata = this.connection.getMetadata(table.name)
7✔
659
            const columnMetadata = metadata.findColumnWithDatabaseName(
7✔
660
                column.name,
7✔
661
            )
7✔
662
            if (
7✔
663
                columnMetadata &&
7✔
664
                columnMetadata.precision !== null &&
7✔
665
                columnMetadata.precision !== undefined
7✔
666
            )
7✔
667
                return false
7✔
668
        }
7✔
669

×
670
        if (
×
671
            this.connection.driver.dataTypeDefaults &&
×
672
            this.connection.driver.dataTypeDefaults[column.type] &&
7!
673
            this.connection.driver.dataTypeDefaults[column.type].precision !==
×
674
                null &&
7!
675
            this.connection.driver.dataTypeDefaults[column.type].precision !==
×
676
                undefined
×
677
        )
7✔
678
            return (
7!
679
                this.connection.driver.dataTypeDefaults[column.type]
×
680
                    .precision === precision
×
681
            )
×
682

×
683
        return false
×
684
    }
×
685

12,008✔
686
    /**
12,008✔
687
     * Checks if column scale is by default.
12,008✔
688
     * @param table
12,008✔
689
     * @param column
12,008✔
690
     * @param scale
12,008✔
691
     */
12,008✔
692
    protected isDefaultColumnScale(
12,008✔
693
        table: Table,
7✔
694
        column: TableColumn,
7✔
695
        scale: number,
7✔
696
    ): boolean {
7✔
697
        // if table have metadata, we check if length is specified in column metadata
7✔
698
        if (this.connection.hasMetadata(table.name)) {
7✔
699
            const metadata = this.connection.getMetadata(table.name)
7✔
700
            const columnMetadata = metadata.findColumnWithDatabaseName(
7✔
701
                column.name,
7✔
702
            )
7✔
703
            if (
7✔
704
                columnMetadata &&
7✔
705
                columnMetadata.scale !== null &&
7✔
706
                columnMetadata.scale !== undefined
7✔
707
            )
7✔
708
                return false
7✔
709
        }
7✔
710

1!
711
        if (
1✔
712
            this.connection.driver.dataTypeDefaults &&
1✔
713
            this.connection.driver.dataTypeDefaults[column.type] &&
7!
714
            this.connection.driver.dataTypeDefaults[column.type].scale !==
×
715
                null &&
7!
716
            this.connection.driver.dataTypeDefaults[column.type].scale !==
×
717
                undefined
×
718
        )
7✔
719
            return (
7!
720
                this.connection.driver.dataTypeDefaults[column.type].scale ===
×
721
                scale
×
722
            )
×
723

1✔
724
        return false
1✔
725
    }
1✔
726

12,008✔
727
    /**
12,008✔
728
     * Executes sql used special for schema build.
12,008✔
729
     * @param upQueries
12,008✔
730
     * @param downQueries
12,008✔
731
     */
12,008✔
732
    protected async executeQueries(
12,008✔
733
        upQueries: Query | Query[],
7,192✔
734
        downQueries: Query | Query[],
7,192✔
735
    ): Promise<void> {
7,192✔
736
        if (InstanceChecker.isQuery(upQueries)) upQueries = [upQueries]
7,192!
737
        if (InstanceChecker.isQuery(downQueries)) downQueries = [downQueries]
7,192!
738

7,192✔
739
        this.sqlInMemory.upQueries.push(...upQueries)
7,192✔
740
        this.sqlInMemory.downQueries.push(...downQueries)
7,192✔
741

7,192✔
742
        // if sql-in-memory mode is enabled then simply store sql in memory and return
7,192✔
743
        if (this.sqlMemoryMode === true)
7,192✔
744
            return Promise.resolve() as Promise<any>
7,192!
745

7,168✔
746
        for (const { query, parameters } of upQueries) {
7,192✔
747
            await this.query(query, parameters)
12,640✔
748
        }
12,635✔
749
    }
7,163✔
750

12,008✔
751
    /**
12,008✔
752
     * Generated an index name for a table and index
12,008✔
753
     * @param table
12,008✔
754
     * @param index
12,008✔
755
     */
12,008✔
756
    protected generateIndexName(
12,008✔
757
        table: Table | View,
4✔
758
        index: TableIndex,
4✔
759
    ): string {
4✔
760
        // new index may be passed without name. In this case we generate index name manually.
4✔
761
        return this.connection.namingStrategy.indexName(
4✔
762
            table,
4✔
763
            index.columnNames,
4✔
764
            index.where,
4✔
765
        )
4✔
766
    }
4✔
767
}
12,008✔
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