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

typeorm / typeorm / 21049157283

15 Jan 2026 10:52PM UTC coverage: 80.888% (+0.002%) from 80.886%
21049157283

push

github

web-flow
fix(sqlite): handle simple-enum arrays correctly (#11865)

27180 of 33045 branches covered (82.25%)

Branch coverage included in aggregate %.

39 of 41 new or added lines in 3 files covered. (95.12%)

1 existing line in 1 file now uncovered.

91759 of 113996 relevant lines covered (80.49%)

71014.56 hits per line

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

94.4
/src/driver/sqlite-abstract/AbstractSqliteDriver.ts
1
import { Driver } from "../Driver"
27✔
2
import { ObjectLiteral } from "../../common/ObjectLiteral"
27✔
3
import { ColumnMetadata } from "../../metadata/ColumnMetadata"
27✔
4
import { DateUtils } from "../../util/DateUtils"
27✔
5
import { DataSource } from "../../data-source/DataSource"
27✔
6
import { RdbmsSchemaBuilder } from "../../schema-builder/RdbmsSchemaBuilder"
27✔
7
import { CteCapabilities } from "../types/CteCapabilities"
27✔
8
import { MappedColumnTypes } from "../types/MappedColumnTypes"
27✔
9
import { ColumnType } from "../types/ColumnTypes"
27✔
10
import { QueryRunner } from "../../query-runner/QueryRunner"
27✔
11
import { DataTypeDefaults } from "../types/DataTypeDefaults"
27✔
12
import { TableColumn } from "../../schema-builder/table/TableColumn"
27✔
13
import { BaseDataSourceOptions } from "../../data-source/BaseDataSourceOptions"
27✔
14
import { EntityMetadata } from "../../metadata/EntityMetadata"
27✔
15
import { OrmUtils } from "../../util/OrmUtils"
27✔
16
import { ApplyValueTransformers } from "../../util/ApplyValueTransformers"
27✔
17
import { ReplicationMode } from "../types/ReplicationMode"
27✔
18
import { DriverUtils } from "../DriverUtils"
27✔
19
import { TypeORMError } from "../../error"
27✔
20
import { Table } from "../../schema-builder/table/Table"
27✔
21
import { View } from "../../schema-builder/view/View"
27✔
22
import { TableForeignKey } from "../../schema-builder/table/TableForeignKey"
27✔
23
import { InstanceChecker } from "../../util/InstanceChecker"
27✔
24
import { UpsertType } from "../types/UpsertType"
27✔
25

27✔
26
type DatabasesMap = Record<
27✔
27
    string,
27✔
28
    {
27✔
29
        attachFilepathAbsolute: string
27✔
30
        attachFilepathRelative: string
27✔
31
        attachHandle: string
27✔
32
    }
27✔
33
>
27✔
34

27✔
35
/**
27✔
36
 * Organizes communication with sqlite DBMS.
27✔
37
 */
27✔
38
export abstract class AbstractSqliteDriver implements Driver {
27✔
39
    // -------------------------------------------------------------------------
27✔
40
    // Public Properties
27✔
41
    // -------------------------------------------------------------------------
27✔
42

27✔
43
    /**
27✔
44
     * Connection used by driver.
27✔
45
     */
27✔
46
    connection: DataSource
27✔
47

27✔
48
    /**
27✔
49
     * Sqlite has a single QueryRunner because it works on a single database connection.
27✔
50
     */
27✔
51
    queryRunner?: QueryRunner
27✔
52

27✔
53
    /**
27✔
54
     * Real database connection with sqlite database.
27✔
55
     */
27✔
56
    databaseConnection: any
27✔
57

27✔
58
    // -------------------------------------------------------------------------
27✔
59
    // Public Implemented Properties
27✔
60
    // -------------------------------------------------------------------------
27✔
61

27✔
62
    /**
27✔
63
     * Connection options.
27✔
64
     */
27✔
65
    options: BaseDataSourceOptions
27✔
66

27✔
67
    /**
27✔
68
     * Master database used to perform all write queries.
27✔
69
     */
27✔
70
    database?: string
27✔
71

27✔
72
    /**
27✔
73
     * Indicates if replication is enabled.
27✔
74
     */
27✔
75
    isReplicated: boolean = false
27✔
76

27✔
77
    /**
27✔
78
     * SQLite underlying library.
27✔
79
     */
27✔
80
    sqlite: any
27✔
81

27✔
82
    /**
27✔
83
     * Indicates if tree tables are supported by this driver.
27✔
84
     */
27✔
85
    treeSupport = true
27✔
86

27✔
87
    /**
27✔
88
     * Represent transaction support by this driver
27✔
89
     */
27✔
90
    transactionSupport: "simple" | "nested" | "none" = "nested"
27✔
91

27✔
92
    /**
27✔
93
     * Gets list of supported column data types by a driver.
27✔
94
     *
27✔
95
     * @see https://www.tutorialspoint.com/sqlite/sqlite_data_types.htm
27✔
96
     * @see https://sqlite.org/datatype3.html
27✔
97
     */
27✔
98
    supportedDataTypes: ColumnType[] = [
27✔
99
        "int",
27✔
100
        "integer",
27✔
101
        "tinyint",
27✔
102
        "smallint",
27✔
103
        "mediumint",
27✔
104
        "bigint",
27✔
105
        "unsigned big int",
27✔
106
        "int2",
27✔
107
        "int8",
27✔
108
        "integer",
27✔
109
        "character",
27✔
110
        "varchar",
27✔
111
        "varying character",
27✔
112
        "nchar",
27✔
113
        "native character",
27✔
114
        "nvarchar",
27✔
115
        "text",
27✔
116
        "clob",
27✔
117
        "text",
27✔
118
        "blob",
27✔
119
        "real",
27✔
120
        "double",
27✔
121
        "double precision",
27✔
122
        "float",
27✔
123
        "real",
27✔
124
        "numeric",
27✔
125
        "decimal",
27✔
126
        "boolean",
27✔
127
        "date",
27✔
128
        "time",
27✔
129
        "datetime",
27✔
130
        "json",
27✔
131
    ]
27✔
132

27✔
133
    /**
27✔
134
     * Returns type of upsert supported by driver if any
27✔
135
     */
27✔
136
    supportedUpsertTypes: UpsertType[] = ["on-conflict-do-update"]
27✔
137

27✔
138
    /**
27✔
139
     * Gets list of column data types that support length by a driver.
27✔
140
     */
27✔
141
    withLengthColumnTypes: ColumnType[] = [
27✔
142
        "character",
27✔
143
        "varchar",
27✔
144
        "varying character",
27✔
145
        "nchar",
27✔
146
        "native character",
27✔
147
        "nvarchar",
27✔
148
        "text",
27✔
149
        "blob",
27✔
150
        "clob",
27✔
151
    ]
27✔
152

27✔
153
    /**
27✔
154
     * Gets list of spatial column data types.
27✔
155
     */
27✔
156
    spatialTypes: ColumnType[] = []
27✔
157

27✔
158
    /**
27✔
159
     * Gets list of column data types that support precision by a driver.
27✔
160
     */
27✔
161
    withPrecisionColumnTypes: ColumnType[] = [
27✔
162
        "real",
27✔
163
        "double",
27✔
164
        "double precision",
27✔
165
        "float",
27✔
166
        "real",
27✔
167
        "numeric",
27✔
168
        "decimal",
27✔
169
        "date",
27✔
170
        "time",
27✔
171
        "datetime",
27✔
172
    ]
27✔
173

27✔
174
    /**
27✔
175
     * Gets list of column data types that support scale by a driver.
27✔
176
     */
27✔
177
    withScaleColumnTypes: ColumnType[] = [
27✔
178
        "real",
27✔
179
        "double",
27✔
180
        "double precision",
27✔
181
        "float",
27✔
182
        "real",
27✔
183
        "numeric",
27✔
184
        "decimal",
27✔
185
    ]
27✔
186

27✔
187
    /**
27✔
188
     * Orm has special columns and we need to know what database column types should be for those types.
27✔
189
     * Column types are driver dependant.
27✔
190
     */
27✔
191
    mappedDataTypes: MappedColumnTypes = {
27✔
192
        createDate: "datetime",
27✔
193
        createDateDefault: "datetime('now')",
27✔
194
        updateDate: "datetime",
27✔
195
        updateDateDefault: "datetime('now')",
27✔
196
        deleteDate: "datetime",
27✔
197
        deleteDateNullable: true,
27✔
198
        version: "integer",
27✔
199
        treeLevel: "integer",
27✔
200
        migrationId: "integer",
27✔
201
        migrationName: "varchar",
27✔
202
        migrationTimestamp: "bigint",
27✔
203
        cacheId: "int",
27✔
204
        cacheIdentifier: "varchar",
27✔
205
        cacheTime: "bigint",
27✔
206
        cacheDuration: "int",
27✔
207
        cacheQuery: "text",
27✔
208
        cacheResult: "text",
27✔
209
        metadataType: "varchar",
27✔
210
        metadataDatabase: "varchar",
27✔
211
        metadataSchema: "varchar",
27✔
212
        metadataTable: "varchar",
27✔
213
        metadataName: "varchar",
27✔
214
        metadataValue: "text",
27✔
215
    }
27✔
216

27✔
217
    /**
27✔
218
     * Default values of length, precision and scale depends on column data type.
27✔
219
     * Used in the cases when length/precision/scale is not specified by user.
27✔
220
     */
27✔
221
    dataTypeDefaults: DataTypeDefaults
27✔
222

27✔
223
    /**
27✔
224
     * No documentation specifying a maximum length for identifiers could be found
27✔
225
     * for SQLite.
27✔
226
     */
27✔
227
    maxAliasLength?: number
27✔
228

27✔
229
    cteCapabilities: CteCapabilities = {
27✔
230
        enabled: true,
27✔
231
        requiresRecursiveHint: true,
27✔
232
    }
27✔
233

27✔
234
    // -------------------------------------------------------------------------
27✔
235
    // Protected Properties
27✔
236
    // -------------------------------------------------------------------------
27✔
237

27✔
238
    /**
27✔
239
     * Any attached databases (excepting default 'main')
27✔
240
     */
27✔
241
    attachedDatabases: DatabasesMap = {}
27✔
242

27✔
243
    // -------------------------------------------------------------------------
27✔
244
    // Constructor
27✔
245
    // -------------------------------------------------------------------------
27✔
246

27✔
247
    constructor(connection: DataSource) {
27✔
248
        this.connection = connection
4,224✔
249
        this.options = connection.options as BaseDataSourceOptions
4,224✔
250

4,224✔
251
        this.database = DriverUtils.buildDriverOptions(this.options).database
4,224✔
252
    }
4,224✔
253

27✔
254
    // -------------------------------------------------------------------------
27✔
255
    // Public Abstract
27✔
256
    // -------------------------------------------------------------------------
27✔
257

27✔
258
    /**
27✔
259
     * Creates a query runner used to execute database queries.
27✔
260
     */
27✔
261
    abstract createQueryRunner(mode: ReplicationMode): QueryRunner
27✔
262

27✔
263
    // -------------------------------------------------------------------------
27✔
264
    // Public Methods
27✔
265
    // -------------------------------------------------------------------------
27✔
266

27✔
267
    /**
27✔
268
     * Performs connection to the database.
27✔
269
     */
27✔
270
    async connect(): Promise<void> {
27✔
271
        this.databaseConnection = await this.createDatabaseConnection()
2,928✔
272
    }
2,928✔
273

27✔
274
    /**
27✔
275
     * Makes any action after connection (e.g. create extensions in Postgres driver).
27✔
276
     */
27✔
277
    afterConnect(): Promise<void> {
27✔
278
        return Promise.resolve()
1,290✔
279
    }
1,290✔
280

27✔
281
    /**
27✔
282
     * Closes connection with database.
27✔
283
     */
27✔
284
    async disconnect(): Promise<void> {
27✔
285
        return new Promise<void>((ok, fail) => {
×
286
            this.queryRunner = undefined
×
287
            this.databaseConnection.close((err: any) =>
×
288
                err ? fail(err) : ok(),
×
289
            )
×
290
        })
×
291
    }
×
292

27✔
293
    hasAttachedDatabases(): boolean {
27✔
294
        return !!Object.keys(this.attachedDatabases).length
×
295
    }
×
296

27✔
297
    getAttachedDatabaseHandleByRelativePath(path: string): string | undefined {
27✔
298
        return this.attachedDatabases?.[path]?.attachHandle
2,015,799✔
299
    }
2,015,799✔
300

27✔
301
    getAttachedDatabasePathRelativeByHandle(
27✔
302
        handle: string,
981✔
303
    ): string | undefined {
981✔
304
        return Object.values(this.attachedDatabases).find(
981✔
305
            ({ attachHandle }) => handle === attachHandle,
981✔
306
        )?.attachFilepathRelative
981✔
307
    }
981✔
308

27✔
309
    /**
27✔
310
     * Creates a schema builder used to build and sync a schema.
27✔
311
     */
27✔
312
    createSchemaBuilder() {
27✔
313
        return new RdbmsSchemaBuilder(this.connection)
12,687✔
314
    }
12,687✔
315

27✔
316
    /**
27✔
317
     * Prepares given value to a value to be persisted, based on its column type and metadata.
27✔
318
     */
27✔
319
    preparePersistentValue(value: any, columnMetadata: ColumnMetadata): any {
27✔
320
        if (columnMetadata.transformer)
345,477✔
321
            value = ApplyValueTransformers.transformTo(
345,477✔
322
                columnMetadata.transformer,
417✔
323
                value,
417✔
324
            )
417✔
325

345,477✔
326
        if (value === null || value === undefined) return value
345,477✔
327

287,859✔
328
        if (
287,859✔
329
            columnMetadata.type === Boolean ||
287,859✔
330
            columnMetadata.type === "boolean"
269,742✔
331
        ) {
345,477✔
332
            return value === true ? 1 : 0
18,135✔
333
        } else if (columnMetadata.type === "date") {
345,477✔
334
            return DateUtils.mixedDateToDateString(value, {
39✔
335
                utc: columnMetadata.utc,
39✔
336
            })
39✔
337
        } else if (columnMetadata.type === "time") {
269,724!
338
            return DateUtils.mixedDateToTimeString(value)
12✔
339
        } else if (
269,685✔
340
            columnMetadata.type === "datetime" ||
269,673✔
341
            columnMetadata.type === Date
269,589✔
342
        ) {
269,673✔
343
            // to string conversation needs because SQLite stores date as integer number, when date came as Object
309✔
344
            // TODO: think about `toUTC` conversion
309✔
345
            return DateUtils.mixedDateToUtcDatetimeString(value)
309✔
346
        } else if (
269,673✔
347
            columnMetadata.type === "json" ||
269,364✔
348
            columnMetadata.type === "simple-json"
269,358✔
349
        ) {
269,364✔
350
            return DateUtils.simpleJsonToString(value)
48✔
351
        } else if (columnMetadata.type === "simple-array") {
269,364!
352
            return DateUtils.simpleArrayToString(value)
6✔
353
        } else if (columnMetadata.type === "simple-enum") {
269,316!
354
            return DateUtils.simpleEnumToString(value)
102✔
355
        }
102✔
356

269,208✔
357
        return value
269,208✔
358
    }
269,208✔
359

27✔
360
    /**
27✔
361
     * Prepares given value to a value to be hydrated, based on its column type or metadata.
27✔
362
     */
27✔
363
    prepareHydratedValue(value: any, columnMetadata: ColumnMetadata): any {
27✔
364
        if (value === null || value === undefined)
598,803✔
365
            return columnMetadata.transformer
598,803✔
366
                ? ApplyValueTransformers.transformFrom(
46,665✔
367
                      columnMetadata.transformer,
108✔
368
                      value,
108✔
369
                  )
46,665✔
370
                : value
46,665✔
371

552,138✔
372
        if (
552,138✔
373
            columnMetadata.type === Boolean ||
552,138✔
374
            columnMetadata.type === "boolean"
545,727✔
375
        ) {
598,803✔
376
            value = value ? true : false
6,417✔
377
        } else if (
598,803✔
378
            columnMetadata.type === "datetime" ||
545,721✔
379
            columnMetadata.type === Date
541,671✔
380
        ) {
545,721✔
381
            /**
4,413✔
382
             * Fix date conversion issue
4,413✔
383
             *
4,413✔
384
             * If the format of the date string is "2018-03-14 02:33:33.906", Safari (and iOS WKWebView) will convert it to an invalid date object.
4,413✔
385
             * We need to modify the date string to "2018-03-14T02:33:33.906Z" and Safari will convert it correctly.
4,413✔
386
             *
4,413✔
387
             * ISO 8601
4,413✔
388
             * https://www.w3.org/TR/NOTE-datetime
4,413✔
389
             */
4,413✔
390
            if (value && typeof value === "string") {
4,413✔
391
                // There are various valid time string formats a sqlite time string might have:
2,919✔
392
                // https://www.sqlite.org/lang_datefunc.html
2,919✔
393
                // There are two separate fixes we may need to do:
2,919✔
394
                //   1) Add 'T' separator if space is used instead
2,919✔
395
                //   2) Add 'Z' UTC suffix if no timezone or offset specified
2,919✔
396

2,919✔
397
                if (/^\d\d\d\d-\d\d-\d\d \d\d:\d\d/.test(value)) {
2,919✔
398
                    value = value.replace(" ", "T")
2,910✔
399
                }
2,910✔
400
                if (
2,919✔
401
                    /^\d\d\d\d-\d\d-\d\dT\d\d:\d\d(:\d\d(\.\d\d\d)?)?$/.test(
2,919✔
402
                        value,
2,919✔
403
                    )
2,919✔
404
                ) {
2,919✔
405
                    value += "Z"
2,910✔
406
                }
2,910✔
407
            }
2,919✔
408

4,413✔
409
            value = DateUtils.normalizeHydratedDate(value)
4,413✔
410
        } else if (columnMetadata.type === "date") {
545,721✔
411
            value = DateUtils.mixedDateToDateString(value, {
45✔
412
                utc: columnMetadata.utc,
45✔
413
            })
45✔
414
        } else if (columnMetadata.type === "time") {
541,308!
415
            value = DateUtils.mixedTimeToString(value)
12✔
416
        } else if (
541,263✔
417
            columnMetadata.type === "json" ||
541,251✔
418
            columnMetadata.type === "simple-json"
541,245✔
419
        ) {
541,251✔
420
            value = DateUtils.stringToSimpleJson(value)
60✔
421
        } else if (columnMetadata.type === "simple-array") {
541,251!
422
            value = DateUtils.stringToSimpleArray(value)
6✔
423
        } else if (columnMetadata.type === "simple-enum") {
541,191!
424
            value = DateUtils.stringToSimpleEnum(value, columnMetadata)
492✔
425
        } else if (columnMetadata.type === Number) {
541,185✔
426
            // convert to number if number
130,038✔
427
            value = !isNaN(+value) ? parseInt(value) : value
130,038!
428
        }
130,038✔
429

552,138✔
430
        if (columnMetadata.transformer)
552,138✔
431
            value = ApplyValueTransformers.transformFrom(
598,803✔
432
                columnMetadata.transformer,
555✔
433
                value,
555✔
434
            )
555✔
435

552,138✔
436
        return value
552,138✔
437
    }
552,138✔
438

27✔
439
    /**
27✔
440
     * Replaces parameters in the given sql with special escaping character
27✔
441
     * and an array of parameter names to be passed to a query.
27✔
442
     */
27✔
443
    escapeQueryWithParameters(
27✔
444
        sql: string,
154,365✔
445
        parameters: ObjectLiteral,
154,365✔
446
        nativeParameters: ObjectLiteral,
154,365✔
447
    ): [string, any[]] {
154,365✔
448
        const escapedParameters: any[] = Object.keys(nativeParameters).map(
154,365✔
449
            (key) => {
154,365✔
450
                // Mapping boolean values to their numeric representation
12✔
451
                if (typeof nativeParameters[key] === "boolean") {
12✔
452
                    return nativeParameters[key] === true ? 1 : 0
12✔
453
                }
12✔
454

×
455
                if (nativeParameters[key] instanceof Date) {
×
456
                    return DateUtils.mixedDateToUtcDatetimeString(
×
457
                        nativeParameters[key],
×
458
                    )
×
459
                }
×
460

×
461
                return nativeParameters[key]
×
462
            },
154,365✔
463
        )
154,365✔
464

154,365✔
465
        if (!parameters || !Object.keys(parameters).length)
154,365✔
466
            return [sql, escapedParameters]
154,365✔
467

148,089✔
468
        sql = sql.replace(
148,089✔
469
            /:(\.\.\.)?([A-Za-z0-9_.]+)/g,
148,089✔
470
            (full, isArray: string, key: string): string => {
148,089✔
471
                if (!parameters.hasOwnProperty(key)) {
401,505!
472
                    return full
×
473
                }
×
474

401,505✔
475
                const value: any = parameters[key]
401,505✔
476

401,505✔
477
                if (isArray) {
401,505✔
478
                    return value
1,044✔
479
                        .map((v: any) => {
1,044✔
480
                            escapedParameters.push(v)
1,467✔
481
                            return this.createParameter(
1,467✔
482
                                key,
1,467✔
483
                                escapedParameters.length - 1,
1,467✔
484
                            )
1,467✔
485
                        })
1,044✔
486
                        .join(", ")
1,044✔
487
                }
1,044✔
488

400,461✔
489
                if (typeof value === "function") {
401,505!
490
                    return value()
×
491
                } else if (typeof value === "number") {
401,505✔
492
                    return String(value)
243,045✔
493
                }
243,045✔
494

157,416✔
495
                // Sqlite does not have a boolean data type so we have to transform
157,416✔
496
                // it to 1 or 0
157,416✔
497
                if (typeof value === "boolean") {
401,505✔
498
                    escapedParameters.push(+value)
1,401✔
499
                    return this.createParameter(
1,401✔
500
                        key,
1,401✔
501
                        escapedParameters.length - 1,
1,401✔
502
                    )
1,401✔
503
                }
1,401✔
504

156,015✔
505
                if (value instanceof Date) {
401,505✔
506
                    escapedParameters.push(
42✔
507
                        DateUtils.mixedDateToUtcDatetimeString(value),
42✔
508
                    )
42✔
509
                    return this.createParameter(
42✔
510
                        key,
42✔
511
                        escapedParameters.length - 1,
42✔
512
                    )
42✔
513
                }
42✔
514

155,973✔
515
                escapedParameters.push(value)
155,973✔
516
                return this.createParameter(key, escapedParameters.length - 1)
155,973✔
517
            },
148,089✔
518
        ) // todo: make replace only in value statements, otherwise problems
148,089✔
519
        return [sql, escapedParameters]
148,089✔
520
    }
148,089✔
521

27✔
522
    /**
27✔
523
     * Escapes a column name.
27✔
524
     */
27✔
525
    escape(columnName: string): string {
27✔
526
        return '"' + columnName + '"'
1,369,260✔
527
    }
1,369,260✔
528

27✔
529
    /**
27✔
530
     * Build full table name with database name, schema name and table name.
27✔
531
     * E.g. myDB.mySchema.myTable
27✔
532
     *
27✔
533
     * Returns only simple table name because all inherited drivers does not supports schema and database.
27✔
534
     */
27✔
535
    buildTableName(
27✔
536
        tableName: string,
969,099✔
537
        schema?: string,
969,099✔
538
        database?: string,
969,099✔
539
    ): string {
969,099✔
540
        return tableName
969,099✔
541
    }
969,099✔
542

27✔
543
    /**
27✔
544
     * Parse a target table name or other types and return a normalized table definition.
27✔
545
     */
27✔
546
    parseTableName(
27✔
547
        target: EntityMetadata | Table | View | TableForeignKey | string,
4,418,355✔
548
    ): { database?: string; schema?: string; tableName: string } {
4,418,355✔
549
        const driverDatabase = this.database
4,418,355✔
550
        const driverSchema = undefined
4,418,355✔
551

4,418,355✔
552
        if (InstanceChecker.isTable(target) || InstanceChecker.isView(target)) {
4,418,355✔
553
            const parsed = this.parseTableName(
1,472,271✔
554
                target.schema
1,472,271✔
555
                    ? `"${target.schema}"."${target.name}"`
1,472,271!
556
                    : target.name,
1,472,271✔
557
            )
1,472,271✔
558

1,472,271✔
559
            return {
1,472,271✔
560
                database: target.database || parsed.database || driverDatabase,
1,472,271!
561
                schema: target.schema || parsed.schema || driverSchema,
1,472,271✔
562
                tableName: parsed.tableName,
1,472,271✔
563
            }
1,472,271✔
564
        }
1,472,271✔
565

2,946,084✔
566
        if (InstanceChecker.isTableForeignKey(target)) {
4,418,355✔
567
            const parsed = this.parseTableName(target.referencedTableName)
1,959✔
568

1,959✔
569
            return {
1,959✔
570
                database:
1,959✔
571
                    target.referencedDatabase ||
1,959✔
572
                    parsed.database ||
1,959!
573
                    driverDatabase,
1,959✔
574
                schema:
1,959✔
575
                    target.referencedSchema || parsed.schema || driverSchema,
1,959✔
576
                tableName: parsed.tableName,
1,959✔
577
            }
1,959✔
578
        }
1,959✔
579

2,944,125✔
580
        if (InstanceChecker.isEntityMetadata(target)) {
4,418,355✔
581
            // EntityMetadata tableName is never a path
1,469,895✔
582

1,469,895✔
583
            return {
1,469,895✔
584
                database: target.database || driverDatabase,
1,469,895✔
585
                schema: target.schema || driverSchema,
1,469,895✔
586
                tableName: target.tableName,
1,469,895✔
587
            }
1,469,895✔
588
        }
1,469,895✔
589

1,474,230✔
590
        const parts = target.split(".")
1,474,230✔
591

1,474,230✔
592
        if (parts.length === 3) {
4,418,355!
593
            return {
×
594
                database: parts[0] || driverDatabase,
×
595
                schema: parts[1] || driverSchema,
×
596
                tableName: parts[2],
×
597
            }
×
598
        } else if (parts.length === 2) {
4,418,355!
599
            const database =
789✔
600
                this.getAttachedDatabasePathRelativeByHandle(parts[0]) ??
789!
601
                driverDatabase
×
602
            return {
789✔
603
                database: database,
789✔
604
                schema: parts[0],
789✔
605
                tableName: parts[1],
789✔
606
            }
789✔
607
        } else {
1,474,230✔
608
            return {
1,473,441✔
609
                database: driverDatabase,
1,473,441✔
610
                schema: driverSchema,
1,473,441✔
611
                tableName: target,
1,473,441✔
612
            }
1,473,441✔
613
        }
1,473,441✔
614
    }
4,418,355✔
615

27✔
616
    /**
27✔
617
     * Creates a database type from a given column metadata.
27✔
618
     */
27✔
619
    normalizeType(column: {
27✔
620
        type?: ColumnType
305,967✔
621
        length?: number | string
305,967✔
622
        precision?: number | null
305,967✔
623
        scale?: number
305,967✔
624
    }): string {
305,967✔
625
        if (column.type === Number || column.type === "int") {
305,967✔
626
            return "integer"
156,162✔
627
        } else if (column.type === String) {
305,967✔
628
            return "varchar"
117,339✔
629
        } else if (column.type === Date) {
149,805✔
630
            return "datetime"
1,179✔
631
        } else if (column.type === Boolean) {
32,466✔
632
            return "boolean"
8,127✔
633
        } else if (column.type === "uuid") {
31,287✔
634
            return "varchar"
4,572✔
635
        } else if (column.type === "simple-array") {
23,160!
636
            return "text"
30✔
637
        } else if (column.type === "simple-json") {
18,588✔
638
            return "text"
210✔
639
        } else if (column.type === "simple-enum") {
18,558!
640
            return "varchar"
636✔
641
        } else {
18,348✔
642
            return (column.type as string) || ""
17,712!
643
        }
17,712✔
644
    }
305,967✔
645

27✔
646
    /**
27✔
647
     * Normalizes "default" value of the column.
27✔
648
     */
27✔
649
    normalizeDefault(columnMetadata: ColumnMetadata): string | undefined {
27✔
650
        const defaultValue = columnMetadata.default
267,864✔
651

267,864✔
652
        if (typeof defaultValue === "number") {
267,864✔
653
            return "" + defaultValue
3,039✔
654
        }
3,039✔
655

264,825✔
656
        if (typeof defaultValue === "boolean") {
267,864✔
657
            return defaultValue ? "1" : "0"
198✔
658
        }
198✔
659

264,627✔
660
        if (typeof defaultValue === "function") {
267,864✔
661
            return defaultValue()
7,059✔
662
        }
7,059✔
663

257,568✔
664
        if (typeof defaultValue === "string") {
267,864✔
665
            return `'${defaultValue}'`
5,019✔
666
        }
5,019✔
667

252,549✔
668
        if (defaultValue === null || defaultValue === undefined) {
267,864✔
669
            return undefined
252,369✔
670
        }
252,369✔
671

180!
672
        if (
180✔
673
            Array.isArray(defaultValue) &&
180✔
674
            columnMetadata.type === "simple-enum"
180✔
675
        ) {
267,864!
676
            return `'${defaultValue.join(",")}'`
180✔
677
        }
180✔
NEW
678

×
679
        return `${defaultValue}`
×
680
    }
×
681

27✔
682
    /**
27✔
683
     * Normalizes "isUnique" value of the column.
27✔
684
     */
27✔
685
    normalizeIsUnique(column: ColumnMetadata): boolean {
27✔
686
        return column.entityMetadata.uniques.some(
270,045✔
687
            (uq) => uq.columns.length === 1 && uq.columns[0] === column,
270,045✔
688
        )
270,045✔
689
    }
270,045✔
690

27✔
691
    /**
27✔
692
     * Calculates column length taking into account the default length values.
27✔
693
     */
27✔
694
    getColumnLength(column: ColumnMetadata): string {
27✔
695
        return column.length ? column.length.toString() : ""
129,537✔
696
    }
129,537✔
697

27✔
698
    /**
27✔
699
     * Normalizes "default" value of the column.
27✔
700
     */
27✔
701
    createFullType(column: TableColumn): string {
27✔
702
        let type = column.type
247,263✔
703
        if (column.enum) {
247,263!
704
            return "varchar"
240✔
705
        }
240✔
706
        if (column.length) {
247,263✔
707
            type += "(" + column.length + ")"
8,154✔
708
        } else if (
247,263✔
709
            column.precision !== null &&
238,869✔
710
            column.precision !== undefined &&
238,869✔
711
            column.scale !== null &&
238,869✔
712
            column.scale !== undefined
78✔
713
        ) {
238,869✔
714
            type += "(" + column.precision + "," + column.scale + ")"
39✔
715
        } else if (
238,869✔
716
            column.precision !== null &&
238,830✔
717
            column.precision !== undefined
238,830✔
718
        ) {
238,830✔
719
            type += "(" + column.precision + ")"
39✔
720
        }
39✔
721

247,023✔
722
        if (column.isArray) type += " array"
247,263!
723

247,023✔
724
        return type
247,023✔
725
    }
247,023✔
726

27✔
727
    /**
27✔
728
     * Obtains a new database connection to a master server.
27✔
729
     * Used for replication.
27✔
730
     * If replication is not setup then returns default connection's database connection.
27✔
731
     */
27✔
732
    obtainMasterConnection(): Promise<any> {
27✔
733
        return Promise.resolve()
×
734
    }
×
735

27✔
736
    /**
27✔
737
     * Obtains a new database connection to a slave server.
27✔
738
     * Used for replication.
27✔
739
     * If replication is not setup then returns master (default) connection's database connection.
27✔
740
     */
27✔
741
    obtainSlaveConnection(): Promise<any> {
27✔
742
        return Promise.resolve()
×
743
    }
×
744

27✔
745
    /**
27✔
746
     * Creates generated map of values generated or returned by database after INSERT query.
27✔
747
     */
27✔
748
    createGeneratedMap(
27✔
749
        metadata: EntityMetadata,
93,291✔
750
        insertResult: any,
93,291✔
751
        entityIndex: number,
93,291✔
752
        entityNum: number,
93,291✔
753
    ) {
93,291✔
754
        const generatedMap = metadata.generatedColumns.reduce(
93,291✔
755
            (map, generatedColumn) => {
93,291✔
756
                let value: any
28,146✔
757
                if (
28,146✔
758
                    generatedColumn.generationStrategy === "increment" &&
28,146✔
759
                    insertResult
27,426✔
760
                ) {
28,146✔
761
                    // NOTE: When INSERT statement is successfully completed, the last inserted row ID is returned.
27,423✔
762
                    // see also: SqliteQueryRunner.query()
27,423✔
763
                    value = insertResult - entityNum + entityIndex + 1
27,423✔
764
                    // } else if (generatedColumn.generationStrategy === "uuid") {
27,423✔
765
                    //     value = insertValue[generatedColumn.databaseName];
27,423✔
766
                }
27,423✔
767

28,146✔
768
                if (!value) return map
28,146✔
769
                return OrmUtils.mergeDeep(
27,423✔
770
                    map,
27,423✔
771
                    generatedColumn.createValueMap(value),
27,423✔
772
                )
27,423✔
773
            },
93,291✔
774
            {} as ObjectLiteral,
93,291✔
775
        )
93,291✔
776

93,291✔
777
        return Object.keys(generatedMap).length > 0 ? generatedMap : undefined
93,291✔
778
    }
93,291✔
779

27✔
780
    /**
27✔
781
     * Differentiate columns of this table and columns from the given column metadatas columns
27✔
782
     * and returns only changed.
27✔
783
     */
27✔
784
    findChangedColumns(
27✔
785
        tableColumns: TableColumn[],
43,764✔
786
        columnMetadatas: ColumnMetadata[],
43,764✔
787
    ): ColumnMetadata[] {
43,764✔
788
        return columnMetadatas.filter((columnMetadata) => {
43,764✔
789
            const tableColumn = tableColumns.find(
135,060✔
790
                (c) => c.name === columnMetadata.databaseName,
135,060✔
791
            )
135,060✔
792
            if (!tableColumn) return false // we don't need new columns, we only need exist and changed
135,060✔
793

135,021✔
794
            const isColumnChanged =
135,021✔
795
                tableColumn.name !== columnMetadata.databaseName ||
135,021✔
796
                tableColumn.type !== this.normalizeType(columnMetadata) ||
135,060✔
797
                tableColumn.length !== columnMetadata.length ||
135,060✔
798
                tableColumn.precision !== columnMetadata.precision ||
135,060✔
799
                tableColumn.scale !== columnMetadata.scale ||
135,060✔
800
                this.normalizeDefault(columnMetadata) !== tableColumn.default ||
135,060✔
801
                tableColumn.isPrimary !== columnMetadata.isPrimary ||
135,060✔
802
                tableColumn.isNullable !== columnMetadata.isNullable ||
135,060✔
803
                tableColumn.generatedType !== columnMetadata.generatedType ||
135,060✔
804
                tableColumn.asExpression !== columnMetadata.asExpression ||
135,060✔
805
                tableColumn.isUnique !==
134,895✔
806
                    this.normalizeIsUnique(columnMetadata) ||
135,060✔
807
                (tableColumn.enum &&
134,886!
808
                    columnMetadata.enum &&
134,886!
809
                    !OrmUtils.isArraysEqual(
255✔
810
                        tableColumn.enum,
255✔
811
                        columnMetadata.enum.map((val) => val + ""),
255✔
812
                    )) ||
135,060✔
813
                (columnMetadata.generationStrategy !== "uuid" &&
134,883✔
814
                    tableColumn.isGenerated !== columnMetadata.isGenerated)
134,883✔
815

135,060✔
816
            // DEBUG SECTION
135,060✔
817
            // if (isColumnChanged) {
135,060✔
818
            //     console.log("table:", columnMetadata.entityMetadata.tableName)
135,060✔
819
            //     console.log(
135,060✔
820
            //         "name:",
135,060✔
821
            //         tableColumn.name,
135,060✔
822
            //         columnMetadata.databaseName,
135,060✔
823
            //     )
135,060✔
824
            //     console.log(
135,060✔
825
            //         "type:",
135,060✔
826
            //         tableColumn.type,
135,060✔
827
            //         this.normalizeType(columnMetadata),
135,060✔
828
            //     )
135,060✔
829
            //     console.log(
135,060✔
830
            //         "length:",
135,060✔
831
            //         tableColumn.length,
135,060✔
832
            //         columnMetadata.length,
135,060✔
833
            //     )
135,060✔
834
            //     console.log(
135,060✔
835
            //         "precision:",
135,060✔
836
            //         tableColumn.precision,
135,060✔
837
            //         columnMetadata.precision,
135,060✔
838
            //     )
135,060✔
839
            //     console.log("scale:", tableColumn.scale, columnMetadata.scale)
135,060✔
840
            //     console.log(
135,060✔
841
            //         "default:",
135,060✔
842
            //         this.normalizeDefault(columnMetadata),
135,060✔
843
            //         columnMetadata.default,
135,060✔
844
            //     )
135,060✔
845
            //     console.log(
135,060✔
846
            //         "isPrimary:",
135,060✔
847
            //         tableColumn.isPrimary,
135,060✔
848
            //         columnMetadata.isPrimary,
135,060✔
849
            //     )
135,060✔
850
            //     console.log(
135,060✔
851
            //         "isNullable:",
135,060✔
852
            //         tableColumn.isNullable,
135,060✔
853
            //         columnMetadata.isNullable,
135,060✔
854
            //     )
135,060✔
855
            //     console.log(
135,060✔
856
            //         "generatedType:",
135,060✔
857
            //         tableColumn.generatedType,
135,060✔
858
            //         columnMetadata.generatedType,
135,060✔
859
            //     )
135,060✔
860
            //     console.log(
135,060✔
861
            //         "asExpression:",
135,060✔
862
            //         tableColumn.asExpression,
135,060✔
863
            //         columnMetadata.asExpression,
135,060✔
864
            //     )
135,060✔
865
            //     console.log(
135,060✔
866
            //         "isUnique:",
135,060✔
867
            //         tableColumn.isUnique,
135,060✔
868
            //         this.normalizeIsUnique(columnMetadata),
135,060✔
869
            //     )
135,060✔
870
            //     console.log(
135,060✔
871
            //         "enum:",
135,060✔
872
            //         tableColumn.enum &&
135,060✔
873
            //             columnMetadata.enum &&
135,060✔
874
            //             !OrmUtils.isArraysEqual(
135,060✔
875
            //                 tableColumn.enum,
135,060✔
876
            //                 columnMetadata.enum.map((val) => val + ""),
135,060✔
877
            //             ),
135,060✔
878
            //     )
135,060✔
879
            //     console.log(
135,060✔
880
            //         "isGenerated:",
135,060✔
881
            //         tableColumn.isGenerated,
135,060✔
882
            //         columnMetadata.isGenerated,
135,060✔
883
            //     )
135,060✔
884
            // }
135,060✔
885

135,060✔
886
            return isColumnChanged
135,060✔
887
        })
43,764✔
888
    }
43,764✔
889

27✔
890
    /**
27✔
891
     * Returns true if driver supports RETURNING / OUTPUT statement.
27✔
892
     */
27✔
893
    isReturningSqlSupported(): boolean {
27✔
894
        return false
187,176✔
895
    }
187,176✔
896

27✔
897
    /**
27✔
898
     * Returns true if driver supports uuid values generation on its own.
27✔
899
     */
27✔
900
    isUUIDGenerationSupported(): boolean {
27✔
901
        return false
1,053✔
902
    }
1,053✔
903

27✔
904
    /**
27✔
905
     * Returns true if driver supports fulltext indices.
27✔
906
     */
27✔
907
    isFullTextColumnTypeSupported(): boolean {
27✔
908
        return false
216✔
909
    }
216✔
910

27✔
911
    /**
27✔
912
     * Creates an escaped parameter.
27✔
913
     */
27✔
914
    createParameter(parameterName: string, index: number): string {
27✔
915
        // return "$" + (index + 1);
160,497✔
916
        return "?"
160,497✔
917
        // return "$" + parameterName;
160,497✔
918
    }
160,497✔
919

27✔
920
    // -------------------------------------------------------------------------
27✔
921
    // Protected Methods
27✔
922
    // -------------------------------------------------------------------------
27✔
923

27✔
924
    /**
27✔
925
     * Creates connection with the database.
27✔
926
     */
27✔
927
    protected async createDatabaseConnection(): Promise<any> {
27✔
928
        throw new TypeORMError(
×
929
            "Do not use AbstractSqlite directly, it has to be used with one of the sqlite drivers",
×
930
        )
×
931
    }
×
932

27✔
933
    /**
27✔
934
     * If driver dependency is not given explicitly, then try to load it via "require".
27✔
935
     */
27✔
936
    protected loadDependencies(): void {
27✔
937
        // dependencies have to be loaded in the specific driver
×
938
    }
×
939
}
27✔
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