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

typeorm / typeorm / 19549987525

20 Nov 2025 08:11PM UTC coverage: 80.769% (+4.3%) from 76.433%
19549987525

push

github

web-flow
ci: run tests on commits to master and next (#11783)

Co-authored-by: Oleg "OSA413" Sokolov <OSA413@users.noreply.github.com>

26500 of 32174 branches covered (82.36%)

Branch coverage included in aggregate %.

91252 of 113615 relevant lines covered (80.32%)

88980.79 hits per line

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

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

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

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

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

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

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

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

26✔
62
    /**
26✔
63
     * Connection options.
26✔
64
     */
26✔
65
    options: ReactNativeConnectionOptions
26✔
66

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

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

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

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

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

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

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

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

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

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

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

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

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

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

26✔
228
    cteCapabilities: CteCapabilities = {
26✔
229
        enabled: true,
26✔
230
        requiresRecursiveHint: true,
26✔
231
    }
26✔
232

26✔
233
    // -------------------------------------------------------------------------
26✔
234
    // Protected Properties
26✔
235
    // -------------------------------------------------------------------------
26✔
236

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

26✔
242
    // -------------------------------------------------------------------------
26✔
243
    // Constructor
26✔
244
    // -------------------------------------------------------------------------
26✔
245

26✔
246
    constructor(connection: DataSource) {
26✔
247
        this.connection = connection
×
248
        this.options = connection.options as ReactNativeConnectionOptions
×
249
        // this.database = DriverUtils.buildDriverOptions(this.options).database
×
250
        this.database = this.options.database
×
251

×
252
        this.loadDependencies()
×
253
    }
×
254

26✔
255
    // -------------------------------------------------------------------------
26✔
256
    // Public Abstract
26✔
257
    // -------------------------------------------------------------------------
26✔
258

26✔
259
    /**
26✔
260
     * Creates a query runner used to execute database queries.
26✔
261
     */
26✔
262
    createQueryRunner(mode: ReplicationMode): QueryRunner {
26✔
263
        if (!this.queryRunner)
×
264
            this.queryRunner = new ReactNativeQueryRunner(this)
×
265

×
266
        return this.queryRunner
×
267
    }
×
268

26✔
269
    // -------------------------------------------------------------------------
26✔
270
    // Public Methods
26✔
271
    // -------------------------------------------------------------------------
26✔
272

26✔
273
    /**
26✔
274
     * Performs connection to the database.
26✔
275
     */
26✔
276
    async connect(): Promise<void> {
26✔
277
        this.databaseConnection = await this.createDatabaseConnection()
×
278
    }
×
279

26✔
280
    /**
26✔
281
     * Makes any action after connection (e.g. create extensions in Postgres driver).
26✔
282
     */
26✔
283
    afterConnect(): Promise<void> {
26✔
284
        return Promise.resolve()
×
285
    }
×
286

26✔
287
    /**
26✔
288
     * Closes connection with database.
26✔
289
     */
26✔
290
    async disconnect(): Promise<void> {
26✔
291
        return new Promise<void>((ok, fail) => {
×
292
            this.queryRunner = undefined
×
293
            this.databaseConnection.close(ok, fail)
×
294
        })
×
295
    }
×
296

26✔
297
    hasAttachedDatabases(): boolean {
26✔
298
        return !!Object.keys(this.attachedDatabases).length
×
299
    }
×
300

26✔
301
    getAttachedDatabaseHandleByRelativePath(path: string): string | undefined {
26✔
302
        return this.attachedDatabases?.[path]?.attachHandle
×
303
    }
×
304

26✔
305
    getAttachedDatabasePathRelativeByHandle(
26✔
306
        handle: string,
×
307
    ): string | undefined {
×
308
        return Object.values(this.attachedDatabases).find(
×
309
            ({ attachHandle }) => handle === attachHandle,
×
310
        )?.attachFilepathRelative
×
311
    }
×
312

26✔
313
    /**
26✔
314
     * Creates a schema builder used to build and sync a schema.
26✔
315
     */
26✔
316
    createSchemaBuilder() {
26✔
317
        return new RdbmsSchemaBuilder(this.connection)
×
318
    }
×
319

26✔
320
    /**
26✔
321
     * Prepares given value to a value to be persisted, based on its column type and metadata.
26✔
322
     */
26✔
323
    preparePersistentValue(value: any, columnMetadata: ColumnMetadata): any {
26✔
324
        if (columnMetadata.transformer)
×
325
            value = ApplyValueTransformers.transformTo(
×
326
                columnMetadata.transformer,
×
327
                value,
×
328
            )
×
329

×
330
        if (value === null || value === undefined) return value
×
331

×
332
        if (
×
333
            columnMetadata.type === Boolean ||
×
334
            columnMetadata.type === "boolean"
×
335
        ) {
×
336
            return value === true ? 1 : 0
×
337
        } else if (columnMetadata.type === "date") {
×
338
            return DateUtils.mixedDateToDateString(value)
×
339
        } else if (columnMetadata.type === "time") {
×
340
            return DateUtils.mixedDateToTimeString(value)
×
341
        } else if (
×
342
            columnMetadata.type === "datetime" ||
×
343
            columnMetadata.type === Date
×
344
        ) {
×
345
            // to string conversation needs because SQLite stores date as integer number, when date came as Object
×
346
            // TODO: think about `toUTC` conversion
×
347
            return DateUtils.mixedDateToUtcDatetimeString(value)
×
348
        } else if (columnMetadata.type === "simple-array") {
×
349
            return DateUtils.simpleArrayToString(value)
×
350
        } else if (columnMetadata.type === "simple-json") {
×
351
            return DateUtils.simpleJsonToString(value)
×
352
        } else if (columnMetadata.type === "simple-enum") {
×
353
            return DateUtils.simpleEnumToString(value)
×
354
        }
×
355

×
356
        return value
×
357
    }
×
358

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

×
371
        if (
×
372
            columnMetadata.type === Boolean ||
×
373
            columnMetadata.type === "boolean"
×
374
        ) {
×
375
            value = value ? true : false
×
376
        } else if (
×
377
            columnMetadata.type === "datetime" ||
×
378
            columnMetadata.type === Date
×
379
        ) {
×
380
            /**
×
381
             * Fix date conversion issue
×
382
             *
×
383
             * 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.
×
384
             * We need to modify the date string to "2018-03-14T02:33:33.906Z" and Safari will convert it correctly.
×
385
             *
×
386
             * ISO 8601
×
387
             * https://www.w3.org/TR/NOTE-datetime
×
388
             */
×
389
            if (value && typeof value === "string") {
×
390
                // There are various valid time string formats a sqlite time string might have:
×
391
                // https://www.sqlite.org/lang_datefunc.html
×
392
                // There are two separate fixes we may need to do:
×
393
                //   1) Add 'T' separator if space is used instead
×
394
                //   2) Add 'Z' UTC suffix if no timezone or offset specified
×
395

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

×
408
            value = DateUtils.normalizeHydratedDate(value)
×
409
        } else if (columnMetadata.type === "date") {
×
410
            value = DateUtils.mixedDateToDateString(value)
×
411
        } else if (columnMetadata.type === "time") {
×
412
            value = DateUtils.mixedTimeToString(value)
×
413
        } else if (columnMetadata.type === "simple-array") {
×
414
            value = DateUtils.stringToSimpleArray(value)
×
415
        } else if (columnMetadata.type === "simple-json") {
×
416
            value = DateUtils.stringToSimpleJson(value)
×
417
        } else if (columnMetadata.type === "simple-enum") {
×
418
            value = DateUtils.stringToSimpleEnum(value, columnMetadata)
×
419
        } else if (columnMetadata.type === Number) {
×
420
            // convert to number if number
×
421
            value = !isNaN(+value) ? parseInt(value) : value
×
422
        }
×
423

×
424
        if (columnMetadata.transformer)
×
425
            value = ApplyValueTransformers.transformFrom(
×
426
                columnMetadata.transformer,
×
427
                value,
×
428
            )
×
429

×
430
        return value
×
431
    }
×
432

26✔
433
    /**
26✔
434
     * Replaces parameters in the given sql with special escaping character
26✔
435
     * and an array of parameter names to be passed to a query.
26✔
436
     */
26✔
437
    escapeQueryWithParameters(
26✔
438
        sql: string,
×
439
        parameters: ObjectLiteral,
×
440
        nativeParameters: ObjectLiteral,
×
441
    ): [string, any[]] {
×
442
        const escapedParameters: any[] = Object.keys(nativeParameters).map(
×
443
            (key) => {
×
444
                // Mapping boolean values to their numeric representation
×
445
                if (typeof nativeParameters[key] === "boolean") {
×
446
                    return nativeParameters[key] === true ? 1 : 0
×
447
                }
×
448

×
449
                if (nativeParameters[key] instanceof Date) {
×
450
                    return DateUtils.mixedDateToUtcDatetimeString(
×
451
                        nativeParameters[key],
×
452
                    )
×
453
                }
×
454

×
455
                return nativeParameters[key]
×
456
            },
×
457
        )
×
458

×
459
        if (!parameters || !Object.keys(parameters).length)
×
460
            return [sql, escapedParameters]
×
461

×
462
        sql = sql.replace(
×
463
            /:(\.\.\.)?([A-Za-z0-9_.]+)/g,
×
464
            (full, isArray: string, key: string): string => {
×
465
                if (!parameters.hasOwnProperty(key)) {
×
466
                    return full
×
467
                }
×
468

×
469
                const value: any = parameters[key]
×
470

×
471
                if (isArray) {
×
472
                    return value
×
473
                        .map((v: any) => {
×
474
                            escapedParameters.push(v)
×
475
                            return this.createParameter(
×
476
                                key,
×
477
                                escapedParameters.length - 1,
×
478
                            )
×
479
                        })
×
480
                        .join(", ")
×
481
                }
×
482

×
483
                if (typeof value === "function") {
×
484
                    return value()
×
485
                } else if (typeof value === "number") {
×
486
                    return String(value)
×
487
                }
×
488

×
489
                // Sqlite does not have a boolean data type so we have to transform
×
490
                // it to 1 or 0
×
491
                if (typeof value === "boolean") {
×
492
                    escapedParameters.push(+value)
×
493
                    return this.createParameter(
×
494
                        key,
×
495
                        escapedParameters.length - 1,
×
496
                    )
×
497
                }
×
498

×
499
                if (value instanceof Date) {
×
500
                    escapedParameters.push(
×
501
                        DateUtils.mixedDateToUtcDatetimeString(value),
×
502
                    )
×
503
                    return this.createParameter(
×
504
                        key,
×
505
                        escapedParameters.length - 1,
×
506
                    )
×
507
                }
×
508

×
509
                escapedParameters.push(value)
×
510
                return this.createParameter(key, escapedParameters.length - 1)
×
511
            },
×
512
        ) // todo: make replace only in value statements, otherwise problems
×
513
        return [sql, escapedParameters]
×
514
    }
×
515

26✔
516
    /**
26✔
517
     * Escapes a column name.
26✔
518
     */
26✔
519
    escape(columnName: string): string {
26✔
520
        return '"' + columnName + '"'
×
521
    }
×
522

26✔
523
    /**
26✔
524
     * Build full table name with database name, schema name and table name.
26✔
525
     * E.g. myDB.mySchema.myTable
26✔
526
     *
26✔
527
     * Returns only simple table name because all inherited drivers does not supports schema and database.
26✔
528
     */
26✔
529
    buildTableName(
26✔
530
        tableName: string,
×
531
        schema?: string,
×
532
        database?: string,
×
533
    ): string {
×
534
        return tableName
×
535
    }
×
536

26✔
537
    /**
26✔
538
     * Parse a target table name or other types and return a normalized table definition.
26✔
539
     */
26✔
540
    parseTableName(
26✔
541
        target: EntityMetadata | Table | View | TableForeignKey | string,
×
542
    ): { database?: string; schema?: string; tableName: string } {
×
543
        const driverDatabase = this.database
×
544
        const driverSchema = undefined
×
545

×
546
        if (InstanceChecker.isTable(target) || InstanceChecker.isView(target)) {
×
547
            const parsed = this.parseTableName(
×
548
                target.schema
×
549
                    ? `"${target.schema}"."${target.name}"`
×
550
                    : target.name,
×
551
            )
×
552

×
553
            return {
×
554
                database: target.database || parsed.database || driverDatabase,
×
555
                schema: target.schema || parsed.schema || driverSchema,
×
556
                tableName: parsed.tableName,
×
557
            }
×
558
        }
×
559

×
560
        if (InstanceChecker.isTableForeignKey(target)) {
×
561
            const parsed = this.parseTableName(target.referencedTableName)
×
562

×
563
            return {
×
564
                database:
×
565
                    target.referencedDatabase ||
×
566
                    parsed.database ||
×
567
                    driverDatabase,
×
568
                schema:
×
569
                    target.referencedSchema || parsed.schema || driverSchema,
×
570
                tableName: parsed.tableName,
×
571
            }
×
572
        }
×
573

×
574
        if (InstanceChecker.isEntityMetadata(target)) {
×
575
            // EntityMetadata tableName is never a path
×
576

×
577
            return {
×
578
                database: target.database || driverDatabase,
×
579
                schema: target.schema || driverSchema,
×
580
                tableName: target.tableName,
×
581
            }
×
582
        }
×
583

×
584
        const parts = target.split(".")
×
585

×
586
        if (parts.length === 3) {
×
587
            return {
×
588
                database: parts[0] || driverDatabase,
×
589
                schema: parts[1] || driverSchema,
×
590
                tableName: parts[2],
×
591
            }
×
592
        } else if (parts.length === 2) {
×
593
            const database =
×
594
                this.getAttachedDatabasePathRelativeByHandle(parts[0]) ??
×
595
                driverDatabase
×
596
            return {
×
597
                database: database,
×
598
                schema: parts[0],
×
599
                tableName: parts[1],
×
600
            }
×
601
        } else {
×
602
            return {
×
603
                database: driverDatabase,
×
604
                schema: driverSchema,
×
605
                tableName: target,
×
606
            }
×
607
        }
×
608
    }
×
609

26✔
610
    /**
26✔
611
     * Creates a database type from a given column metadata.
26✔
612
     */
26✔
613
    normalizeType(column: {
26✔
614
        type?: ColumnType
×
615
        length?: number | string
×
616
        precision?: number | null
×
617
        scale?: number
×
618
    }): string {
×
619
        if (column.type === Number || column.type === "int") {
×
620
            return "integer"
×
621
        } else if (column.type === String) {
×
622
            return "varchar"
×
623
        } else if (column.type === Date) {
×
624
            return "datetime"
×
625
        } else if (column.type === Boolean) {
×
626
            return "boolean"
×
627
        } else if (column.type === "uuid") {
×
628
            return "varchar"
×
629
        } else if (column.type === "simple-array") {
×
630
            return "text"
×
631
        } else if (column.type === "simple-json") {
×
632
            return "text"
×
633
        } else if (column.type === "simple-enum") {
×
634
            return "varchar"
×
635
        } else {
×
636
            return (column.type as string) || ""
×
637
        }
×
638
    }
×
639

26✔
640
    /**
26✔
641
     * Normalizes "default" value of the column.
26✔
642
     */
26✔
643
    normalizeDefault(columnMetadata: ColumnMetadata): string | undefined {
26✔
644
        const defaultValue = columnMetadata.default
×
645

×
646
        if (typeof defaultValue === "number") {
×
647
            return "" + defaultValue
×
648
        }
×
649

×
650
        if (typeof defaultValue === "boolean") {
×
651
            return defaultValue ? "1" : "0"
×
652
        }
×
653

×
654
        if (typeof defaultValue === "function") {
×
655
            return defaultValue()
×
656
        }
×
657

×
658
        if (typeof defaultValue === "string") {
×
659
            return `'${defaultValue}'`
×
660
        }
×
661

×
662
        if (defaultValue === null || defaultValue === undefined) {
×
663
            return undefined
×
664
        }
×
665

×
666
        return `${defaultValue}`
×
667
    }
×
668

26✔
669
    /**
26✔
670
     * Normalizes "isUnique" value of the column.
26✔
671
     */
26✔
672
    normalizeIsUnique(column: ColumnMetadata): boolean {
26✔
673
        return column.entityMetadata.uniques.some(
×
674
            (uq) => uq.columns.length === 1 && uq.columns[0] === column,
×
675
        )
×
676
    }
×
677

26✔
678
    /**
26✔
679
     * Calculates column length taking into account the default length values.
26✔
680
     */
26✔
681
    getColumnLength(column: ColumnMetadata): string {
26✔
682
        return column.length ? column.length.toString() : ""
×
683
    }
×
684

26✔
685
    /**
26✔
686
     * Normalizes "default" value of the column.
26✔
687
     */
26✔
688
    createFullType(column: TableColumn): string {
26✔
689
        let type = column.type
×
690
        if (column.enum) {
×
691
            return "varchar"
×
692
        }
×
693
        if (column.length) {
×
694
            type += "(" + column.length + ")"
×
695
        } else if (
×
696
            column.precision !== null &&
×
697
            column.precision !== undefined &&
×
698
            column.scale !== null &&
×
699
            column.scale !== undefined
×
700
        ) {
×
701
            type += "(" + column.precision + "," + column.scale + ")"
×
702
        } else if (
×
703
            column.precision !== null &&
×
704
            column.precision !== undefined
×
705
        ) {
×
706
            type += "(" + column.precision + ")"
×
707
        }
×
708

×
709
        if (column.isArray) type += " array"
×
710

×
711
        return type
×
712
    }
×
713

26✔
714
    /**
26✔
715
     * Obtains a new database connection to a master server.
26✔
716
     * Used for replication.
26✔
717
     * If replication is not setup then returns default connection's database connection.
26✔
718
     */
26✔
719
    obtainMasterConnection(): Promise<any> {
26✔
720
        return Promise.resolve()
×
721
    }
×
722

26✔
723
    /**
26✔
724
     * Obtains a new database connection to a slave server.
26✔
725
     * Used for replication.
26✔
726
     * If replication is not setup then returns master (default) connection's database connection.
26✔
727
     */
26✔
728
    obtainSlaveConnection(): Promise<any> {
26✔
729
        return Promise.resolve()
×
730
    }
×
731

26✔
732
    /**
26✔
733
     * Creates generated map of values generated or returned by database after INSERT query.
26✔
734
     */
26✔
735
    createGeneratedMap(
26✔
736
        metadata: EntityMetadata,
×
737
        insertResult: any,
×
738
        entityIndex: number,
×
739
        entityNum: number,
×
740
    ) {
×
741
        const generatedMap = metadata.generatedColumns.reduce(
×
742
            (map, generatedColumn) => {
×
743
                let value: any
×
744
                if (
×
745
                    generatedColumn.generationStrategy === "increment" &&
×
746
                    insertResult
×
747
                ) {
×
748
                    // NOTE: When INSERT statement is successfully completed, the last inserted row ID is returned.
×
749
                    // see also: SqliteQueryRunner.query()
×
750
                    value = insertResult - entityNum + entityIndex + 1
×
751
                    // } else if (generatedColumn.generationStrategy === "uuid") {
×
752
                    //     value = insertValue[generatedColumn.databaseName];
×
753
                }
×
754

×
755
                if (!value) return map
×
756
                return OrmUtils.mergeDeep(
×
757
                    map,
×
758
                    generatedColumn.createValueMap(value),
×
759
                )
×
760
            },
×
761
            {} as ObjectLiteral,
×
762
        )
×
763

×
764
        return Object.keys(generatedMap).length > 0 ? generatedMap : undefined
×
765
    }
×
766

26✔
767
    /**
26✔
768
     * Differentiate columns of this table and columns from the given column metadatas columns
26✔
769
     * and returns only changed.
26✔
770
     */
26✔
771
    findChangedColumns(
26✔
772
        tableColumns: TableColumn[],
×
773
        columnMetadatas: ColumnMetadata[],
×
774
    ): ColumnMetadata[] {
×
775
        return columnMetadatas.filter((columnMetadata) => {
×
776
            const tableColumn = tableColumns.find(
×
777
                (c) => c.name === columnMetadata.databaseName,
×
778
            )
×
779
            if (!tableColumn) return false // we don't need new columns, we only need exist and changed
×
780

×
781
            const isColumnChanged =
×
782
                tableColumn.name !== columnMetadata.databaseName ||
×
783
                tableColumn.type !== this.normalizeType(columnMetadata) ||
×
784
                tableColumn.length !== columnMetadata.length ||
×
785
                tableColumn.precision !== columnMetadata.precision ||
×
786
                tableColumn.scale !== columnMetadata.scale ||
×
787
                this.normalizeDefault(columnMetadata) !== tableColumn.default ||
×
788
                tableColumn.isPrimary !== columnMetadata.isPrimary ||
×
789
                tableColumn.isNullable !== columnMetadata.isNullable ||
×
790
                tableColumn.generatedType !== columnMetadata.generatedType ||
×
791
                tableColumn.asExpression !== columnMetadata.asExpression ||
×
792
                tableColumn.isUnique !==
×
793
                    this.normalizeIsUnique(columnMetadata) ||
×
794
                (tableColumn.enum &&
×
795
                    columnMetadata.enum &&
×
796
                    !OrmUtils.isArraysEqual(
×
797
                        tableColumn.enum,
×
798
                        columnMetadata.enum.map((val) => val + ""),
×
799
                    )) ||
×
800
                (columnMetadata.generationStrategy !== "uuid" &&
×
801
                    tableColumn.isGenerated !== columnMetadata.isGenerated)
×
802

×
803
            // DEBUG SECTION
×
804
            // if (isColumnChanged) {
×
805
            //     console.log("table:", columnMetadata.entityMetadata.tableName)
×
806
            //     console.log(
×
807
            //         "name:",
×
808
            //         tableColumn.name,
×
809
            //         columnMetadata.databaseName,
×
810
            //     )
×
811
            //     console.log(
×
812
            //         "type:",
×
813
            //         tableColumn.type,
×
814
            //         this.normalizeType(columnMetadata),
×
815
            //     )
×
816
            //     console.log(
×
817
            //         "length:",
×
818
            //         tableColumn.length,
×
819
            //         columnMetadata.length,
×
820
            //     )
×
821
            //     console.log(
×
822
            //         "precision:",
×
823
            //         tableColumn.precision,
×
824
            //         columnMetadata.precision,
×
825
            //     )
×
826
            //     console.log("scale:", tableColumn.scale, columnMetadata.scale)
×
827
            //     console.log(
×
828
            //         "default:",
×
829
            //         this.normalizeDefault(columnMetadata),
×
830
            //         columnMetadata.default,
×
831
            //     )
×
832
            //     console.log(
×
833
            //         "isPrimary:",
×
834
            //         tableColumn.isPrimary,
×
835
            //         columnMetadata.isPrimary,
×
836
            //     )
×
837
            //     console.log(
×
838
            //         "isNullable:",
×
839
            //         tableColumn.isNullable,
×
840
            //         columnMetadata.isNullable,
×
841
            //     )
×
842
            //     console.log(
×
843
            //         "generatedType:",
×
844
            //         tableColumn.generatedType,
×
845
            //         columnMetadata.generatedType,
×
846
            //     )
×
847
            //     console.log(
×
848
            //         "asExpression:",
×
849
            //         tableColumn.asExpression,
×
850
            //         columnMetadata.asExpression,
×
851
            //     )
×
852
            //     console.log(
×
853
            //         "isUnique:",
×
854
            //         tableColumn.isUnique,
×
855
            //         this.normalizeIsUnique(columnMetadata),
×
856
            //     )
×
857
            //     console.log(
×
858
            //         "enum:",
×
859
            //         tableColumn.enum &&
×
860
            //             columnMetadata.enum &&
×
861
            //             !OrmUtils.isArraysEqual(
×
862
            //                 tableColumn.enum,
×
863
            //                 columnMetadata.enum.map((val) => val + ""),
×
864
            //             ),
×
865
            //     )
×
866
            //     console.log(
×
867
            //         "isGenerated:",
×
868
            //         tableColumn.isGenerated,
×
869
            //         columnMetadata.isGenerated,
×
870
            //     )
×
871
            // }
×
872

×
873
            return isColumnChanged
×
874
        })
×
875
    }
×
876

26✔
877
    /**
26✔
878
     * Returns true if driver supports RETURNING / OUTPUT statement.
26✔
879
     */
26✔
880
    isReturningSqlSupported(): boolean {
26✔
881
        return false
×
882
    }
×
883

26✔
884
    /**
26✔
885
     * Returns true if driver supports uuid values generation on its own.
26✔
886
     */
26✔
887
    isUUIDGenerationSupported(): boolean {
26✔
888
        return false
×
889
    }
×
890

26✔
891
    /**
26✔
892
     * Returns true if driver supports fulltext indices.
26✔
893
     */
26✔
894
    isFullTextColumnTypeSupported(): boolean {
26✔
895
        return false
×
896
    }
×
897

26✔
898
    /**
26✔
899
     * Creates an escaped parameter.
26✔
900
     */
26✔
901
    createParameter(parameterName: string, index: number): string {
26✔
902
        // return "$" + (index + 1);
×
903
        return "?"
×
904
        // return "$" + parameterName;
×
905
    }
×
906

26✔
907
    // -------------------------------------------------------------------------
26✔
908
    // Protected Methods
26✔
909
    // -------------------------------------------------------------------------
26✔
910

26✔
911
    /**
26✔
912
     * Creates connection with the database.
26✔
913
     */
26✔
914
    protected createDatabaseConnection() {
26✔
915
        return new Promise<void>((ok, fail) => {
×
916
            const options = Object.assign(
×
917
                {},
×
918
                {
×
919
                    name: this.options.database,
×
920
                    location: this.options.location,
×
921
                },
×
922
                this.options.extra || {},
×
923
            )
×
924

×
925
            this.sqlite.openDatabase(
×
926
                options,
×
927
                (db: any) => {
×
928
                    const databaseConnection = db
×
929

×
930
                    // we need to enable foreign keys in sqlite to make sure all foreign key related features
×
931
                    // working properly. this also makes onDelete work with sqlite.
×
932
                    databaseConnection.executeSql(
×
933
                        `PRAGMA foreign_keys = ON`,
×
934
                        [],
×
935
                        (result: any) => {
×
936
                            ok(databaseConnection)
×
937
                        },
×
938
                        (error: any) => {
×
939
                            fail(error)
×
940
                        },
×
941
                    )
×
942
                },
×
943
                (error: any) => {
×
944
                    fail(error)
×
945
                },
×
946
            )
×
947
        })
×
948
    }
×
949

26✔
950
    /**
26✔
951
     * If driver dependency is not given explicitly, then try to load it via "require".
26✔
952
     */
26✔
953
    protected loadDependencies(): void {
26✔
954
        try {
×
955
            const sqlite =
×
956
                this.options.driver || require("react-native-sqlite-storage")
×
957
            this.sqlite = sqlite
×
958
        } catch (e) {
×
959
            throw new DriverPackageNotInstalledError(
×
960
                "React-Native",
×
961
                "react-native-sqlite-storage",
×
962
            )
×
963
        }
×
964
    }
×
965
}
26✔
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