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

typeorm / typeorm / 15219332477

23 May 2025 09:13PM UTC coverage: 17.216% (-59.1%) from 76.346%
15219332477

Pull #11332

github

naorpeled
cr comments - move if block
Pull Request #11332: feat: add new undefined and null behavior flags

1603 of 12759 branches covered (12.56%)

Branch coverage included in aggregate %.

0 of 31 new or added lines in 3 files covered. (0.0%)

14132 existing lines in 166 files now uncovered.

4731 of 24033 relevant lines covered (19.69%)

60.22 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

135
    /**
136
     * Returns type of upsert supported by driver if any
137
     */
UNCOV
138
    supportedUpsertTypes: UpsertType[] = []
×
139

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

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

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

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

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

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

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

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

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

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

UNCOV
256
    cteCapabilities: CteCapabilities = {
×
257
        enabled: true,
258
    }
259

UNCOV
260
    dummyTableName = "DUAL"
×
261

262
    // -------------------------------------------------------------------------
263
    // Constructor
264
    // -------------------------------------------------------------------------
265

266
    constructor(connection: DataSource) {
UNCOV
267
        this.connection = connection
×
UNCOV
268
        this.options = connection.options as OracleConnectionOptions
×
269

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

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

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

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

298
    /**
299
     * Performs connection to the database.
300
     * Based on pooling options, it can either create connection immediately,
301
     * either create a pool and create connection when needed.
302
     */
303
    async connect(): Promise<void> {
UNCOV
304
        this.oracle.fetchAsString = [this.oracle.DB_TYPE_CLOB]
×
UNCOV
305
        this.oracle.fetchAsBuffer = [this.oracle.DB_TYPE_BLOB]
×
UNCOV
306
        if (this.options.replication) {
×
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 {
UNCOV
317
            this.master = await this.createPool(this.options, this.options)
×
318
        }
319

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

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

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

UNCOV
331
            await queryRunner.release()
×
332
        }
333
    }
334

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

342
    /**
343
     * Closes connection with the database.
344
     */
345
    async disconnect(): Promise<void> {
UNCOV
346
        if (!this.master)
×
347
            return Promise.reject(new ConnectionIsNotSetError("oracle"))
×
348

UNCOV
349
        await this.closePool(this.master)
×
UNCOV
350
        await Promise.all(this.slaves.map((slave) => this.closePool(slave)))
×
UNCOV
351
        this.master = undefined
×
UNCOV
352
        this.slaves = []
×
353
    }
354

355
    /**
356
     * Creates a schema builder used to build and sync a schema.
357
     */
358
    createSchemaBuilder() {
UNCOV
359
        return new RdbmsSchemaBuilder(this.connection)
×
360
    }
361

362
    /**
363
     * Creates a query runner used to execute database queries.
364
     */
365
    createQueryRunner(mode: ReplicationMode) {
UNCOV
366
        return new OracleQueryRunner(this, mode)
×
367
    }
368

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

UNCOV
388
        const parameterIndexMap = new Map<string, number>()
×
UNCOV
389
        sql = sql.replace(
×
390
            /:(\.\.\.)?([A-Za-z0-9_.]+)/g,
391
            (full, isArray: string, key: string): string => {
UNCOV
392
                if (!parameters.hasOwnProperty(key)) {
×
393
                    return full
×
394
                }
395

UNCOV
396
                if (parameterIndexMap.has(key)) {
×
UNCOV
397
                    return this.parametersPrefix + parameterIndexMap.get(key)
×
398
                }
399

UNCOV
400
                const value: any = parameters[key]
×
401

UNCOV
402
                if (isArray) {
×
UNCOV
403
                    return value
×
404
                        .map((v: any) => {
UNCOV
405
                            escapedParameters.push(v)
×
UNCOV
406
                            return this.createParameter(
×
407
                                key,
408
                                escapedParameters.length - 1,
409
                            )
410
                        })
411
                        .join(", ")
412
                }
413

UNCOV
414
                if (typeof value === "function") {
×
415
                    return value()
×
416
                }
417

UNCOV
418
                if (typeof value === "boolean") {
×
UNCOV
419
                    return value ? "1" : "0"
×
420
                }
421

UNCOV
422
                escapedParameters.push(value)
×
UNCOV
423
                parameterIndexMap.set(key, escapedParameters.length)
×
UNCOV
424
                return this.createParameter(key, escapedParameters.length - 1)
×
425
            },
426
        ) // todo: make replace only in value statements, otherwise problems
UNCOV
427
        return [sql, escapedParameters]
×
428
    }
