• 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

93.4
/src/driver/oracle/OracleDriver.ts
1
import { Driver } from "../Driver"
26✔
2
import { ConnectionIsNotSetError } from "../../error/ConnectionIsNotSetError"
26✔
3
import { DriverPackageNotInstalledError } from "../../error/DriverPackageNotInstalledError"
26✔
4
import { CteCapabilities } from "../types/CteCapabilities"
26✔
5
import { OracleQueryRunner } from "./OracleQueryRunner"
26✔
6
import { ObjectLiteral } from "../../common/ObjectLiteral"
26✔
7
import { ColumnMetadata } from "../../metadata/ColumnMetadata"
26✔
8
import { DateUtils } from "../../util/DateUtils"
26✔
9
import { PlatformTools } from "../../platform/PlatformTools"
26✔
10
import { DataSource } from "../../data-source/DataSource"
26✔
11
import { RdbmsSchemaBuilder } from "../../schema-builder/RdbmsSchemaBuilder"
26✔
12
import { OracleConnectionOptions } from "./OracleConnectionOptions"
26✔
13
import { MappedColumnTypes } from "../types/MappedColumnTypes"
26✔
14
import { ColumnType } from "../types/ColumnTypes"
26✔
15
import { DataTypeDefaults } from "../types/DataTypeDefaults"
26✔
16
import { TableColumn } from "../../schema-builder/table/TableColumn"
26✔
17
import { OracleConnectionCredentialsOptions } from "./OracleConnectionCredentialsOptions"
26✔
18
import { DriverUtils } from "../DriverUtils"
26✔
19
import { EntityMetadata } from "../../metadata/EntityMetadata"
26✔
20
import { OrmUtils } from "../../util/OrmUtils"
26✔
21
import { ApplyValueTransformers } from "../../util/ApplyValueTransformers"
26✔
22
import { ReplicationMode } from "../types/ReplicationMode"
26✔
23
import { Table } from "../../schema-builder/table/Table"
26✔
24
import { View } from "../../schema-builder/view/View"
26✔
25
import { TableForeignKey } from "../../schema-builder/table/TableForeignKey"
26✔
26
import { TypeORMError } from "../../error"
26✔
27
import { InstanceChecker } from "../../util/InstanceChecker"
26✔
28
import { UpsertType } from "../types/UpsertType"
26✔
29
import { OnDeleteType } from "../../metadata/types/OnDeleteType"
26✔
30
import { OnUpdateType } from "../../metadata/types/OnUpdateType"
26✔
31

26✔
32
/**
26✔
33
 * Organizes communication with Oracle RDBMS.
26✔
34
 */
