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

typeorm / typeorm / 21112139320

18 Jan 2026 01:00PM UTC coverage: 80.932% (-0.003%) from 80.935%
21112139320

push

github

web-flow
feat: add encryption key for React Native (#11736)

Co-authored-by: gioboa <giorgiob.boa@gmail.com>

26916 of 32704 branches covered (82.3%)

Branch coverage included in aggregate %.

0 of 5 new or added lines in 2 files covered. (0.0%)

1 existing line in 1 file now uncovered.

91955 of 114174 relevant lines covered (80.54%)

69704.13 hits per line

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

41.65
/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
                utc: columnMetadata.utc,
×
340
            })
×
341
        } else if (columnMetadata.type === "time") {
×
342
            return DateUtils.mixedDateToTimeString(value)
×
343
        } else if (
×
344
            columnMetadata.type === "datetime" ||
×
345
            columnMetadata.type === Date
×
346
        ) {
×
347
            // to string conversation needs because SQLite stores date as integer number, when date came as Object
×
348
            // TODO: think about `toUTC` conversion
×
349
            return DateUtils.mixedDateToUtcDatetimeString(value)
×
350
        } else if (columnMetadata.type === "simple-array") {
×
351
            return DateUtils.simpleArrayToString(value)
×
352
        } else if (columnMetadata.type === "simple-json") {
×
353
            return DateUtils.simpleJsonToString(value)
×
354
        } else if (columnMetadata.type === "simple-enum") {
×
355
            return DateUtils.simpleEnumToString(value)
×
356
        }
×
357

×
358
        return value
×
359
    }
×
360

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

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

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

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

×
428
        if (columnMetadata.transformer)
×
429
            value = ApplyValueTransformers.transformFrom(
×
430
                columnMetadata.transformer,
×
431
                value,
×
432
            )
×
433

×
434
        return value
×
435
    }
×
436

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

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

×
459
                return nativeParameters[key]
×
460
            },
×
461
        )
×
462

×
463
        if (!parameters || !Object.keys(parameters).length)
×
464
            return [sql, escapedParameters]
×
465

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

×
473
                const value: any = parameters[key]
×
474

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

×
487
                if (typeof value === "function") {
×
488
                    return value()
×
489
                } else if (typeof value === "number") {
×
490
                    return String(value)
×
491
                }
×
492

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

×
503
                if (value instanceof Date) {
×
504
                    escapedParameters.push(
×
505
                        DateUtils.mixedDateToUtcDatetimeString(value),
×
506
                    )
×
507
                    return this.createParameter(
×
508
                        key,
×
509
                        escapedParameters.length - 1,
×
510
                    )
×
511
                }
×
512

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

26✔
520
    /**
26✔
521
     * Escapes a column name.
26✔
522
     */
26✔
523
    escape(columnName: string): string {
26✔
524
        return '"' + columnName + '"'
×
525
    }
×
526

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

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

×
550
        if (InstanceChecker.isTable(target) || InstanceChecker.isView(target)) {
×
551
            const parsed = this.parseTableName(
×
552
                target.schema
×
553
                    ? `"${target.schema}"."${target.name}"`
×
554
                    : target.name,
×
555
            )
×
556

×
557
            return {
×
558
                database: target.database || parsed.database || driverDatabase,
×
559
                schema: target.schema || parsed.schema || driverSchema,
×
560
                tableName: parsed.tableName,
×
561
            }
×
562
        }
×
563

×
564
        if (InstanceChecker.isTableForeignKey(target)) {
×
565
            const parsed = this.parseTableName(target.referencedTableName)
×
566

×
567
            return {
×
568
                database:
×
569
                    target.referencedDatabase ||
×
570
                    parsed.database ||
×
571
                    driverDatabase,
×
572
                schema:
×
573
                    target.referencedSchema || parsed.schema || driverSchema,
×
574
                tableName: parsed.tableName,
×
575
            }
×
576
        }
×
577

×
578
        if (InstanceChecker.isEntityMetadata(target)) {
×
579
            // EntityMetadata tableName is never a path
×
580

×
581
            return {
×
582
                database: target.database || driverDatabase,
×
583
                schema: target.schema || driverSchema,
×
584
                tableName: target.tableName,
×
585
            }
×
586
        }
×
587

×
588
        const parts = target.split(".")
×
589

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

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

26✔
644
    /**
26✔
645
     * Normalizes "default" value of the column.
26✔
646
     */
26✔
647
    normalizeDefault(columnMetadata: ColumnMetadata): string | undefined {
26✔
648
        const defaultValue = columnMetadata.default
×
649

×
650
        if (typeof defaultValue === "number") {
×
651
            return "" + defaultValue
×
652
        }
×
653

×
654
        if (typeof defaultValue === "boolean") {
×
655
            return defaultValue ? "1" : "0"
×
656
        }
×
657

×
658
        if (typeof defaultValue === "function") {
×
659
            return defaultValue()
×
660
        }
×
661

×
662
        if (typeof defaultValue === "string") {
×
663
            return `'${defaultValue}'`
×
664
        }
×
665

×
666
        if (defaultValue === null || defaultValue === undefined) {
×
667
            return undefined
×
668
        }
×
669

×
670
        return `${defaultValue}`
×
671
    }
×
672

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

26✔
682
    /**
26✔
683
     * Calculates column length taking into account the default length values.
26✔
684
     */
26✔
685
    getColumnLength(column: ColumnMetadata): string {
26✔
686
        return column.length ? column.length.toString() : ""
×
687
    }
×
688

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

×
713
        if (column.isArray) type += " array"
×
714

×
715
        return type
×
716
    }
×
717

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

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

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

×
759
                if (!value) return map
×
760
                return OrmUtils.mergeDeep(
×
761
                    map,
×
762
                    generatedColumn.createValueMap(value),
×
763
                )
×
764
            },
×
765
            {} as ObjectLiteral,
×
766
        )
×
767

×
768
        return Object.keys(generatedMap).length > 0 ? generatedMap : undefined
×
769
    }
×
770

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

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

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

×
877
            return isColumnChanged
×
878
        })
×
879
    }
×
880

26✔
881
    /**
26✔
882
     * Returns true if driver supports RETURNING / OUTPUT statement.
26✔
883
     */
26✔
884
    isReturningSqlSupported(): boolean {
26✔
885
        return false
×
886
    }
×
887

26✔
888
    /**
26✔
889
     * Returns true if driver supports uuid values generation on its own.
26✔
890
     */
26✔
891
    isUUIDGenerationSupported(): boolean {
26✔
892
        return false
×
893
    }
×
894

26✔
895
    /**
26✔
896
     * Returns true if driver supports fulltext indices.
26✔
897
     */
26✔
898
    isFullTextColumnTypeSupported(): boolean {
26✔
899
        return false
×
900
    }
×
901

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

26✔
911
    // -------------------------------------------------------------------------
26✔
912
    // Protected Methods
26✔
913
    // -------------------------------------------------------------------------
26✔
914

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

×
930
            this.sqlite.openDatabase(
×
931
                options,
×
932
                (db: any) => {
×
933
                    const databaseConnection = db
×
934

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

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