429

430
    /**
431
     * Escapes a column name.
432
     */
433
    escape(columnName: string): string {
UNCOV
434
        return `"${columnName}"`
×
435
    }
436

437
    /**
438
     * Build full table name with database name, schema name and table name.
439
     * Oracle does not support table schemas. One user can have only one schema.
440
     */
441
    buildTableName(
442
        tableName: string,
443
        schema?: string,
444
        database?: string,
445
    ): string {
UNCOV
446
        const tablePath = [tableName]
×
447

UNCOV
448
        if (schema) {
×
UNCOV
449
            tablePath.unshift(schema)
×
450
        }
451

UNCOV
452
        return tablePath.join(".")
×
453
    }
454

455
    /**
456
     * Parse a target table name or other types and return a normalized table definition.
457
     */
458
    parseTableName(
459
        target: EntityMetadata | Table | View | TableForeignKey | string,
460
    ): { database?: string; schema?: string; tableName: string } {
UNCOV
461
        const driverDatabase = this.database
×
UNCOV
462
        const driverSchema = this.schema
×
463

UNCOV
464
        if (InstanceChecker.isTable(target) || InstanceChecker.isView(target)) {
×
UNCOV
465
            const parsed = this.parseTableName(target.name)
×
466

UNCOV
467
            return {
×
468
                database: target.database || parsed.database || driverDatabase,
×
469
                schema: target.schema || parsed.schema || driverSchema,
×
470
                tableName: parsed.tableName,
471
            }
472
        }
473

UNCOV
474
        if (InstanceChecker.isTableForeignKey(target)) {
×
UNCOV
475
            const parsed = this.parseTableName(target.referencedTableName)
×
476

UNCOV
477
            return {
×
478
                database:
479
                    target.referencedDatabase ||
×
480
                    parsed.database ||
481
                    driverDatabase,
482
                schema:
483
                    target.referencedSchema || parsed.schema || driverSchema,
×
484
                tableName: parsed.tableName,
485
            }
486
        }
487

UNCOV
488
        if (InstanceChecker.isEntityMetadata(target)) {
×
489
            // EntityMetadata tableName is never a path
490

UNCOV
491
            return {
×
492
                database: target.database || driverDatabase,
×
493
                schema: target.schema || driverSchema,
×
494
                tableName: target.tableName,
495
            }
496
        }
497

UNCOV
498
        const parts = target.split(".")
×
499

UNCOV
500
        if (parts.length === 3) {
×
501
            return {
×
502
                database: parts[0] || driverDatabase,
×
503
                schema: parts[1] || driverSchema,
×
504
                tableName: parts[2],
505
            }
UNCOV
506
        } else if (parts.length === 2) {
×
UNCOV
507
            return {
×
508
                database: driverDatabase,
509
                schema: parts[0] || driverSchema,
×
510
                tableName: parts[1],
511
            }
512
        } else {
UNCOV
513
            return {
×
514
                database: driverDatabase,
515
                schema: driverSchema,
516
                tableName: target,
517
            }
518
        }
519
    }
520

521
    /**
522
     * Prepares given value to a value to be persisted, based on its column type and metadata.
523
     */