26✔
35
export class OracleDriver implements Driver {
26✔
36
    // -------------------------------------------------------------------------
26✔
37
    // Public Properties
26✔
38
    // -------------------------------------------------------------------------
26✔
39

26✔
40
    /**
26✔
41
     * Connection used by driver.
26✔
42
     */
26✔
43
    connection: DataSource
26✔
44

26✔
45
    /**
26✔
46
     * Underlying oracle library.
26✔
47
     */
26✔
48
    oracle: any
26✔
49

26✔
50
    /**
26✔
51
     * Pool for master database.
26✔
52
     */
26✔
53
    master: any
26✔
54

26✔
55
    /**
26✔
56
     * Pool for slave databases.
26✔
57
     * Used in replication.
26✔
58
     */
26✔
59
    slaves: any[] = []
26✔
60

26✔
61
    // -------------------------------------------------------------------------
26✔
62
    // Public Implemented Properties
26✔
63
    // -------------------------------------------------------------------------
26✔
64

26✔
65
    /**
26✔
66
     * Connection options.
26✔
67
     */
26✔
68
    options: OracleConnectionOptions
26✔
69

26✔
70
    /**
26✔
71
     * Database name used to perform all write queries.
26✔
72
     */
26✔
73
    database?: string
26✔
74

26✔
75
    /**
26✔
76
     * Schema name used to perform all write queries.
26✔
77
     */
26✔
78
    schema?: string
26✔
79

26✔
80
    /**
26✔
81
     * Indicates if replication is enabled.
26✔
82
     */
26✔
83
    isReplicated: boolean = false
26✔
84

26✔
85
    /**
26✔
86
     * Indicates if tree tables are supported by this driver.
26✔
87
     */
26✔
88
    treeSupport = true
26✔
89

26✔
90
    /**
26✔
91
     * Represent transaction support by this driver
26✔
92
     */
26✔
93
    transactionSupport = "nested" as const
26✔
94

26✔
95
    /**
26✔
96
     * Gets list of supported column data types by a driver.
26✔
97
     *
26✔
98
     * @see https://www.techonthenet.com/oracle/datatypes.php
26✔
99
     * @see https://docs.oracle.com/cd/B28359_01/server.111/b28318/datatype.htm#CNCPT012
26✔
100
     */
26✔
101
    supportedDataTypes: ColumnType[] = [
26✔
102
        "char",
26✔
103
        "nchar",
26✔
104
        "nvarchar2",
26✔
105
        "varchar2",
26✔
106
        "long",
26✔
107
        "raw",
26✔
108
        "long raw",
26✔
109
        "number",
26✔
110
        "numeric",
26✔
111
        "float",
26✔
112
        "dec",
26✔
113
        "decimal",
26✔
114
        "integer",
26✔
115
        "int",
26✔
116
        "smallint",
26✔
117
        "real",
26✔
118
        "double precision",
26✔
119
        "date",
26✔
120
        "timestamp",
26✔
121
        "timestamp with time zone",
26✔
122
        "timestamp with local time zone",
26✔
123
        "interval year to month",
26✔
124
        "interval day to second",
26✔
125
        "bfile",
26✔
126
        "blob",
26✔
127
        "clob",
26✔
128
        "nclob",
26✔
129
        "rowid",
26✔
130
        "urowid",
26✔
131
        "simple-json",
26✔
132
        "json",
26✔
133
    ]
26✔
134

26✔
135
    /**
26✔
136
     * Returns type of upsert supported by driver if any
26✔
137
     */
26✔
138
    supportedUpsertTypes: UpsertType[] = ["merge-into"]
26✔
139

26✔
140
    /**
26✔
141
     * Returns list of supported onDelete types by driver.
26✔
142
     * https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/sql-language-reference.pdf
26✔
143
     * Oracle does not support NO ACTION, but NO ACTION is set by default in EntityMetadata
26✔
144
     */
26✔
145
    supportedOnDeleteTypes: OnDeleteType[] = [
26✔
146
        "CASCADE",
26✔
147
        "SET NULL",
26✔
148
        "NO ACTION",
26✔
149
    ]
26✔
150

26✔
151
    /**
26✔
152
     * Returns list of supported onUpdate types by driver.
26✔
153
     * Oracle does not have onUpdate option, but we allow NO ACTION since it is set by default in EntityMetadata
26✔
154
     */
26✔
155
    supportedOnUpdateTypes: OnUpdateType[] = ["NO ACTION"]
26✔
156

26✔
157
    /**
26✔
158
     * Gets list of spatial column data types.
26✔
159
     */
26✔
160
    spatialTypes: ColumnType[] = []
26✔
161

26✔
162
    /**
26✔
163
     * Gets list of column data types that support length by a driver.
26✔
164
     */
26✔
165
    withLengthColumnTypes: ColumnType[] = [
26✔
166
        "char",
26✔
167
        "nchar",
26✔
168
        "nvarchar2",
26✔
169
        "varchar2",
26✔
170
        "varchar",
26✔
171
        "raw",
26✔
172
    ]
26✔
173

26✔
174
    /**
26✔
175
     * Gets list of column data types that support precision by a driver.
26✔
176
     */
26✔
177
    withPrecisionColumnTypes: ColumnType[] = [
26✔
178
        "number",
26✔
179
        "float",
26✔
180
        "timestamp",
26✔
181
        "timestamp with time zone",
26✔
182
        "timestamp with local time zone",
26✔
183
    ]
26✔
184

26✔
185
    /**
26✔
186
     * Gets list of column data types that support scale by a driver.
26✔
187
     */
26✔
188
    withScaleColumnTypes: ColumnType[] = ["number"]
26✔
189

26✔
190
    /**
26✔
191
     * Orm has special columns and we need to know what database column types should be for those types.
26✔
192
     * Column types are driver dependant.
26✔
193
     */
26✔
194
    mappedDataTypes: MappedColumnTypes = {
26✔
195
        createDate: "timestamp",
26✔
196
        createDateDefault: "CURRENT_TIMESTAMP",
26✔
197
        updateDate: "timestamp",
26✔
198
        updateDateDefault: "CURRENT_TIMESTAMP",
26✔
199
        deleteDate: "timestamp",
26✔
200
        deleteDateNullable: true,
26✔
201
        version: "number",
26✔
202
        treeLevel: "number",
26✔
203
        migrationId: "number",
26✔
204
        migrationName: "varchar2",
26✔
205
        migrationTimestamp: "number",
26✔
206
        cacheId: "number",
26✔
207
        cacheIdentifier: "varchar2",
26✔
208
        cacheTime: "number",
26✔
209
        cacheDuration: "number",
26✔
210
        cacheQuery: "clob",
26✔
211
        cacheResult: "clob",
26✔
212
        metadataType: "varchar2",
26✔
213
        metadataDatabase: "varchar2",
26✔
214
        metadataSchema: "varchar2",
26✔
215
        metadataTable: "varchar2",
26✔
216
        metadataName: "varchar2",
26✔
217
        metadataValue: "clob",
26✔
218
    }
26✔
219

26✔
220
    /**
26✔
221
     * The prefix used for the parameters
26✔
222
     */
26✔
223
    parametersPrefix: string = ":"
26✔
224

26✔
225
    /**
26✔
226
     * Default values of length, precision and scale depends on column data type.
26✔
227
     * Used in the cases when length/precision/scale is not specified by user.
26✔
228
     */
26✔
229
    dataTypeDefaults: DataTypeDefaults = {
26✔
230
        char: { length: 1 },
26✔
231
        nchar: { length: 1 },
26✔
232
        varchar: { length: 255 },
26✔
233
        varchar2: { length: 255 },
26✔
234
        nvarchar2: { length: 255 },
26✔
235
        raw: { length: 2000 },
26✔
236
        float: { precision: 126 },
26✔
237
        timestamp: { precision: 6 },
26✔
238
        "timestamp with time zone": { precision: 6 },
26✔
239
        "timestamp with local time zone": { precision: 6 },
26✔
240
    }
26✔
241

26✔
242
    /**
26✔
243
     * Max length allowed by Oracle for aliases.
26✔
244
     * @see https://docs.oracle.com/database/121/SQLRF/sql_elements008.htm#SQLRF51129
26✔
245
     * > The following list of rules applies to both quoted and nonquoted identifiers unless otherwise indicated
26✔
246
     * > Names must be from 1 to 30 bytes long with these exceptions:
26✔
247
     * > [...]
26✔
248
     *
26✔
249
     * Since Oracle 12.2 (with a compatible driver/client), the limit has been set to 128.
26✔
250
     * @see https://docs.oracle.com/en/database/oracle/oracle-database/12.2/sqlrf/Database-Object-Names-and-Qualifiers.html
26✔
251
     *
26✔
252
     * > If COMPATIBLE is set to a value of 12.2 or higher, then names must be from 1 to 128 bytes long with these exceptions
26✔
253
     */
26✔
254
    maxAliasLength = 29
26✔
255

26✔
256
    cteCapabilities: CteCapabilities = {
26✔
257
        enabled: true,
26✔
258
    }
26✔
259

26✔
260
    dummyTableName = "DUAL"
26✔
261

26✔
262
    // -------------------------------------------------------------------------
26✔
263
    // Constructor
26✔
264
    // -------------------------------------------------------------------------
26✔
265

26✔
266
    constructor(connection: DataSource) {
26✔
267
        this.connection = connection
904✔
268
        this.options = connection.options as OracleConnectionOptions
904✔
269

904✔
270
        if (this.options.useUTC === true) {
904!
271
            process.env.ORA_SDTZ = "UTC"
×
272
        }
×
273
        // load oracle package
904✔
274
        this.loadDependencies()
904✔
275

904✔
276
        this.database = DriverUtils.buildDriverOptions(
904✔
277
            this.options.replication
904✔
278
                ? this.options.replication.master
904!
279
                : this.options,
904✔
280
        ).database
904✔
281
        this.schema = DriverUtils.buildDriverOptions(this.options).schema
904✔
282

904✔
283
        // Object.assign(connection.options, DriverUtils.buildDriverOptions(connection.options)); // todo: do it better way
904✔
284
        // validate options to make sure everything is set
904✔
285
        // if (!this.options.host)
904✔
286
        //     throw new DriverOptionNotSetError("host");
904✔
287
        // if (!this.options.username)
904✔
288
        //     throw new DriverOptionNotSetError("username");
904✔
289
        // if (!this.options.sid)
904✔
290
        //     throw new DriverOptionNotSetError("sid");
904✔
291
        //
904✔
292
    }
904✔
293

26✔
294
    // -------------------------------------------------------------------------
26✔
295
    // Public Methods
26✔
296
    // -------------------------------------------------------------------------
26✔
297

26✔
298
    /**
26✔
299
     * Performs connection to the database.
26✔
300
     * Based on pooling options, it can either create connection immediately,
26✔
301
     * either create a pool and create connection when needed.
26✔
302
     */
26✔
303
    async connect(): Promise<void> {
26✔
304
        this.oracle.fetchAsString = [this.oracle.DB_TYPE_CLOB]
904✔
305
        this.oracle.fetchAsBuffer = [this.oracle.DB_TYPE_BLOB]
904✔
306
        if (this.options.replication) {
904!
307
            this.slaves = await Promise.all(
×
308
                this.options.replication.slaves.map((slave) => {
×
309
                    return this.createPool(this.options, slave)
×
310
                }),
×
311
            )
×
312
            this.master = await this.createPool(
×
313
                this.options,
×
314
                this.options.replication.master,
×
315
            )
×
316
        } else {
904✔
317
            this.master = await this.createPool(this.options, this.options)
904✔
318
        }
904✔
319

904✔
320
        if (!this.database || !this.schema) {
904!
321
            const queryRunner = this.createQueryRunner("master")
904✔
322

904✔
323
            if (!this.database) {
904✔
324
                this.database = await queryRunner.getCurrentDatabase()
904✔
325
            }
904✔
326

904✔
327
            if (!this.schema) {
904✔
328
                this.schema = await queryRunner.getCurrentSchema()
904✔
329
            }
904✔
330

904✔
331
            await queryRunner.release()
904✔
332
        }
904✔
333
    }
904✔
334

26✔
335
    /**
26✔
336
     * Makes any action after connection (e.g. create extensions in Postgres driver).
26✔
337
     */
26✔
338
    afterConnect(): Promise<void> {
26✔
339
        return Promise.resolve()
898✔
340
    }
898✔
341

26✔
342
    /**
26✔
343
     * Closes connection with the database.
26✔
344
     */
26✔
345
    async disconnect(): Promise<void> {
26✔
346
        if (!this.master) {
904!
347
            throw new ConnectionIsNotSetError("oracle")
×
348
        }
×
349

904✔
350
        await this.closePool(this.master)
904✔
351
        await Promise.all(this.slaves.map((slave) => this.closePool(slave)))
904✔
352
        this.master = undefined
904✔
353
        this.slaves = []
904✔
354
    }
904✔
355

26✔
356
    /**
26✔
357
     * Creates a schema builder used to build and sync a schema.
26✔
358
     */
26✔
359
    createSchemaBuilder() {
26✔
360
        return new RdbmsSchemaBuilder(this.connection)
2,704✔
361
    }
2,704✔
362

26✔
363
    /**
26✔
364
     * Creates a query runner used to execute database queries.
26✔
365
     */
26✔
366
    createQueryRunner(mode: ReplicationMode) {
26✔
367
        return new OracleQueryRunner(this, mode)
23,034✔
368
    }
23,034✔
369

26✔
370
    /**
26✔
371
     * Replaces parameters in the given sql with special escaping character
26✔
372
     * and an array of parameter names to be passed to a query.
26✔
373
     */
26✔
374
    escapeQueryWithParameters(
26✔
375
        sql: string,
33,894✔
376
        parameters: ObjectLiteral,
33,894✔
377
        nativeParameters: ObjectLiteral,
33,894✔
378
    ): [string, any[]] {
33,894✔
379
        const escapedParameters: any[] = Object.keys(nativeParameters).map(
33,894✔
380
            (key) => {
33,894✔
381
                if (typeof nativeParameters[key] === "boolean")
×
382
                    return nativeParameters[key] ? 1 : 0
×
383
                return nativeParameters[key]
×
384
            },
33,894✔
385
        )
33,894✔
386
        if (!parameters || !Object.keys(parameters).length)
33,894✔
387
            return [sql, escapedParameters]
33,894✔
388

32,624✔
389
        sql = sql.replace(
32,624✔
390
            /:(\.\.\.)?([A-Za-z0-9_.]+)/g,
32,624✔
391
            (full, isArray: string, key: string): string => {
32,624✔
392
                if (!parameters.hasOwnProperty(key)) {
74,558!
393
                    return full
×
394
                }
×
395

74,558✔
396
                const value: any = parameters[key]
74,558✔
397

74,558✔
398
                if (isArray) {
74,558✔
399
                    return value
216✔
400
                        .map((v: any) => {
216✔
401
                            escapedParameters.push(v)
310✔
402
                            return this.createParameter(
310✔
403
                                key,
310✔
404
                                escapedParameters.length - 1,
310✔
405
                            )
310✔
406
                        })
216✔
407
                        .join(", ")
216✔
408
                }
216✔
409

74,342✔
410
                if (typeof value === "function") {
74,558!
411
                    return value()
×
412
                }
×
413

74,342✔
414
                if (typeof value === "boolean") {
74,558✔
415
                    return value ? "1" : "0"
304✔
416
                }
304✔
417

74,038✔
418
                escapedParameters.push(value)
74,038✔
419

74,038✔
420
                return this.createParameter(key, escapedParameters.length - 1)
74,038✔
421
            },
32,624✔
422
        ) // todo: make replace only in value statements, otherwise problems
32,624✔
423
        return [sql, escapedParameters]
32,624✔
424
    }
32,624✔
425

26✔
426
    /**
26✔
427
     * Escapes a column name.
26✔
428
     */
26✔
429
    escape(columnName: string): string {
26✔
430
        return `"${columnName}"`
2,862,046✔
431
    }
2,862,046✔
432

26✔
433
    /**
26✔
434
     * Build full table name with database name, schema name and table name.
26✔
435
     * Oracle does not support table schemas. One user can have only one schema.
26✔
436
     */
26✔
437
    buildTableName(
26✔
438
        tableName: string,
606,048✔
439
        schema?: string,
606,048✔
440
        database?: string,
606,048✔
441
    ): string {
606,048✔
442
        const tablePath = [tableName]
606,048✔
443

606,048✔
444
        if (schema) {
606,048✔
445
            tablePath.unshift(schema)
590,838✔
446
        }
590,838✔
447

606,048✔
448
        return tablePath.join(".")
606,048✔
449
    }
606,048✔
450

26✔
451
    /**
26✔
452
     * Parse a target table name or other types and return a normalized table definition.
26✔
453
     */
26✔
454
    parseTableName(
26✔
455
        target: EntityMetadata | Table | View | TableForeignKey | string,
955,778✔
456
    ): { database?: string; schema?: string; tableName: string } {
955,778✔
457
        const driverDatabase = this.database
955,778✔
458
        const driverSchema = this.schema
955,778✔
459

955,778✔
460
        if (InstanceChecker.isTable(target) || InstanceChecker.isView(target)) {
955,778✔
461
            const parsed = this.parseTableName(target.name)
317,016✔
462

317,016✔
463
            return {
317,016✔
464
                database: target.database || parsed.database || driverDatabase,
317,016!
465
                schema: target.schema || parsed.schema || driverSchema,
317,016!
466
                tableName: parsed.tableName,
317,016✔
467
            }
317,016✔
468
        }
317,016✔
469

638,762✔
470
        if (InstanceChecker.isTableForeignKey(target)) {
955,778✔
471
            const parsed = this.parseTableName(target.referencedTableName)
6,116✔
472

6,116✔
473
            return {
6,116✔
474
                database:
6,116✔
475
                    target.referencedDatabase ||
6,116✔
476
                    parsed.database ||
6,116!
477
                    driverDatabase,
6,116✔
478
                schema:
6,116✔
479
                    target.referencedSchema || parsed.schema || driverSchema,
6,116!
480
                tableName: parsed.tableName,
6,116✔
481
            }
6,116✔
482
        }
6,116✔
483

632,646✔
484
        if (InstanceChecker.isEntityMetadata(target)) {
955,778✔
485
            // EntityMetadata tableName is never a path
300,930✔
486

300,930✔
487
            return {
300,930✔
488
                database: target.database || driverDatabase,
300,930✔
489
                schema: target.schema || driverSchema,
300,930✔
490
                tableName: target.tableName,
300,930✔
491
            }
300,930✔
492
        }
300,930✔
493

331,716✔
494
        const parts = target.split(".")
331,716✔
495

331,716✔
496
        if (parts.length === 3) {
955,778!
497
            return {
×
498
                database: parts[0] || driverDatabase,
×
499
                schema: parts[1] || driverSchema,
×
500
                tableName: parts[2],
×
501
            }
×
502
        } else if (parts.length === 2) {
955,778✔
503
            return {
6,046✔
504
                database: driverDatabase,
6,046✔
505
                schema: parts[0] || driverSchema,
6,046!
506
                tableName: parts[1],
6,046✔
507
            }
6,046✔
508
        } else {
331,716✔
509
            return {
325,670✔
510
                database: driverDatabase,
325,670✔
511
                schema: driverSchema,
325,670✔
512
                tableName: target,
325,670✔
513
            }
325,670✔
514
        }
325,670✔
515
    }
955,778✔
516

26✔
517
    /**
26✔
518
     * Prepares given value to a value to be persisted, based on its column type and metadata.
26✔
519
     */
26✔
520
    preparePersistentValue(value: any, columnMetadata: ColumnMetadata): any {
26✔
521
        if (columnMetadata.transformer)
59,524✔
522
            value = ApplyValueTransformers.transformTo(
59,524✔
523
                columnMetadata.transformer,
80✔
524
                value,
80✔
525
            )
80✔
526

59,524✔
527
        if (value === null || value === undefined) return value
59,524✔
528

48,630✔
529
        if (columnMetadata.type === Boolean) {
59,524✔
530
            return value ? 1 : 0
4,018✔
531
        } else if (columnMetadata.type === "date") {
59,524✔
532
            if (typeof value === "string") value = value.replace(/[^0-9-]/g, "")
6✔
533
            return () =>
6✔
534
                `TO_DATE('${DateUtils.mixedDateToDateString(
6✔
535
                    value,
6✔
536
                )}', 'YYYY-MM-DD')`
6✔
537
        } else if (
44,612✔
538
            columnMetadata.type === Date ||
44,606✔
539
            columnMetadata.type === "timestamp" ||
44,606✔
540
            columnMetadata.type === "timestamp with time zone" ||
44,606✔
541
            columnMetadata.type === "timestamp with local time zone"
44,540✔
542
        ) {
44,606✔
543
            return DateUtils.mixedDateToDate(value)
70✔
544
        } else if (columnMetadata.type === "simple-array") {
44,606✔
545
            return DateUtils.simpleArrayToString(value)
2✔
546
        } else if (columnMetadata.type === "simple-json") {
44,536✔
547
            return DateUtils.simpleJsonToString(value)
10✔
548
        } else if (columnMetadata.type === "json") {
44,534✔
549
            return DateUtils.simpleJsonToString(value)
2✔
550
        }
2✔
551

44,522✔
552
        return value
44,522✔
553
    }
44,522✔
554

26✔
555
    /**
26✔
556
     * Prepares given value to a value to be persisted, based on its column type or metadata.
26✔
557
     */
26✔
558
    prepareHydratedValue(value: any, columnMetadata: ColumnMetadata): any {
26✔
559
        if (value === null || value === undefined)
49,130✔
560
            return columnMetadata.transformer
49,130✔
561
                ? ApplyValueTransformers.transformFrom(
2,626✔
562
                      columnMetadata.transformer,
24✔
563
                      value,
24✔
564
                  )
2,626✔
565
                : value
2,626✔
566

46,504✔
567
        if (columnMetadata.type === Boolean) {
49,130✔
568
            value = !!value
1,424✔
569
        } else if (columnMetadata.type === "date") {
49,130✔
570
            value = DateUtils.mixedDateToDateString(value)
6✔
571
        } else if (columnMetadata.type === "time") {
45,080!
572
            value = DateUtils.mixedTimeToString(value)
×
573
        } else if (
45,074✔
574
            columnMetadata.type === Date ||
45,074✔
575
            columnMetadata.type === "timestamp" ||
45,074✔
576
            columnMetadata.type === "timestamp with time zone" ||
45,074✔
577
            columnMetadata.type === "timestamp with local time zone"
44,188✔
578
        ) {
45,074✔
579
            value = DateUtils.normalizeHydratedDate(value)
890✔
580
        } else if (columnMetadata.type === "simple-array") {
45,074✔
581
            value = DateUtils.stringToSimpleArray(value)
2✔
582
        } else if (columnMetadata.type === "simple-json") {
44,184✔
583
            value = DateUtils.stringToSimpleJson(value)
10✔
584
        } else if (columnMetadata.type === Number) {
44,182✔
585
            // convert to number if number
32,164✔
586
            value = !isNaN(+value) ? parseInt(value) : value
32,164!
587
        }
32,164✔
588

46,504✔
589
        if (columnMetadata.transformer)
46,504✔
590
            value = ApplyValueTransformers.transformFrom(
49,130✔
591
                columnMetadata.transformer,
138✔
592
                value,
138✔
593
            )
138✔
594

46,504✔
595
        return value
46,504✔
596
    }
46,504✔
597

26✔
598
    /**
26✔
599
     * Creates a database type from a given column metadata.
26✔
600
     */
26✔
601
    normalizeType(column: {
26✔
602
        type?: ColumnType
73,910✔
603
        length?: number | string
73,910✔
604
        precision?: number | null
73,910✔
605
        scale?: number
73,910✔
606
        isArray?: boolean
73,910✔
607
    }): string {
73,910✔
608
        if (
73,910✔
609
            column.type === Number ||
73,910✔
610
            column.type === Boolean ||
73,910✔
611
            column.type === "numeric" ||
73,910✔
612
            column.type === "dec" ||
73,910✔
613
            column.type === "decimal" ||
73,910✔
614
            column.type === "int" ||
73,910✔
615
            column.type === "integer" ||
73,910✔
616
            column.type === "smallint"
31,952✔
617
        ) {
73,910✔
618
            return "number"
41,972✔
619
        } else if (
73,910✔
620
            column.type === "real" ||
31,938✔
621
            column.type === "double precision"
31,924✔
622
        ) {
31,938✔
623
            return "float"
28✔
624
        } else if (column.type === String || column.type === "varchar") {
31,938✔
625
            return "varchar2"
26,084✔
626
        } else if (column.type === Date) {
31,910✔
627
            return "timestamp"
410✔
628
        } else if ((column.type as any) === Buffer) {
5,826✔
629
            return "blob"
14✔
630
        } else if (column.type === "uuid") {
5,416✔
631
            return "varchar2"
1,214✔
632
        } else if (column.type === "simple-array") {
5,402✔
633
            return "clob"
14✔
634
        } else if (column.type === "simple-json") {
4,188✔
635
            return "clob"
50✔
636
        } else if (column.type === "json") {
4,174✔
637
            return "json"
14✔
638
        } else {
4,124✔
639
            return (column.type as string) || ""
4,110!
640
        }
4,110✔
641
    }
73,910✔
642

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

56,270✔
649
        if (typeof defaultValue === "number") {
56,270✔
650
            return "" + defaultValue
544✔
651
        }
544✔
652

55,726✔
653
        if (typeof defaultValue === "boolean") {
56,270✔
654
            return defaultValue ? "1" : "0"
36✔
655
        }
36✔
656

55,690✔
657
        if (typeof defaultValue === "function") {
56,270✔
658
            return defaultValue()
1,646✔
659
        }
1,646✔
660

54,044✔
661
        if (typeof defaultValue === "string") {
56,270✔
662
            return `'${defaultValue}'`
752✔
663
        }
752✔
664

53,292✔
665
        if (defaultValue === null || defaultValue === undefined) {
56,270✔
666
            return undefined
53,292✔
667
        }
53,292✔
668

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

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

26✔
681
    /**
26✔
682
     * Calculates column length taking into account the default length values.
26✔
683
     */
26✔
684
    getColumnLength(column: ColumnMetadata | TableColumn): string {
26✔
685
        if (column.length) return column.length.toString()
97,436✔
686

73,160✔
687
        switch (column.type) {
73,160✔
688
            case String:
97,436✔
689
            case "varchar":
97,436✔
690
            case "varchar2":
97,436✔
691
            case "nvarchar2":
97,436✔
692
                return "255"
22,318✔
693
            case "raw":
97,436✔
694
                return "2000"
14✔
695
            case "uuid":
97,436✔
696
                return "36"
932✔
697
            default:
97,436✔
698
                return ""
49,896✔
699
        }
97,436✔
700
    }
97,436✔
701

26✔
702
    createFullType(column: TableColumn): string {
26✔
703
        let type = column.type
28,168✔
704

28,168✔
705
        // used 'getColumnLength()' method, because in Oracle column length is required for some data types.
28,168✔
706
        if (this.getColumnLength(column)) {
28,168✔
707
            type += `(${this.getColumnLength(column)})`
11,526✔
708
        } else if (
28,168✔
709
            column.precision !== null &&
16,642✔
710
            column.precision !== undefined &&
16,642✔
711
            column.scale !== null &&
16,642✔
712
            column.scale !== undefined
52✔
713
        ) {
16,642✔
714
            type += "(" + column.precision + "," + column.scale + ")"
28✔
715
        } else if (
16,642✔
716
            column.precision !== null &&
16,614✔
717
            column.precision !== undefined
16,614✔
718
        ) {
16,614✔
719
            type += "(" + column.precision + ")"
24✔
720
        }
24✔
721

28,168✔
722
        if (column.type === "timestamp with time zone") {
28,168✔
723
            type =
16✔
724
                "TIMESTAMP" +
16✔
725
                (column.precision !== null && column.precision !== undefined
16✔
726
                    ? "(" + column.precision + ")"
16✔
727
                    : "") +
16✔
728
                " WITH TIME ZONE"
16✔
729
        } else if (column.type === "timestamp with local time zone") {
28,168✔
730
            type =
12✔
731
                "TIMESTAMP" +
12✔
732
                (column.precision !== null && column.precision !== undefined
12✔
733
                    ? "(" + column.precision + ")"
12✔
734
                    : "") +
12✔
735
                " WITH LOCAL TIME ZONE"
12✔
736
        }
12✔
737

28,168✔
738
        if (column.isArray) type += " array"
28,168!
739

28,168✔
740
        return type
28,168✔
741
    }
28,168✔
742

26✔
743
    /**
26✔
744
     * Obtains a new database connection to a master server.
26✔
745
     * Used for replication.
26✔
746
     * If replication is not setup then returns default connection's database connection.
26✔
747
     */
26✔
748
    obtainMasterConnection(): Promise<any> {
26✔
749
        return new Promise<any>((ok, fail) => {
22,102✔
750
            if (!this.master) {
22,102!
751
                return fail(new TypeORMError("Driver not Connected"))
×
752
            }
×
753

22,102✔
754
            this.master.getConnection(
22,102✔
755
                (err: any, connection: any, release: Function) => {
22,102✔
756
                    if (err) return fail(err)
22,102!
757
                    ok(connection)
22,102✔
758
                },
22,102✔
759
            )
22,102✔
760
        })
22,102✔
761
    }
22,102✔
762

26✔
763
    /**
26✔
764
     * Obtains a new database connection to a slave server.
26✔
765
     * Used for replication.
26✔
766
     * If replication is not setup then returns master (default) connection's database connection.
26✔
767
     */
26✔
768
    obtainSlaveConnection(): Promise<any> {
26✔
769
        if (!this.slaves.length) return this.obtainMasterConnection()
×
770

×
771
        return new Promise<any>((ok, fail) => {
×
772
            const random = Math.floor(Math.random() * this.slaves.length)
×
773

×
774
            this.slaves[random].getConnection((err: any, connection: any) => {
×
775
                if (err) return fail(err)
×
776
                ok(connection)
×
777
            })
×
778
        })
×
779
    }
×
780

26✔
781
    /**
26✔
782
     * Creates generated map of values generated or returned by database after INSERT query.
26✔
783
     */
26✔
784
    createGeneratedMap(metadata: EntityMetadata, insertResult: ObjectLiteral) {
26✔
785
        if (!insertResult) return undefined
18,900✔
786

18,734✔
787
        return Object.keys(insertResult).reduce((map, key) => {
18,734✔
788
            const column = metadata.findColumnWithDatabaseName(key)
8,356✔
789
            if (column) {
8,356✔
790
                OrmUtils.mergeDeep(
8,356✔
791
                    map,
8,356✔
792
                    column.createValueMap(
8,356✔
793
                        this.prepareHydratedValue(insertResult[key], column),
8,356✔
794
                    ),
8,356✔
795
                )
8,356✔
796
            }
8,356✔
797
            return map
8,356✔
798
        }, {} as ObjectLiteral)
18,734✔
799
    }
18,734✔
800

26✔
801
    /**
26✔
802
     * Differentiate columns of this table and columns from the given column metadatas columns
26✔
803
     * and returns only changed.
26✔
804
     */
26✔
805
    findChangedColumns(
26✔
806
        tableColumns: TableColumn[],
9,184✔
807
        columnMetadatas: ColumnMetadata[],
9,184✔
808
    ): ColumnMetadata[] {
9,184✔
809
        return columnMetadatas.filter((columnMetadata) => {
9,184✔
810
            const tableColumn = tableColumns.find(
28,824✔
811
                (c) => c.name === columnMetadata.databaseName,
28,824✔
812
            )
28,824✔
813
            if (!tableColumn) return false // we don't need new columns, we only need exist and changed
28,824✔
814

28,816✔
815
            const isColumnChanged =
28,816✔
816
                tableColumn.name !== columnMetadata.databaseName ||
28,816✔
817
                tableColumn.type !== this.normalizeType(columnMetadata) ||
28,824✔
818
                tableColumn.length !== this.getColumnLength(columnMetadata) ||
28,824✔
819
                tableColumn.precision !== columnMetadata.precision ||
28,824✔
820
                tableColumn.scale !== columnMetadata.scale ||
28,824✔
821
                // || tableColumn.comment !== columnMetadata.comment
28,798✔
822
                tableColumn.default !== this.normalizeDefault(columnMetadata) ||
28,824✔
823
                tableColumn.isPrimary !== columnMetadata.isPrimary ||
28,824✔
824
                tableColumn.isNullable !== columnMetadata.isNullable ||
28,824✔
825
                tableColumn.asExpression !== columnMetadata.asExpression ||
28,824✔
826
                tableColumn.generatedType !== columnMetadata.generatedType ||
28,824✔
827
                tableColumn.isUnique !==
28,796✔
828
                    this.normalizeIsUnique(columnMetadata) ||
28,824✔
829
                (columnMetadata.generationStrategy !== "uuid" &&
28,794✔
830
                    tableColumn.isGenerated !== columnMetadata.isGenerated)
28,794✔
831

28,824✔
832
            // DEBUG SECTION
28,824✔
833
            // if (isColumnChanged) {
28,824✔
834
            //     console.log("table:", columnMetadata.entityMetadata.tableName)
28,824✔
835
            //     console.log(
28,824✔
836
            //         "name:",
28,824✔
837
            //         tableColumn.name,
28,824✔
838
            //         columnMetadata.databaseName,
28,824✔
839
            //     )
28,824✔
840
            //     console.log(
28,824✔
841
            //         "type:",
28,824✔
842
            //         tableColumn.type,
28,824✔
843
            //         this.normalizeType(columnMetadata),
28,824✔
844
            //     )
28,824✔
845
            //     console.log(
28,824✔
846
            //         "length:",
28,824✔
847
            //         tableColumn.length,
28,824✔
848
            //         columnMetadata.length,
28,824✔
849
            //     )
28,824✔
850
            //     console.log(
28,824✔
851
            //         "precision:",
28,824✔
852
            //         tableColumn.precision,
28,824✔
853
            //         columnMetadata.precision,
28,824✔
854
            //     )
28,824✔
855
            //     console.log("scale:", tableColumn.scale, columnMetadata.scale)
28,824✔
856
            //     console.log(
28,824✔
857
            //         "comment:",
28,824✔
858
            //         tableColumn.comment,
28,824✔
859
            //         columnMetadata.comment,
28,824✔
860
            //     )
28,824✔
861
            //     console.log(
28,824✔
862
            //         "default:",
28,824✔
863
            //         tableColumn.default,
28,824✔
864
            //         this.normalizeDefault(columnMetadata),
28,824✔
865
            //     )
28,824✔
866
            //     console.log(
28,824✔
867
            //         "enum:",
28,824✔
868
            //         tableColumn.enum &&
28,824✔
869
            //             columnMetadata.enum &&
28,824✔
870
            //             !OrmUtils.isArraysEqual(
28,824✔
871
            //                 tableColumn.enum,
28,824✔
872
            //                 columnMetadata.enum.map((val) => val + ""),
28,824✔
873
            //             ),
28,824✔
874
            //     )
28,824✔
875
            //     console.log(
28,824✔
876
            //         "onUpdate:",
28,824✔
877
            //         tableColumn.onUpdate,
28,824✔
878
            //         columnMetadata.onUpdate,
28,824✔
879
            //     )
28,824✔
880
            //     console.log(
28,824✔
881
            //         "isPrimary:",
28,824✔
882
            //         tableColumn.isPrimary,
28,824✔
883
            //         columnMetadata.isPrimary,
28,824✔
884
            //     )
28,824✔
885
            //     console.log(
28,824✔
886
            //         "isNullable:",
28,824✔
887
            //         tableColumn.isNullable,
28,824✔
888
            //         columnMetadata.isNullable,
28,824✔
889
            //     )
28,824✔
890
            //     console.log(
28,824✔
891
            //         "asExpression:",
28,824✔
892
            //         tableColumn.asExpression,
28,824✔
893
            //         columnMetadata.asExpression,
28,824✔
894
            //     )
28,824✔
895
            //     console.log(
28,824✔
896
            //         "generatedType:",
28,824✔
897
            //         tableColumn.generatedType,
28,824✔
898
            //         columnMetadata.generatedType,
28,824✔
899
            //     )
28,824✔
900
            //     console.log(
28,824✔
901
            //         "isUnique:",
28,824✔
902
            //         tableColumn.isUnique,
28,824✔
903
            //         this.normalizeIsUnique(columnMetadata),
28,824✔
904
            //     )
28,824✔
905
            //     console.log(
28,824✔
906
            //         "isGenerated:",
28,824✔
907
            //         tableColumn.isGenerated,
28,824✔
908
            //         columnMetadata.isGenerated,
28,824✔
909
            //     )
28,824✔
910
            //     console.log("==========================================")
28,824✔
911
            // }
28,824✔
912

28,824✔
913
            return isColumnChanged
28,824✔
914
        })
9,184✔
915
    }
9,184✔
916

26✔
917
    /**
26✔
918
     * Returns true if driver supports RETURNING / OUTPUT statement.
26✔
919
     */
26✔
920
    isReturningSqlSupported(): boolean {
26✔
921
        return true
45,194✔
922
    }
45,194✔
923

26✔
924
    /**
26✔
925
     * Returns true if driver supports uuid values generation on its own.
26✔
926
     */
26✔
927
    isUUIDGenerationSupported(): boolean {
26✔
928
        return false
204✔
929
    }
204✔
930

26✔
931
    /**
26✔
932
     * Returns true if driver supports fulltext indices.
26✔
933
     */
26✔
934
    isFullTextColumnTypeSupported(): boolean {
26✔
935
        return false
56✔
936
    }
56✔
937

26✔
938
    /**
26✔
939
     * Creates an escaped parameter.
26✔
940
     */
26✔
941
    createParameter(parameterName: string, index: number): string {
26✔
942
        return this.parametersPrefix + (index + 1)
74,682✔
943
    }
74,682✔
944

26✔
945
    /**
26✔
946
     * Converts column type in to native oracle type.
26✔
947
     */
26✔
948
    columnTypeToNativeParameter(type: ColumnType): any {
26✔
949
        switch (this.normalizeType({ type: type as any })) {
8,360✔
950
            case "number":
8,360✔
951
            case "numeric":
8,360✔
952
            case "int":
8,360✔
953
            case "integer":
8,360✔
954
            case "smallint":
8,360✔
955
            case "dec":
8,360✔
956
            case "decimal":
8,360✔
957
                return this.oracle.DB_TYPE_NUMBER
7,330✔
958
            case "char":
8,360!
959
            case "nchar":
8,360!
960
            case "nvarchar2":
8,360!
961
            case "varchar2":
8,360✔
962
                return this.oracle.DB_TYPE_VARCHAR
560✔
963
            case "blob":
8,360!
964
                return this.oracle.DB_TYPE_BLOB
×
965
            case "simple-json":
8,360!
966
            case "clob":
8,360!
967
                return this.oracle.DB_TYPE_CLOB
×
968
            case "date":
8,360!
969
            case "timestamp":
8,360✔
970
            case "timestamp with time zone":
8,360✔
971
            case "timestamp with local time zone":
8,360✔
972
                return this.oracle.DB_TYPE_TIMESTAMP
470✔
973
            case "json":
8,360!
974
                return this.oracle.DB_TYPE_JSON
×
975
        }
8,360✔
976
    }
8,360✔
977

26✔
978
    // -------------------------------------------------------------------------
26✔
979
    // Protected Methods
26✔
980
    // -------------------------------------------------------------------------
26✔
981

26✔
982
    /**
26✔
983
     * Loads all driver dependencies.
26✔
984
     */
26✔
985
    protected loadDependencies(): void {
26✔
986
        try {
904✔
987
            const oracle = this.options.driver || PlatformTools.load("oracledb")
904✔
988
            this.oracle = oracle
904✔
989
        } catch (e) {
904!
990
            throw new DriverPackageNotInstalledError("Oracle", "oracledb")
×
991
        }
×
992
        const thickMode = this.options.thickMode
904✔
993
        if (thickMode) {
904!
994
            typeof thickMode === "object"
×
995
                ? this.oracle.initOracleClient(thickMode)
×
996
                : this.oracle.initOracleClient()
×
997
        }
×
998
    }
904✔
999

26✔
1000
    /**
26✔
1001
     * Creates a new connection pool for a given database credentials.
26✔
1002
     */
26✔
1003
    protected async createPool(
26✔
1004
        options: OracleConnectionOptions,
904✔
1005
        credentials: OracleConnectionCredentialsOptions,
904✔
1006
    ): Promise<any> {
904✔
1007
        credentials = Object.assign(
904✔
1008
            {},
904✔
1009
            credentials,
904✔
1010
            DriverUtils.buildDriverOptions(credentials),
904✔
1011
        ) // todo: do it better way
904✔
1012

904✔
1013
        if (!credentials.connectString) {
904✔
1014
            let address = `(PROTOCOL=TCP)`
904✔
1015

904✔
1016
            if (credentials.host) {
904✔
1017
                address += `(HOST=${credentials.host})`
904✔
1018
            }
904✔
1019

904✔
1020
            if (credentials.port) {
904✔
1021
                address += `(PORT=${credentials.port})`
904✔
1022
            }
904✔
1023

904✔
1024
            let connectData = `(SERVER=DEDICATED)`
904✔
1025

904✔
1026
            if (credentials.sid) {
904!
1027
                connectData += `(SID=${credentials.sid})`
×
1028
            }
×
1029

904✔
1030
            if (credentials.serviceName) {
904✔
1031
                connectData += `(SERVICE_NAME=${credentials.serviceName})`
904✔
1032
            }
904✔
1033

904✔
1034
            const connectString = `(DESCRIPTION=(ADDRESS=${address})(CONNECT_DATA=${connectData}))`
904✔
1035
            Object.assign(credentials, { connectString })
904✔
1036
        }
904✔
1037

904✔
1038
        // build connection options for the driver
904✔
1039
        const connectionOptions = Object.assign(
904✔
1040
            {},
904✔
1041
            {
904✔
1042
                user: credentials.username,
904✔
1043
                password: credentials.password,
904✔
1044
                connectString: credentials.connectString,
904✔
1045
            },
904✔
1046
            {
904✔
1047
                poolMax: options.poolSize,
904✔
1048
            },
904✔
1049
            options.extra || {},
904✔
1050
        )
904✔
1051

904✔
1052
        // pooling is enabled either when its set explicitly to true,
904✔
1053
        // either when its not defined at all (e.g. enabled by default)
904✔
1054
        return new Promise<void>((ok, fail) => {
904✔
1055
            this.oracle.createPool(connectionOptions, (err: any, pool: any) => {
904✔
1056
                if (err) return fail(err)
904!
1057
                ok(pool)
904✔
1058
            })
904✔
1059
        })
904✔
1060
    }
904✔
1061

26✔
1062
    /**
26✔
1063
     * Closes connection pool.
26✔
1064
     */
26✔
1065
    protected async closePool(pool: any): Promise<void> {
26✔
1066
        return new Promise<void>((ok, fail) => {
904✔
1067
            pool.close((err: any) => (err ? fail(err) : ok()))
904!
1068
            pool = undefined
904✔
1069
        })
904✔
1070
    }
904✔
1071
}
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