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

typeorm / typeorm / 20030970342

08 Dec 2025 02:10PM UTC coverage: 80.764% (-0.007%) from 80.771%
20030970342

push

github

web-flow
chore: disable eslint errors for chai assertions (#11833)

26554 of 32253 branches covered (82.33%)

Branch coverage included in aggregate %.

26 of 61 new or added lines in 5 files covered. (42.62%)

1 existing line in 1 file now uncovered.

91386 of 113778 relevant lines covered (80.32%)

68140.32 hits per line

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

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

26✔
32
/**
26✔
33
 * Organizes communication with Cockroach DBMS.
26✔
34
 */
26✔
35
export class CockroachDriver 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
     * Cockroach underlying library.
26✔
47
     */
26✔
48
    postgres: 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
     * We store all created query runners because we need to release them.
26✔
63
     */
26✔
64
    connectedQueryRunners: QueryRunner[] = []
26✔
65

26✔
66
    // -------------------------------------------------------------------------
26✔
67
    // Public Implemented Properties
26✔
68
    // -------------------------------------------------------------------------
26✔
69

26✔
70
    /**
26✔
71
     * Connection options.
26✔
72
     */
26✔
73
    options: CockroachConnectionOptions
26✔
74

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

26✔
80
    /**
26✔
81
     * Schema name used to perform all write queries.
26✔
82
     */
26✔
83
    schema?: string
26✔
84

26✔
85
    /**
26✔
86
     * Schema that's used internally by Postgres for object resolution.
26✔
87
     *
26✔
88
     * Because we never set this we have to track it in separately from the `schema` so
26✔
89
     * we know when we have to specify the full schema or not.
26✔
90
     *
26✔
91
     * In most cases this will be `public`.
26✔
92
     */
26✔
93
    searchSchema?: string
26✔
94

26✔
95
    /**
26✔
96
     * Indicates if replication is enabled.
26✔
97
     */
26✔
98
    isReplicated: boolean = false
26✔
99

26✔
100
    /**
26✔
101
     * Indicates if tree tables are supported by this driver.
26✔
102
     */
26✔
103
    treeSupport = true
26✔
104

26✔
105
    /**
26✔
106
     * Represent transaction support by this driver
26✔
107
     */
26✔
108
    transactionSupport = "nested" as const
26✔
109

26✔
110
    /**
26✔
111
     * Gets list of supported column data types by a driver.
26✔
112
     *
26✔
113
     * @see https://www.cockroachlabs.com/docs/stable/data-types.html
26✔
114
     */
26✔
115
    supportedDataTypes: ColumnType[] = [
26✔
116
        "array",
26✔
117
        "bool",
26✔
118
        "boolean",
26✔
119
        "bytes",
26✔
120
        "bytea",
26✔
121
        "blob",
26✔
122
        "date",
26✔
123
        "enum",
26✔
124
        "geometry",
26✔
125
        "geography",
26✔
126
        "numeric",
26✔
127
        "decimal",
26✔
128
        "dec",
26✔
129
        "float",
26✔
130
        "float4",
26✔
131
        "float8",
26✔
132
        "double precision",
26✔
133
        "real",
26✔
134
        "inet",
26✔
135
        "int",
26✔
136
        "int4",
26✔
137
        "integer",
26✔
138
        "int2",
26✔
139
        "int8",
26✔
140
        "int64",
26✔
141
        "smallint",
26✔
142
        "bigint",
26✔
143
        "interval",
26✔
144
        "string",
26✔
145
        "character varying",
26✔
146
        "character",
26✔
147
        "char",
26✔
148
        "char varying",
26✔
149
        "varchar",
26✔
150
        "text",
26✔
151
        "time",
26✔
152
        "time without time zone",
26✔
153
        "timestamp",
26✔
154
        "timestamptz",
26✔
155
        "timestamp without time zone",
26✔
156
        "timestamp with time zone",
26✔
157
        "json",
26✔
158
        "jsonb",
26✔
159
        "uuid",
26✔
160
    ]
26✔
161

26✔
162
    /**
26✔
163
     * Returns type of upsert supported by driver if any
26✔
164
     */
26✔
165
    supportedUpsertTypes: UpsertType[] = [
26✔
166
        "on-conflict-do-update",
26✔
167
        "primary-key",
26✔
168
    ]
26✔
169

26✔
170
    /**
26✔
171
     * Gets list of spatial column data types.
26✔
172
     */
26✔
173
    spatialTypes: ColumnType[] = ["geometry", "geography"]
26✔
174

26✔
175
    /**
26✔
176
     * Gets list of column data types that support length by a driver.
26✔
177
     */
26✔
178
    withLengthColumnTypes: ColumnType[] = [
26✔
179
        "character varying",
26✔
180
        "char varying",
26✔
181
        "varchar",
26✔
182
        "character",
26✔
183
        "char",
26✔
184
        "string",
26✔
185
    ]
26✔
186

26✔
187
    /**
26✔
188
     * Gets list of column data types that support precision by a driver.
26✔
189
     */
26✔
190
    withPrecisionColumnTypes: ColumnType[] = ["numeric", "decimal", "dec"]
26✔
191

26✔
192
    /**
26✔
193
     * Gets list of column data types that support scale by a driver.
26✔
194
     */
26✔
195
    withScaleColumnTypes: ColumnType[] = ["numeric", "decimal", "dec"]
26✔
196

26✔
197
    /**
26✔
198
     * Orm has special columns and we need to know what database column types should be for those types.
26✔
199
     * Column types are driver dependent.
26✔
200
     */
26✔
201
    mappedDataTypes: MappedColumnTypes = {
26✔
202
        createDate: "timestamptz",
26✔
203
        createDateDefault: "now()",
26✔
204
        updateDate: "timestamptz",
26✔
205
        updateDateDefault: "now()",
26✔
206
        deleteDate: "timestamptz",
26✔
207
        deleteDateNullable: true,
26✔
208
        version: Number,
26✔
209
        treeLevel: Number,
26✔
210
        migrationId: Number,
26✔
211
        migrationName: "varchar",
26✔
212
        migrationTimestamp: "int8",
26✔
213
        cacheId: Number,
26✔
214
        cacheIdentifier: "varchar",
26✔
215
        cacheTime: "int8",
26✔
216
        cacheDuration: Number,
26✔
217
        cacheQuery: "string",
26✔
218
        cacheResult: "string",
26✔
219
        metadataType: "varchar",
26✔
220
        metadataDatabase: "varchar",
26✔
221
        metadataSchema: "varchar",
26✔
222
        metadataTable: "varchar",
26✔
223
        metadataName: "varchar",
26✔
224
        metadataValue: "string",
26✔
225
    }
26✔
226

26✔
227
    /**
26✔
228
     * The prefix used for the parameters
26✔
229
     */
26✔
230
    parametersPrefix: string = "$"
26✔
231

26✔
232
    /**
26✔
233
     * Default values of length, precision and scale depends on column data type.
26✔
234
     * Used in the cases when length/precision/scale is not specified by user.
26✔
235
     */
26✔
236
    dataTypeDefaults: DataTypeDefaults = {
26✔
237
        char: { length: 1 },
26✔
238
    }
26✔
239

26✔
240
    /**
26✔
241
     * No documentation specifying a maximum length for identifiers could be found
26✔
242
     * for CockroarchDb.
26✔
243
     */
26✔
244
    maxAliasLength?: number
26✔
245

26✔
246
    cteCapabilities: CteCapabilities = {
26✔
247
        enabled: true,
26✔
248
        writable: true,
26✔
249
        materializedHint: true,
26✔
250
        requiresRecursiveHint: true,
26✔
251
    }
26✔
252

26✔
253
    // -------------------------------------------------------------------------
26✔
254
    // Constructor
26✔
255
    // -------------------------------------------------------------------------
26✔
256

26✔
257
    constructor(connection: DataSource) {
26✔
258
        this.connection = connection
464✔
259
        this.options = connection.options as CockroachConnectionOptions
464✔
260
        this.isReplicated = this.options.replication ? true : false
464!
261

464✔
262
        // load postgres package
464✔
263
        this.loadDependencies()
464✔
264

464✔
265
        this.database = DriverUtils.buildDriverOptions(
464✔
266
            this.options.replication
464✔
267
                ? this.options.replication.master
464!
268
                : this.options,
464✔
269
        ).database
464✔
270
        this.schema = DriverUtils.buildDriverOptions(this.options).schema
464✔
271

464✔
272
        // ObjectUtils.assign(this.options, DriverUtils.buildDriverOptions(connection.options)); // todo: do it better way
464✔
273
        // validate options to make sure everything is set
464✔
274
        // todo: revisit validation with replication in mind
464✔
275
        // if (!this.options.host)
464✔
276
        //     throw new DriverOptionNotSetError("host");
464✔
277
        // if (!this.options.username)
464✔
278
        //     throw new DriverOptionNotSetError("username");
464✔
279
        // if (!this.options.database)
464✔
280
        //     throw new DriverOptionNotSetError("database");
464✔
281
    }
464✔
282

26✔
283
    // -------------------------------------------------------------------------
26✔
284
    // Public Implemented Methods
26✔
285
    // -------------------------------------------------------------------------
26✔
286

26✔
287
    /**
26✔
288
     * Performs connection to the database.
26✔
289
     * Based on pooling options, it can either create connection immediately,
26✔
290
     * either create a pool and create connection when needed.
26✔
291
     */
26✔
292
    async connect(): Promise<void> {
26✔
293
        if (this.options.replication) {
464!
294
            this.slaves = await Promise.all(
×
295
                this.options.replication.slaves.map((slave) => {
×
296
                    return this.createPool(this.options, slave)
×
297
                }),
×
298
            )
×
299
            this.master = await this.createPool(
×
300
                this.options,
×
301
                this.options.replication.master,
×
302
            )
×
303
        } else {
464✔
304
            this.master = await this.createPool(this.options, this.options)
464✔
305
        }
464✔
306

464✔
307
        if (!this.database || !this.searchSchema) {
464✔
308
            const queryRunner = this.createQueryRunner("master")
464✔
309

464✔
310
            if (!this.database) {
464!
311
                this.database = await queryRunner.getCurrentDatabase()
×
312
            }
×
313

464✔
314
            if (!this.searchSchema) {
464✔
315
                this.searchSchema = await queryRunner.getCurrentSchema()
464✔
316
            }
464✔
317

464✔
318
            await queryRunner.release()
464✔
319
        }
464✔
320

464✔
321
        if (!this.schema) {
464✔
322
            this.schema = this.searchSchema
463✔
323
        }
463✔
324
    }
464✔
325

26✔
326
    /**
26✔
327
     * Makes any action after connection (e.g. create extensions in Postgres driver).
26✔
328
     */
26✔
329
    async afterConnect(): Promise<void> {
26✔
330
        // enable time travel queries
462✔
331
        if (this.options.timeTravelQueries) {
462!
332
            await this.connection.query(
×
333
                `SET default_transaction_use_follower_reads = 'on';`,
×
334
            )
×
335
        }
×
336

462✔
337
        // enable experimental alter column type support (we need it to alter enum types)
462✔
338
        await this.connection.query(
462✔
339
            "SET enable_experimental_alter_column_type_general = true",
462✔
340
        )
462✔
341

462✔
342
        return Promise.resolve()
462✔
343
    }
462✔
344

26✔
345
    /**
26✔
346
     * Closes connection with database.
26✔
347
     */
26✔
348
    async disconnect(): Promise<void> {
26✔
349
        if (!this.master) {
464!
350
            throw new ConnectionIsNotSetError("cockroachdb")
×
351
        }
×
352

464✔
353
        await this.closePool(this.master)
464✔
354
        await Promise.all(this.slaves.map((slave) => this.closePool(slave)))
464✔
355
        this.master = undefined
464✔
356
        this.slaves = []
464✔
357
    }
464✔
358

26✔
359
    /**
26✔
360
     * Creates a schema builder used to build and sync a schema.
26✔
361
     */
26✔
362
    createSchemaBuilder() {
26✔
363
        return new RdbmsSchemaBuilder(this.connection)
1,413✔
364
    }
1,413✔
365

26✔
366
    /**
26✔
367
     * Creates a query runner used to execute database queries.
26✔
368
     */
26✔
369
    createQueryRunner(mode: ReplicationMode) {
26✔
370
        return new CockroachQueryRunner(this, mode)
12,383✔
371
    }
12,383✔
372

26✔
373
    /**
26✔
374
     * Prepares given value to a value to be persisted, based on its column type and metadata.
26✔
375
     */
26✔
376
    preparePersistentValue(value: any, columnMetadata: ColumnMetadata): any {
26✔
377
        if (columnMetadata.transformer)
26,918✔
378
            value = ApplyValueTransformers.transformTo(
26,918✔
379
                columnMetadata.transformer,
36✔
380
                value,
36✔
381
            )
36✔
382

26,918✔
383
        if (value === null || value === undefined) return value
26,918✔
384

24,709✔
385
        if (columnMetadata.type === Boolean) {
26,918✔
386
            return value === true ? 1 : 0
2,003✔
387
        } else if (columnMetadata.type === "date") {
26,918✔
388
            return DateUtils.mixedDateToDateString(value, {
4✔
389
                utc: columnMetadata.utc,
4✔
390
            })
4✔
391
        } else if (columnMetadata.type === "time") {
22,706✔
392
            return DateUtils.mixedDateToTimeString(value)
1✔
393
        } else if (
22,702✔
394
            columnMetadata.type === "datetime" ||
22,701✔
395
            columnMetadata.type === Date ||
22,701✔
396
            columnMetadata.type === "timestamp" ||
22,701✔
397
            columnMetadata.type === "timestamptz" ||
22,701✔
398
            columnMetadata.type === "timestamp with time zone" ||
22,701✔
399
            columnMetadata.type === "timestamp without time zone"
22,659✔
400
        ) {
22,701✔
401
            return DateUtils.mixedDateToDate(value)
43✔
402
        } else if (
22,701✔
403
            ["json", "jsonb", ...this.spatialTypes].indexOf(
22,658✔
404
                columnMetadata.type,
22,658✔
405
            ) >= 0
22,658✔
406
        ) {
22,658✔
407
            return JSON.stringify(value)
24✔
408
        } else if (columnMetadata.type === "simple-array") {
22,658✔
409
            return DateUtils.simpleArrayToString(value)
1✔
410
        } else if (columnMetadata.type === "simple-json") {
22,634✔
411
            return DateUtils.simpleJsonToString(value)
5✔
412
        }
5✔
413

22,628✔
414
        return value
22,628✔
415
    }
22,628✔
416

26✔
417
    /**
26✔
418
     * Prepares given value to a value to be persisted, based on its column type or metadata.
26✔
419
     */
26✔
420
    prepareHydratedValue(value: any, columnMetadata: ColumnMetadata): any {
26✔
421
        if (value === null || value === undefined)
67,379✔
422
            return columnMetadata.transformer
67,379✔
423
                ? ApplyValueTransformers.transformFrom(
3,870✔
424
                      columnMetadata.transformer,
12✔
425
                      value,
12✔
426
                  )
3,870✔
427
                : value
3,870✔
428

63,509✔
429
        // unique_rowid() generates bigint value and should not be converted to number
63,509✔
430
        if (
63,509✔
431
            ([Number, "int4", "smallint", "int2"].some(
63,509✔
432
                (v) => v === columnMetadata.type,
63,509✔
433
            ) &&
63,509✔
434
                !columnMetadata.isArray) ||
67,379✔
435
            columnMetadata.generationStrategy === "increment"
46,857✔
436
        ) {
67,379✔
437
            value = parseInt(value)
16,695✔
438
        } else if (columnMetadata.type === Boolean) {
67,379✔
439
            value = value ? true : false
709✔
440
        } else if (
46,814✔
441
            columnMetadata.type === "datetime" ||
46,105✔
442
            columnMetadata.type === Date ||
46,105✔
443
            columnMetadata.type === "timestamp" ||
46,105✔
444
            columnMetadata.type === "timestamptz" ||
46,105✔
445
            columnMetadata.type === "timestamp with time zone" ||
46,105✔
446
            columnMetadata.type === "timestamp without time zone"
45,629✔
447
        ) {
46,105✔
448
            value = DateUtils.normalizeHydratedDate(value)
477✔
449
        } else if (columnMetadata.type === "date") {
46,105✔
450
            value = DateUtils.mixedDateToDateString(value, {
4✔
451
                utc: columnMetadata.utc,
4✔
452
            })
4✔
453
        } else if (columnMetadata.type === "time") {
45,628✔
454
            value = DateUtils.mixedTimeToString(value)
1✔
455
        } else if (columnMetadata.type === "simple-array") {
45,624✔
456
            value = DateUtils.stringToSimpleArray(value)
1✔
457
        } else if (columnMetadata.type === "simple-json") {
45,623✔
458
            value = DateUtils.stringToSimpleJson(value)
5✔
459
        } else if (
45,622✔
460
            columnMetadata.type === "enum" ||
45,617✔
461
            columnMetadata.type === "simple-enum"
45,506✔
462
        ) {
45,617✔
463
            if (columnMetadata.isArray) {
112✔
464
                if (value === "{}") return []
72✔
465
                if (Array.isArray(value)) return value
72✔
466

42✔
467
                // manually convert enum array to array of values (pg does not support, see https://github.com/brianc/node-pg-types/issues/56)
42✔
468
                value = (value as string)
42✔
469
                    .slice(1, -1)
42✔
470
                    .split(",")
42✔
471
                    .map((val) => {
42✔
472
                        // replace double quotes from the beginning and from the end
68✔
473
                        if (val.startsWith(`"`) && val.endsWith(`"`))
68✔
474
                            val = val.slice(1, -1)
68✔
475
                        // replace escaped backslash and double quotes
68✔
476
                        return val.replace(/\\(\\|")/g, "$1")
68✔
477
                    })
42✔
478

42✔
479
                // convert to number if that exists in possible enum options
42✔
480
                value = value.map((val: string) => {
42✔
481
                    return !isNaN(+val) &&
68✔
482
                        columnMetadata.enum!.indexOf(parseInt(val)) >= 0
24✔
483
                        ? parseInt(val)
68✔
484
                        : val
68✔
485
                })
42✔
486
            } else {
112✔
487
                // convert to number if that exists in possible enum options
40✔
488
                value =
40✔
489
                    !isNaN(+value) &&
40✔
490
                    columnMetadata.enum!.indexOf(parseInt(value)) >= 0
21✔
491
                        ? parseInt(value)
40✔
492
                        : value
40✔
493
            }
40✔
494
        }
112✔
495

63,479✔
496
        if (columnMetadata.transformer)
63,479✔
497
            value = ApplyValueTransformers.transformFrom(
67,379✔
498
                columnMetadata.transformer,
67✔
499
                value,
67✔
500
            )
67✔
501

63,479✔
502
        return value
63,479✔
503
    }
63,479✔
504

26✔
505
    /**
26✔
506
     * Replaces parameters in the given sql with special escaping character
26✔
507
     * and an array of parameter names to be passed to a query.
26✔
508
     */
26✔
509
    escapeQueryWithParameters(
26✔
510
        sql: string,
15,108✔
511
        parameters: ObjectLiteral,
15,108✔
512
        nativeParameters: ObjectLiteral,
15,108✔
513
    ): [string, any[]] {
15,108✔
514
        const escapedParameters: any[] = Object.keys(nativeParameters).map(
15,108✔
515
            (key) => nativeParameters[key],
15,108✔
516
        )
15,108✔
517
        if (!parameters || !Object.keys(parameters).length)
15,108✔
518
            return [sql, escapedParameters]
15,108✔
519

14,411✔
520
        const parameterIndexMap = new Map<string, number>()
14,411✔
521
        sql = sql.replace(
14,411✔
522
            /:(\.\.\.)?([A-Za-z0-9_.]+)/g,
14,411✔
523
            (full, isArray: string, key: string): string => {
14,411✔
524
                if (!parameters.hasOwnProperty(key)) {
34,185✔
525
                    return full
78✔
526
                }
78✔
527

34,107✔
528
                if (parameterIndexMap.has(key)) {
34,185✔
529
                    return this.parametersPrefix + parameterIndexMap.get(key)
7✔
530
                }
7✔
531

34,100✔
532
                const value: any = parameters[key]
34,100✔
533

34,100✔
534
                if (isArray) {
34,185✔
535
                    return value
361✔
536
                        .map((v: any) => {
361✔
537
                            escapedParameters.push(v)
434✔
538
                            return this.createParameter(
434✔
539
                                key,
434✔
540
                                escapedParameters.length - 1,
434✔
541
                            )
434✔
542
                        })
361✔
543
                        .join(", ")
361✔
544
                }
361✔
545

33,739✔
546
                if (typeof value === "function") {
34,185!
547
                    return value()
×
548
                }
×
549

33,739✔
550
                escapedParameters.push(value)
33,739✔
551
                parameterIndexMap.set(key, escapedParameters.length)
33,739✔
552
                return this.createParameter(key, escapedParameters.length - 1)
33,739✔
553
            },
14,411✔
554
        ) // todo: make replace only in value statements, otherwise problems
14,411✔
555
        return [sql, escapedParameters]
14,411✔
556
    }
14,411✔
557

26✔
558
    /**
26✔
559
     * Escapes a column name.
26✔
560
     */
26✔
561
    escape(columnName: string): string {
26✔
562
        return '"' + columnName + '"'
143,735✔
563
    }
143,735✔
564

26✔
565
    /**
26✔
566
     * Build full table name with schema name and table name.
26✔
567
     * E.g. myDB.mySchema.myTable
26✔
568
     */
26✔
569
    buildTableName(tableName: string, schema?: string): string {
26✔
570
        const tablePath = [tableName]
304,114✔
571

304,114✔
572
        if (schema) {
304,114✔
573
            tablePath.unshift(schema)
296,242✔
574
        }
296,242✔
575

304,114✔
576
        return tablePath.join(".")
304,114✔
577
    }
304,114✔
578

26✔
579
    /**
26✔
580
     * Parse a target table name or other types and return a normalized table definition.
26✔
581
     */
26✔
582
    parseTableName(
26✔
583
        target: EntityMetadata | Table | View | TableForeignKey | string,
534,339✔
584
    ): { database?: string; schema?: string; tableName: string } {
534,339✔
585
        const driverDatabase = this.database
534,339✔
586
        const driverSchema = this.schema
534,339✔
587

534,339✔
588
        if (InstanceChecker.isTable(target) || InstanceChecker.isView(target)) {
534,339✔
589
            // name is sometimes a path
179,964✔
590
            const parsed = this.parseTableName(target.name)
179,964✔
591

179,964✔
592
            return {
179,964✔
593
                database: target.database || parsed.database || driverDatabase,
179,964!
594
                schema: target.schema || parsed.schema || driverSchema,
179,964!
595
                tableName: parsed.tableName,
179,964✔
596
            }
179,964✔
597
        }
179,964✔
598

354,375✔
599
        if (InstanceChecker.isTableForeignKey(target)) {
534,339✔
600
            // referencedTableName is sometimes a path
3,078✔
601
            const parsed = this.parseTableName(target.referencedTableName)
3,078✔
602

3,078✔
603
            return {
3,078✔
604
                database:
3,078✔
605
                    target.referencedDatabase ||
3,078✔
606
                    parsed.database ||
3,078!
607
                    driverDatabase,
3,078✔
608
                schema:
3,078✔
609
                    target.referencedSchema || parsed.schema || driverSchema,
3,078!
610
                tableName: parsed.tableName,
3,078✔
611
            }
3,078✔
612
        }
3,078✔
613

351,297✔
614
        if (InstanceChecker.isEntityMetadata(target)) {
534,339✔
615
            // EntityMetadata tableName is never a path
151,031✔
616

151,031✔
617
            return {
151,031✔
618
                database: target.database || driverDatabase,
151,031✔
619
                schema: target.schema || driverSchema,
151,031✔
620
                tableName: target.tableName,
151,031✔
621
            }
151,031✔
622
        }
151,031✔
623

200,266✔
624
        const parts = target.split(".")
200,266✔
625

200,266✔
626
        return {
200,266✔
627
            database: driverDatabase,
200,266✔
628
            schema: (parts.length > 1 ? parts[0] : undefined) || driverSchema,
534,339✔
629
            tableName: parts.length > 1 ? parts[1] : parts[0],
534,339✔
630
        }
534,339✔
631
    }
534,339✔
632

26✔
633
    /**
26✔
634
     * Creates a database type from a given column metadata.
26✔
635
     */
26✔
636
    normalizeType(column: {
26✔
637
        type?: ColumnType
33,554✔
638
        length?: number | string
33,554✔
639
        precision?: number | null
33,554✔
640
        scale?: number
33,554✔
641
        isArray?: boolean
33,554✔
642
        isGenerated?: boolean
33,554✔
643
        generationStrategy?: "increment" | "uuid" | "rowid"
33,554✔
644
    }): string {
33,554✔
645
        if (
33,554✔
646
            column.type === Number ||
33,554✔
647
            column.type === "integer" ||
33,554✔
648
            column.type === "int" ||
33,554✔
649
            column.type === "bigint" ||
33,554✔
650
            column.type === "int64"
16,454✔
651
        ) {
33,554✔
652
            return "int8"
17,107✔
653
        } else if (
33,554✔
654
            column.type === String ||
16,447✔
655
            column.type === "character varying" ||
16,447✔
656
            column.type === "char varying"
3,781✔
657
        ) {
16,447✔
658
            return "varchar"
12,680✔
659
        } else if (
16,447✔
660
            column.type === Date ||
3,767✔
661
            column.type === "timestamp without time zone"
3,656✔
662
        ) {
3,767✔
663
            return "timestamp"
118✔
664
        } else if (column.type === "timestamp with time zone") {
3,767✔
665
            return "timestamptz"
7✔
666
        } else if (column.type === "time without time zone") {
3,649✔
667
            return "time"
7✔
668
        } else if (column.type === Boolean || column.type === "boolean") {
3,642✔
669
            return "bool"
896✔
670
        } else if (
3,635✔
671
            column.type === "simple-array" ||
2,739✔
672
            column.type === "simple-json" ||
2,739✔
673
            column.type === "text"
2,707✔
674
        ) {
2,739✔
675
            return "string"
55✔
676
        } else if (column.type === "bytea" || column.type === "blob") {
2,739✔
677
            return "bytes"
14✔
678
        } else if (column.type === "smallint") {
2,684✔
679
            return "int2"
7✔
680
        } else if (column.type === "numeric" || column.type === "dec") {
2,670✔
681
            return "decimal"
28✔
682
        } else if (
2,663✔
683
            column.type === "double precision" ||
2,635✔
684
            column.type === "float"
2,628✔
685
        ) {
2,635✔
686
            return "float8"
24✔
687
        } else if (column.type === "real") {
2,635✔
688
            return "float4"
7✔
689
        } else if (column.type === "character") {
2,611✔
690
            return "char"
14✔
691
        } else if (column.type === "simple-enum") {
2,604✔
692
            return "enum"
29✔
693
        } else if (column.type === "json") {
2,590✔
694
            return "jsonb"
7✔
695
        } else {
2,561✔
696
            return (column.type as string) || ""
2,554!
697
        }
2,554✔
698
    }
33,554✔
699

26✔
700
    /**
26✔
701
     * Normalizes "default" value of the column.
26✔
702
     */
26✔
703
    normalizeDefault(columnMetadata: ColumnMetadata): string | undefined {
26✔
704
        const defaultValue = columnMetadata.default
26,067✔
705

26,067✔
706
        if (defaultValue === undefined || defaultValue === null) {
26,067✔
707
            return undefined
24,559✔
708
        }
24,559✔
709

1,508✔
710
        if (
1,508✔
711
            (columnMetadata.type === "enum" ||
1,508✔
712
                columnMetadata.type === "simple-enum") &&
26,067✔
713
            defaultValue !== undefined
102✔
714
        ) {
26,067✔
715
            if (columnMetadata.isArray) {
102✔
716
                const enumName = this.buildEnumName(columnMetadata)
54✔
717
                let arrayValue = defaultValue
54✔
718
                if (typeof defaultValue === "string") {
54✔
719
                    if (defaultValue === "{}") return `ARRAY[]::${enumName}[]`
12✔
720
                    arrayValue = defaultValue
6✔
721
                        .replace("{", "")
6✔
722
                        .replace("}", "")
6✔
723
                        .split(",")
6✔
724
                }
6✔
725
                if (Array.isArray(arrayValue)) {
48✔
726
                    const expr = `ARRAY[${arrayValue
48✔
727
                        .map((it) => `'${it}'`)
48✔
728
                        .join(",")}]`
48✔
729
                    return `${expr}::${enumName}[]`
48✔
730
                }
48✔
731
            } else {
102✔
732
                return `'${defaultValue}'`
48✔
733
            }
48✔
734
        } else if (typeof defaultValue === "number") {
26,067✔
735
            return `(${defaultValue})`
315✔
736
        }
315✔
737

1,091✔
738
        if (typeof defaultValue === "boolean") {
26,067✔
739
            return defaultValue ? "true" : "false"
18✔
740
        }
18✔
741

1,073✔
742
        if (typeof defaultValue === "function") {
26,067✔
743
            const value = defaultValue()
701✔
744
            if (value.toUpperCase() === "CURRENT_TIMESTAMP") {
701✔
745
                return "current_timestamp()"
42✔
746
            } else if (value.toUpperCase() === "CURRENT_DATE") {
701✔
747
                return "current_date()"
30✔
748
            }
30✔
749
            return value
629✔
750
        }
629✔
751

372✔
752
        if (typeof defaultValue === "string") {
372✔
753
            const arrayCast = columnMetadata.isArray
372✔
754
                ? `::${columnMetadata.type}[]`
372!
755
                : ""
372✔
756
            return `'${defaultValue}'${arrayCast}`
372✔
757
        }
372✔
758

×
759
        if (ObjectUtils.isObject(defaultValue) && defaultValue !== null) {
26,067!
760
            return `'${JSON.stringify(defaultValue)}'`
×
761
        }
×
762

×
763
        return `${defaultValue}`
×
764
    }
×
765

26✔
766
    /**
26✔
767
     * Normalizes "isUnique" value of the column.
26✔
768
     */
26✔
769
    normalizeIsUnique(column: ColumnMetadata): boolean {
26✔
770
        return column.entityMetadata.uniques.some(
29,512✔
771
            (uq) => uq.columns.length === 1 && uq.columns[0] === column,
29,512✔
772
        )
29,512✔
773
    }
29,512✔
774

26✔
775
    /**
26✔
776
     * Returns default column lengths, which is required on column creation.
26✔
777
     */
26✔
778
    getColumnLength(column: ColumnMetadata): string {
26✔
779
        return column.length ? column.length.toString() : ""
14,240✔
780
    }
14,240✔
781

26✔
782
    /**
26✔
783
     * Creates column type definition including length, precision and scale
26✔
784
     */
26✔
785
    createFullType(column: TableColumn): string {
26✔
786
        let type = column.type
11,530✔
787

11,530✔
788
        if (column.length) {
11,530✔
789
            type += "(" + column.length + ")"
416✔
790
        } else if (
11,530✔
791
            column.precision !== null &&
11,114✔
792
            column.precision !== undefined &&
11,114✔
793
            column.scale !== null &&
11,114✔
794
            column.scale !== undefined
17✔
795
        ) {
11,114✔
796
            type += "(" + column.precision + "," + column.scale + ")"
15✔
797
        } else if (
11,114✔
798
            column.precision !== null &&
11,099✔
799
            column.precision !== undefined
11,099✔
800
        ) {
11,099✔
801
            type += "(" + column.precision + ")"
2✔
802
        } else if (this.spatialTypes.indexOf(column.type as ColumnType) >= 0) {
11,099✔
803
            if (column.spatialFeatureType != null && column.srid != null) {
74✔
804
                type = `${column.type}(${column.spatialFeatureType},${column.srid})`
8✔
805
            } else if (column.spatialFeatureType != null) {
74✔
806
                type = `${column.type}(${column.spatialFeatureType})`
8✔
807
            } else {
66✔
808
                type = column.type
58✔
809
            }
58✔
810
        }
74✔
811

11,530✔
812
        if (column.isArray) type += " array"
11,530✔
813

11,530✔
814
        return type
11,530✔
815
    }
11,530✔
816

26✔
817
    /**
26✔
818
     * Obtains a new database connection to a master server.
26✔
819
     * Used for replication.
26✔
820
     * If replication is not setup then returns default connection's database connection.
26✔
821
     */
26✔
822
    async obtainMasterConnection(): Promise<any> {
26✔
823
        if (!this.master) {
12,362!
824
            throw new TypeORMError("Driver not Connected")
×
825
        }
×
826

12,362✔
827
        return new Promise((ok, fail) => {
12,362✔
828
            this.master.connect((err: any, connection: any, release: any) => {
12,362✔
829
                if (err) {
12,362!
NEW
830
                    fail(err)
×
831
                } else {
12,362✔
832
                    ok([connection, release])
12,362✔
833
                }
12,362✔
834
            })
12,362✔
835
        })
12,362✔
836
    }
12,362✔
837

26✔
838
    /**
26✔
839
     * Obtains a new database connection to a slave server.
26✔
840
     * Used for replication.
26✔
841
     * If replication is not setup then returns master (default) connection's database connection.
26✔
842
     */
26✔
843
    async obtainSlaveConnection(): Promise<any> {
26✔
844
        if (!this.slaves.length) return this.obtainMasterConnection()
×
845

×
846
        const random = Math.floor(Math.random() * this.slaves.length)
×
847

×
848
        return new Promise((ok, fail) => {
×
849
            this.slaves[random].connect(
×
850
                (err: any, connection: any, release: any) => {
×
NEW
851
                    if (err) {
×
NEW
852
                        fail(err)
×
NEW
853
                    } else {
×
NEW
854
                        ok([connection, release])
×
NEW
855
                    }
×
856
                },
×
857
            )
×
858
        })
×
859
    }
×
860

26✔
861
    /**
26✔
862
     * Creates generated map of values generated or returned by database after INSERT query.
26✔
863
     *
26✔
864
     * todo: slow. optimize Object.keys(), OrmUtils.mergeDeep and column.createValueMap parts
26✔
865
     */
26✔
866
    createGeneratedMap(metadata: EntityMetadata, insertResult: ObjectLiteral) {
26✔
867
        if (!insertResult) return undefined
9,670✔
868

3,749✔
869
        return Object.keys(insertResult).reduce((map, key) => {
3,749✔
870
            const column = metadata.findColumnWithDatabaseName(key)
4,410✔
871
            if (column) {
4,410✔
872
                OrmUtils.mergeDeep(
4,410✔
873
                    map,
4,410✔
874
                    column.createValueMap(
4,410✔
875
                        this.prepareHydratedValue(insertResult[key], column),
4,410✔
876
                    ),
4,410✔
877
                )
4,410✔
878
            }
4,410✔
879
            return map
4,410✔
880
        }, {} as ObjectLiteral)
3,749✔
881
    }
3,749✔
882

26✔
883
    /**
26✔
884
     * Differentiate columns of this table and columns from the given column metadatas columns
26✔
885
     * and returns only changed.
26✔
886
     */
26✔
887
    findChangedColumns(
26✔
888
        tableColumns: TableColumn[],
4,673✔
889
        columnMetadatas: ColumnMetadata[],
4,673✔
890
    ): ColumnMetadata[] {
4,673✔
891
        return columnMetadatas.filter((columnMetadata) => {
4,673✔
892
            const tableColumn = tableColumns.find(
14,776✔
893
                (c) => c.name === columnMetadata.databaseName,
14,776✔
894
            )
14,776✔
895
            if (!tableColumn) return false // we don't need new columns, we only need exist and changed
14,776✔
896

14,772✔
897
            // console.log("table:", columnMetadata.entityMetadata.tableName)
14,772✔
898
            // console.log("name:", {
14,772✔
899
            //     tableColumn: tableColumn.name,
14,772✔
900
            //     columnMetadata: columnMetadata.databaseName,
14,772✔
901
            // })
14,772✔
902
            // console.log("type:", {
14,772✔
903
            //     tableColumn: tableColumn.type,
14,772✔
904
            //     columnMetadata: this.normalizeType(columnMetadata),
14,772✔
905
            // })
14,772✔
906
            // console.log("length:", {
14,772✔
907
            //     tableColumn: tableColumn.length,
14,772✔
908
            //     columnMetadata: columnMetadata.length,
14,772✔
909
            // })
14,772✔
910
            // console.log("width:", tableColumn.width, columnMetadata.width);
14,772✔
911
            // console.log("precision:", tableColumn.precision, columnMetadata.precision);
14,772✔
912
            // console.log("scale:", tableColumn.scale, columnMetadata.scale);
14,772✔
913
            // console.log("comment:", tableColumn.comment, this.escapeComment(columnMetadata.comment));
14,772✔
914
            // console.log("default:", tableColumn.default, columnMetadata.default);
14,772✔
915
            // console.log("default changed:", !this.compareDefaultValues(this.normalizeDefault(columnMetadata), tableColumn.default));
14,772✔
916
            // console.log("isPrimary:", tableColumn.isPrimary, columnMetadata.isPrimary);
14,772✔
917
            // console.log("isNullable:", tableColumn.isNullable, columnMetadata.isNullable);
14,772✔
918
            // console.log("isUnique:", tableColumn.isUnique, this.normalizeIsUnique(columnMetadata));
14,772✔
919
            // console.log("asExpression:", {
14,772✔
920
            //     tableColumn: (tableColumn.asExpression || "").trim(),
14,772✔
921
            //     columnMetadata: (columnMetadata.asExpression || "").trim(),
14,772✔
922
            // })
14,772✔
923
            // console.log("==========================================");
14,772✔
924

14,772✔
925
            return (
14,772✔
926
                tableColumn.name !== columnMetadata.databaseName ||
14,772✔
927
                tableColumn.type !== this.normalizeType(columnMetadata) ||
14,776✔
928
                tableColumn.length !== columnMetadata.length ||
14,776✔
929
                tableColumn.isArray !== columnMetadata.isArray ||
14,776✔
930
                tableColumn.precision !== columnMetadata.precision ||
14,776✔
931
                (columnMetadata.scale !== undefined &&
14,767✔
932
                    tableColumn.scale !== columnMetadata.scale) ||
14,776✔
933
                tableColumn.comment !==
14,767✔
934
                    this.escapeComment(columnMetadata.comment) ||
14,776✔
935
                (!tableColumn.isGenerated &&
14,750✔
936
                    this.lowerDefaultValueIfNecessary(
11,889✔
937
                        this.normalizeDefault(columnMetadata),
11,889✔
938
                    ) !== tableColumn.default) || // we included check for generated here, because generated columns already can have default values
14,776✔
939
                tableColumn.isPrimary !== columnMetadata.isPrimary ||
14,776✔
940
                tableColumn.isNullable !== columnMetadata.isNullable ||
14,776✔
941
                tableColumn.isUnique !==
14,725✔
942
                    this.normalizeIsUnique(columnMetadata) ||
14,776✔
943
                tableColumn.enumName !== columnMetadata.enumName ||
14,776✔
944
                (tableColumn.enum &&
14,723✔
945
                    columnMetadata.enum &&
14,723✔
946
                    !OrmUtils.isArraysEqual(
102✔
947
                        tableColumn.enum,
102✔
948
                        columnMetadata.enum.map((val) => val + ""),
102✔
949
                    )) || // enums in postgres are always strings
14,776✔
950
                tableColumn.isGenerated !== columnMetadata.isGenerated ||
14,776✔
951
                tableColumn.generatedType !== columnMetadata.generatedType ||
14,776✔
952
                (tableColumn.asExpression || "").trim() !==
14,722✔
953
                    (columnMetadata.asExpression || "").trim() ||
14,776✔
954
                (tableColumn.spatialFeatureType || "").toLowerCase() !==
14,722✔
955
                    (columnMetadata.spatialFeatureType || "").toLowerCase() ||
14,776✔
956
                tableColumn.srid !== columnMetadata.srid
14,722✔
957
            )
14,776✔
958
        })
4,673✔
959
    }
4,673✔
960

26✔
961
    private lowerDefaultValueIfNecessary(value: string | undefined) {
26✔
962
        if (!value) {
11,889✔
963
            return value
11,117✔
964
        }
11,117✔
965
        return value
772✔
966
            .split(`'`)
772✔
967
            .map((v, i) => {
772✔
968
                return i % 2 === 1 ? v : v.toLowerCase()
1,270✔
969
            })
772✔
970
            .join(`'`)
772✔
971
    }
772✔
972
    /**
26✔
973
     * Returns true if driver supports RETURNING / OUTPUT statement.
26✔
974
     */
26✔
975
    isReturningSqlSupported(): boolean {
26✔
976
        return true
19,118✔
977
    }
19,118✔
978

26✔
979
    /**
26✔
980
     * Returns true if driver supports uuid values generation on its own.
26✔
981
     */
26✔
982
    isUUIDGenerationSupported(): boolean {
26✔
983
        return true
120✔
984
    }
120✔
985

26✔
986
    /**
26✔
987
     * Returns true if driver supports fulltext indices.
26✔
988
     */
26✔
989
    isFullTextColumnTypeSupported(): boolean {
26✔
990
        return false
90✔
991
    }
90✔
992

26✔
993
    /**
26✔
994
     * Creates an escaped parameter.
26✔
995
     */
26✔
996
    createParameter(parameterName: string, index: number): string {
26✔
997
        return this.parametersPrefix + (index + 1)
34,343✔
998
    }
34,343✔
999

26✔
1000
    // -------------------------------------------------------------------------
26✔
1001
    // Public Methods
26✔
1002
    // -------------------------------------------------------------------------
26✔
1003

26✔
1004
    /**
26✔
1005
     * Loads postgres query stream package.
26✔
1006
     */
26✔
1007
    loadStreamDependency() {
26✔
1008
        try {
2✔
1009
            return PlatformTools.load("pg-query-stream")
2✔
1010
        } catch {
2!
1011
            // todo: better error for browser env
×
1012
            throw new TypeORMError(
×
1013
                `To use streams you should install pg-query-stream package. Please run "npm i pg-query-stream".`,
×
1014
            )
×
1015
        }
×
1016
    }
2✔
1017

26✔
1018
    // -------------------------------------------------------------------------
26✔
1019
    // Protected Methods
26✔
1020
    // -------------------------------------------------------------------------
26✔
1021

26✔
1022
    /**
26✔
1023
     * If driver dependency is not given explicitly, then try to load it via "require".
26✔
1024
     */
26✔
1025
    protected loadDependencies(): void {
26✔
1026
        try {
464✔
1027
            const postgres = this.options.driver || PlatformTools.load("pg")
464✔
1028
            this.postgres = postgres
464✔
1029
            try {
464✔
1030
                const pgNative =
464✔
1031
                    this.options.nativeDriver || PlatformTools.load("pg-native")
464✔
1032
                if (pgNative && this.postgres.native)
464!
1033
                    this.postgres = this.postgres.native
464!
1034
            } catch (e) {}
464✔
1035
        } catch (e) {
464!
1036
            // todo: better error for browser env
×
1037
            throw new DriverPackageNotInstalledError("Postgres", "pg")
×
1038
        }
×
1039
    }
464✔
1040

26✔
1041
    /**
26✔
1042
     * Creates a new connection pool for a given database credentials.
26✔
1043
     */
26✔
1044
    protected async createPool(
26✔
1045
        options: CockroachConnectionOptions,
464✔
1046
        credentials: CockroachConnectionCredentialsOptions,
464✔
1047
    ): Promise<any> {
464✔
1048
        credentials = Object.assign(
464✔
1049
            {},
464✔
1050
            credentials,
464✔
1051
            DriverUtils.buildDriverOptions(credentials),
464✔
1052
        ) // todo: do it better way
464✔
1053

464✔
1054
        // build connection options for the driver
464✔
1055
        const connectionOptions = Object.assign(
464✔
1056
            {},
464✔
1057
            {
464✔
1058
                host: credentials.host,
464✔
1059
                user: credentials.username,
464✔
1060
                password: credentials.password,
464✔
1061
                database: credentials.database,
464✔
1062
                port: credentials.port,
464✔
1063
                ssl: credentials.ssl,
464✔
1064
                application_name: options.applicationName,
464✔
1065
                max: options.poolSize,
464✔
1066
            },
464✔
1067
            options.extra || {},
464✔
1068
        )
464✔
1069

464✔
1070
        // create a connection pool
464✔
1071
        const pool = new this.postgres.Pool(connectionOptions)
464✔
1072
        const { logger } = this.connection
464✔
1073

464✔
1074
        const poolErrorHandler =
464✔
1075
            options.poolErrorHandler ||
464✔
1076
            ((error: any) =>
464✔
1077
                logger.log("warn", `Postgres pool raised an error. ${error}`))
464✔
1078

464✔
1079
        /*
464✔
1080
          Attaching an error handler to pool errors is essential, as, otherwise, errors raised will go unhandled and
464✔
1081
          cause the hosting app to crash.
464✔
1082
         */
464✔
1083
        pool.on("error", poolErrorHandler)
464✔
1084

464✔
1085
        return new Promise((ok, fail) => {
464✔
1086
            pool.connect((err: any, connection: any, release: Function) => {
464✔
1087
                if (err) return fail(err)
464!
1088
                release()
464✔
1089
                ok(pool)
464✔
1090
            })
464✔
1091
        })
464✔
1092
    }
464✔
1093

26✔
1094
    /**
26✔
1095
     * Closes connection pool.
26✔
1096
     */
26✔
1097
    protected async closePool(pool: any): Promise<void> {
26✔
1098
        await Promise.all(
464✔
1099
            this.connectedQueryRunners.map((queryRunner) =>
464✔
1100
                queryRunner.release(),
464✔
1101
            ),
464✔
1102
        )
464✔
1103
        return new Promise<void>((ok, fail) => {
464✔
1104
            pool.end((err: any) => (err ? fail(err) : ok()))
464!
1105
        })
464✔
1106
    }
464✔
1107

26✔
1108
    /**
26✔
1109
     * Escapes a given comment.
26✔
1110
     */
26✔
1111
    protected escapeComment(comment?: string) {
26✔
1112
        if (!comment) return comment
14,767✔
1113

88✔
1114
        comment = comment.replace(/'/g, "''").replace(/\u0000/g, "") // Null bytes aren't allowed in comments
88✔
1115

88✔
1116
        return comment
88✔
1117
    }
88✔
1118

26✔
1119
    /**
26✔
1120
     * Builds ENUM type name from given table and column.
26✔
1121
     */
26✔
1122
    protected buildEnumName(column: ColumnMetadata): string {
26✔
1123
        const { schema, tableName } = this.parseTableName(column.entityMetadata)
54✔
1124
        let enumName = column.enumName
54✔
1125
            ? column.enumName
54!
1126
            : `${tableName}_${column.databaseName.toLowerCase()}_enum`
54✔
1127
        if (schema) enumName = `${schema}.${enumName}`
54✔
1128
        return enumName
54✔
1129
            .split(".")
54✔
1130
            .map((i) => {
54✔
1131
                return `"${i}"`
108✔
1132
            })
54✔
1133
            .join(".")
54✔
1134
    }
54✔
1135
}
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