524
    preparePersistentValue(value: any, columnMetadata: ColumnMetadata): any {
UNCOV
525
        if (columnMetadata.transformer)
×
UNCOV
526
            value = ApplyValueTransformers.transformTo(
×
527
                columnMetadata.transformer,
528
                value,
529
            )
530

UNCOV
531
        if (value === null || value === undefined) return value
×
532

UNCOV
533
        if (columnMetadata.type === Boolean) {
×
UNCOV
534
            return value ? 1 : 0
×
UNCOV
535
        } else if (columnMetadata.type === "date") {
×
UNCOV
536
            if (typeof value === "string") value = value.replace(/[^0-9-]/g, "")
×
UNCOV
537
            return () =>
×
UNCOV
538
                `TO_DATE('${DateUtils.mixedDateToDateString(
×
539
                    value,
540
                )}', 'YYYY-MM-DD')`
UNCOV
541
        } else if (
×
542
            columnMetadata.type === Date ||
×
543
            columnMetadata.type === "timestamp" ||
544
            columnMetadata.type === "timestamp with time zone" ||
545
            columnMetadata.type === "timestamp with local time zone"
546
        ) {
UNCOV
547
            return DateUtils.mixedDateToDate(value)
×
UNCOV
548
        } else if (columnMetadata.type === "simple-array") {
×
UNCOV
549
            return DateUtils.simpleArrayToString(value)
×
UNCOV
550
        } else if (columnMetadata.type === "simple-json") {
×
UNCOV
551
            return DateUtils.simpleJsonToString(value)
×
UNCOV
552
        } else if (columnMetadata.type === "json") {
×
UNCOV
553
            return DateUtils.simpleJsonToString(value)
×
554
        }
555

UNCOV
556
        return value
×
557
    }
558

559
    /**
560
     * Prepares given value to a value to be persisted, based on its column type or metadata.
561
     */
562
    prepareHydratedValue(value: any, columnMetadata: ColumnMetadata): any {
UNCOV
563
        if (value === null || value === undefined)
×
UNCOV
564
            return columnMetadata.transformer
×
565
                ? ApplyValueTransformers.transformFrom(
566
                      columnMetadata.transformer,
567
                      value,
568
                  )
569
                : value
570

UNCOV
571
        if (columnMetadata.type === Boolean) {
×
UNCOV
572
            value = !!value
×
UNCOV
573
        } else if (columnMetadata.type === "date") {
×
UNCOV
574
            value = DateUtils.mixedDateToDateString(value)
×
UNCOV
575
        } else if (columnMetadata.type === "time") {
×
576
            value = DateUtils.mixedTimeToString(value)
×
UNCOV
577
        } else if (
×
578
            columnMetadata.type === Date ||
×
579
            columnMetadata.type === "timestamp" ||
580
            columnMetadata.type === "timestamp with time zone" ||
581
            columnMetadata.type === "timestamp with local time zone"
582
        ) {
UNCOV
583
            value = DateUtils.normalizeHydratedDate(value)
×
UNCOV
584
        } else if (columnMetadata.type === "simple-array") {
×
UNCOV
585
            value = DateUtils.stringToSimpleArray(value)
×
UNCOV
586
        } else if (columnMetadata.type === "simple-json") {
×
UNCOV
587
            value = DateUtils.stringToSimpleJson(value)
×
UNCOV
588
        } else if (columnMetadata.type === Number) {
×
589
            // convert to number if number
UNCOV
590
            value = !isNaN(+value) ? parseInt(value) : value
×
591
        }
592

UNCOV
593
        if (columnMetadata.transformer)
×
UNCOV
594
            value = ApplyValueTransformers.transformFrom(
×
595
                columnMetadata.transformer,
596
                value,
597
            )
598

UNCOV
599
        return value
×
600
    }
601

602
    /**
603
     * Creates a database type from a given column metadata.
604
     */
605
    normalizeType(column: {
606
        type?: ColumnType
607
        length?: number | string
608
        precision?: number | null
609
        scale?: number
610
        isArray?: boolean
611
    }): string {
UNCOV
612
        if (
×
613
            column.type === Number ||
×
614
            column.type === Boolean ||
615
            column.type === "numeric" ||
616
            column.type === "dec" ||
617
            column.type === "decimal" ||
618
            column.type === "int" ||
619
            column.type === "integer" ||
620
            column.type === "smallint"
621
        ) {
UNCOV
622
            return "number"
×
UNCOV
623
        } else if (
×
624
            column.type === "real" ||
×
625
            column.type === "double precision"
626
        ) {
UNCOV
627
            return "float"
×
UNCOV
628
        } else if (column.type === String || column.type === "varchar") {
×
UNCOV
629
            return "varchar2"
×
UNCOV
630
        } else if (column.type === Date) {
×
UNCOV
631
            return "timestamp"
×
UNCOV
632
        } else if ((column.type as any) === Buffer) {
×
UNCOV
633
            return "blob"
×
UNCOV
634
        } else if (column.type === "uuid") {
×
UNCOV
635
            return "varchar2"
×
UNCOV
636
        } else if (column.type === "simple-array") {
×
UNCOV
637
            return "clob"
×
UNCOV
638
        } else if (column.type === "simple-json") {
×
UNCOV
639
            return "clob"
×
UNCOV
640
        } else if (column.type === "json") {
×
UNCOV
641
            return "json"
×
642
        } else {
UNCOV
643
            return (column.type as string) || ""
×
644
        }
645
    }
646

647
    /**
648
     * Normalizes "default" value of the column.
649
     */
650
    normalizeDefault(columnMetadata: ColumnMetadata): string | undefined {
UNCOV
651
        const defaultValue = columnMetadata.default
×
652

UNCOV
653
        if (typeof defaultValue === "number") {
×
UNCOV
654
            return "" + defaultValue
×
655
        }
656

UNCOV
657
        if (typeof defaultValue === "boolean") {
×
UNCOV
658
            return defaultValue ? "1" : "0"
×
659
        }
660

UNCOV
661
        if (typeof defaultValue === "function") {
×
UNCOV
662
            return defaultValue()
×
663
        }
664

UNCOV
665
        if (typeof defaultValue === "string") {
×
UNCOV
666
            return `'${defaultValue}'`
×
667
        }
668

UNCOV
669
        if (defaultValue === null || defaultValue === undefined) {
×
UNCOV
670
            return undefined
×
671
        }
672

673
        return `${defaultValue}`
×
674
    }
675

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

685
    /**
686
     * Calculates column length taking into account the default length values.
687
     */
688
    getColumnLength(column: ColumnMetadata | TableColumn): string {
UNCOV
689
        if (column.length) return column.length.toString()
×
690

UNCOV
691
        switch (column.type) {
×
692
            case String:
693
            case "varchar":
694
            case "varchar2":
695
            case "nvarchar2":
UNCOV
696
                return "255"
×
697
            case "raw":
UNCOV
698
                return "2000"
×
699
            case "uuid":
UNCOV
700
                return "36"
×
701
            default:
UNCOV
702
                return ""
×
703
        }
704
    }
705

706
    createFullType(column: TableColumn): string {
UNCOV
707
        let type = column.type
×
708

709
        // used 'getColumnLength()' method, because in Oracle column length is required for some data types.
UNCOV
710
        if (this.getColumnLength(column)) {
×
UNCOV
711
            type += `(${this.getColumnLength(column)})`
×
UNCOV
712
        } else if (
×
713
            column.precision !== null &&
×
714
            column.precision !== undefined &&
715
            column.scale !== null &&
716
            column.scale !== undefined
717
        ) {
UNCOV
718
            type += "(" + column.precision + "," + column.scale + ")"
×
UNCOV
719
        } else if (
×
720
            column.precision !== null &&
×
721
            column.precision !== undefined
722
        ) {
UNCOV
723
            type += "(" + column.precision + ")"
×
724
        }
725

UNCOV
726
        if (column.type === "timestamp with time zone") {
×
UNCOV
727
            type =
×
728
                "TIMESTAMP" +
729
                (column.precision !== null && column.precision !== undefined
×
730
                    ? "(" + column.precision + ")"
731
                    : "") +
732
                " WITH TIME ZONE"
UNCOV
733
        } else if (column.type === "timestamp with local time zone") {
×
UNCOV
734
            type =
×
735
                "TIMESTAMP" +
736
                (column.precision !== null && column.precision !== undefined
×
737
                    ? "(" + column.precision + ")"
738
                    : "") +
739
                " WITH LOCAL TIME ZONE"
740
        }
741

UNCOV
742
        if (column.isArray) type += " array"
×
743

UNCOV
744
        return type
×
745
    }
746

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

UNCOV
758
            this.master.getConnection(
×
759
                (err: any, connection: any, release: Function) => {
UNCOV
760
                    if (err) return fail(err)
×
UNCOV
761
                    ok(connection)
×
762
                },
763
            )
764
        })
765
    }
766

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

775
        return new Promise<any>((ok, fail) => {
×
776
            const random = Math.floor(Math.random() * this.slaves.length)
×
777

778
            this.slaves[random].getConnection((err: any, connection: any) => {
×
779
                if (err) return fail(err)
×
780
                ok(connection)
×
781
            })
782
        })
783
    }
784

785
    /**
786
     * Creates generated map of values generated or returned by database after INSERT query.
787
     */
788
    createGeneratedMap(metadata: EntityMetadata, insertResult: ObjectLiteral) {
UNCOV
789
        if (!insertResult) return undefined
×
790

UNCOV
791
        return Object.keys(insertResult).reduce((map, key) => {
×
UNCOV
792
            const column = metadata.findColumnWithDatabaseName(key)
×
UNCOV
793
            if (column) {
×
UNCOV
794
                OrmUtils.mergeDeep(
×
795
                    map,
796
                    column.createValueMap(
797
                        this.prepareHydratedValue(insertResult[key], column),
798
                    ),
799
                )
800
            }
UNCOV
801
            return map
×
802
        }, {} as ObjectLiteral)
803
    }
804

805
    /**
806
     * Differentiate columns of this table and columns from the given column metadatas columns
807
     * and returns only changed.
808
     */
809
    findChangedColumns(
810
        tableColumns: TableColumn[],
811
        columnMetadatas: ColumnMetadata[],
812
    ): ColumnMetadata[] {
UNCOV
813
        return columnMetadatas.filter((columnMetadata) => {
×
UNCOV
814
            const tableColumn = tableColumns.find(
×
UNCOV
815
                (c) => c.name === columnMetadata.databaseName,
×
816
            )
UNCOV
817
            if (!tableColumn) return false // we don't need new columns, we only need exist and changed
×
818

819
            const isColumnChanged =
UNCOV
820
                tableColumn.name !== columnMetadata.databaseName ||
×
821
                tableColumn.type !== this.normalizeType(columnMetadata) ||
822
                tableColumn.length !== this.getColumnLength(columnMetadata) ||
823
                tableColumn.precision !== columnMetadata.precision ||
824
                tableColumn.scale !== columnMetadata.scale ||
825
                // || tableColumn.comment !== columnMetadata.comment
826
                tableColumn.default !== this.normalizeDefault(columnMetadata) ||
827
                tableColumn.isPrimary !== columnMetadata.isPrimary ||
828
                tableColumn.isNullable !== columnMetadata.isNullable ||
829
                tableColumn.asExpression !== columnMetadata.asExpression ||
830
                tableColumn.generatedType !== columnMetadata.generatedType ||
831
                tableColumn.isUnique !==
832
                    this.normalizeIsUnique(columnMetadata) ||
833
                (columnMetadata.generationStrategy !== "uuid" &&
834
                    tableColumn.isGenerated !== columnMetadata.isGenerated)
835

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

UNCOV
917
            return isColumnChanged
×
918
        })
919
    }
920

921
    /**
922
     * Returns true if driver supports RETURNING / OUTPUT statement.
923
     */
924
    isReturningSqlSupported(): boolean {
UNCOV
925
        return true
×
926
    }
927

928
    /**
929
     * Returns true if driver supports uuid values generation on its own.
930
     */
931
    isUUIDGenerationSupported(): boolean {
UNCOV
932
        return false
×
933
    }
934

935
    /**
936
     * Returns true if driver supports fulltext indices.
937
     */
938
    isFullTextColumnTypeSupported(): boolean {
UNCOV
939
        return false
×
940
    }
941

942
    /**
943
     * Creates an escaped parameter.
944
     */
945
    createParameter(parameterName: string, index: number): string {
UNCOV
946
        return this.parametersPrefix + (index + 1)
×
947
    }
948

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

982
    // -------------------------------------------------------------------------
983
    // Protected Methods
984
    // -------------------------------------------------------------------------
985

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

1004
    /**
1005
     * Creates a new connection pool for a given database credentials.
1006
     */
1007
    protected async createPool(
1008
        options: OracleConnectionOptions,
1009
        credentials: OracleConnectionCredentialsOptions,
1010
    ): Promise<any> {
UNCOV
1011
        credentials = Object.assign(
×
1012
            {},
1013
            credentials,
1014
            DriverUtils.buildDriverOptions(credentials),
1015
        ) // todo: do it better way
1016

UNCOV
1017
        if (!credentials.connectString) {
×
UNCOV
1018
            let address = `(PROTOCOL=TCP)`
×
1019

UNCOV
1020
            if (credentials.host) {
×
UNCOV
1021
                address += `(HOST=${credentials.host})`
×
1022
            }
1023

UNCOV
1024
            if (credentials.port) {
×
UNCOV
1025
                address += `(PORT=${credentials.port})`
×
1026
            }
1027

UNCOV
1028
            let connectData = `(SERVER=DEDICATED)`
×
1029

UNCOV
1030
            if (credentials.sid) {
×
1031
                connectData += `(SID=${credentials.sid})`
×
1032
            }
1033

UNCOV
1034
            if (credentials.serviceName) {
×
UNCOV
1035
                connectData += `(SERVICE_NAME=${credentials.serviceName})`
×
1036
            }
1037

UNCOV
1038
            const connectString = `(DESCRIPTION=(ADDRESS=${address})(CONNECT_DATA=${connectData}))`
×
UNCOV
1039
            Object.assign(credentials, { connectString })
×
1040
        }
1041

1042
        // build connection options for the driver
UNCOV
1043
        const connectionOptions = Object.assign(
×
1044
            {},
1045
            {
1046
                user: credentials.username,
1047
                password: credentials.password,
1048
                connectString: credentials.connectString,
1049
            },
1050
            {
1051
                poolMax: options.poolSize,
1052
            },
1053
            options.extra || {},
×
1054
        )
1055

1056
        // pooling is enabled either when its set explicitly to true,
1057
        // either when its not defined at all (e.g. enabled by default)
UNCOV
1058
        return new Promise<void>((ok, fail) => {
×
UNCOV
1059
            this.oracle.createPool(connectionOptions, (err: any, pool: any) => {
×
UNCOV
1060
                if (err) return fail(err)
×
UNCOV
1061
                ok(pool)
×
1062
            })
1063
        })
1064
    }
1065

1066
    /**
1067
     * Closes connection pool.
1068
     */
1069
    protected async closePool(pool: any): Promise<void> {
UNCOV
1070
        return new Promise<void>((ok, fail) => {
×
UNCOV
1071
            pool.close((err: any) => (err ? fail(err) : ok()))
×
UNCOV
1072
            pool = undefined
×
1073
        })
1074
    }
1075
}
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

© 2025 Coveralls, Inc