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

typeorm / typeorm / 19549987525

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

push

github

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

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

26500 of 32174 branches covered (82.36%)

Branch coverage included in aggregate %.

91252 of 113615 relevant lines covered (80.32%)

88980.79 hits per line

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

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

26✔
32
/**
26✔
33
 * Organizes communication with PostgreSQL DBMS.
26✔
34
 */
26✔
35
export class PostgresDriver 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
     * Postgres 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: PostgresConnectionOptions
26✔
74

26✔
75
    /**
26✔
76
     * Version of Postgres. Requires a SQL query to the DB, so it is not always set
26✔
77
     */
26✔
78
    version?: string
26✔
79

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

26✔
85
    /**
26✔
86
     * Schema name used to perform all write queries.
26✔
87
     */
26✔
88
    schema?: string
26✔
89

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

26✔
100
    /**
26✔
101
     * Indicates if replication is enabled.
26✔
102
     */
26✔
103
    isReplicated: boolean = false
26✔
104

26✔
105
    /**
26✔
106
     * Indicates if tree tables are supported by this driver.
26✔
107
     */
26✔
108
    treeSupport = true
26✔
109

26✔
110
    /**
26✔
111
     * Represent transaction support by this driver
26✔
112
     */
26✔
113
    transactionSupport = "nested" as const
26✔
114

26✔
115
    /**
26✔
116
     * Gets list of supported column data types by a driver.
26✔
117
     *
26✔
118
     * @see https://www.postgresql.org/docs/current/datatype.html
26✔
119
     */
26✔
120
    supportedDataTypes: ColumnType[] = [
26✔
121
        "int",
26✔
122
        "int2",
26✔
123
        "int4",
26✔
124
        "int8",
26✔
125
        "smallint",
26✔
126
        "integer",
26✔
127
        "bigint",
26✔
128
        "decimal",
26✔
129
        "numeric",
26✔
130
        "real",
26✔
131
        "float",
26✔
132
        "float4",
26✔
133
        "float8",
26✔
134
        "double precision",
26✔
135
        "money",
26✔
136
        "character varying",
26✔
137
        "varchar",
26✔
138
        "character",
26✔
139
        "char",
26✔
140
        "text",
26✔
141
        "citext",
26✔
142
        "hstore",
26✔
143
        "bytea",
26✔
144
        "bit",
26✔
145
        "varbit",
26✔
146
        "bit varying",
26✔
147
        "timetz",
26✔
148
        "timestamptz",
26✔
149
        "timestamp",
26✔
150
        "timestamp without time zone",
26✔
151
        "timestamp with time zone",
26✔
152
        "date",
26✔
153
        "time",
26✔
154
        "time without time zone",
26✔
155
        "time with time zone",
26✔
156
        "interval",
26✔
157
        "bool",
26✔
158
        "boolean",
26✔
159
        "enum",
26✔
160
        "point",
26✔
161
        "line",
26✔
162
        "lseg",
26✔
163
        "box",
26✔
164
        "path",
26✔
165
        "polygon",
26✔
166
        "circle",
26✔
167
        "cidr",
26✔
168
        "inet",
26✔
169
        "macaddr",
26✔
170
        "macaddr8",
26✔
171
        "tsvector",
26✔
172
        "tsquery",
26✔
173
        "uuid",
26✔
174
        "xml",
26✔
175
        "json",
26✔
176
        "jsonb",
26✔
177
        "jsonpath",
26✔
178
        "int4range",
26✔
179
        "int8range",
26✔
180
        "numrange",
26✔
181
        "tsrange",
26✔
182
        "tstzrange",
26✔
183
        "daterange",
26✔
184
        "int4multirange",
26✔
185
        "int8multirange",
26✔
186
        "nummultirange",
26✔
187
        "tsmultirange",
26✔
188
        "tstzmultirange",
26✔
189
        "datemultirange",
26✔
190
        "geometry",
26✔
191
        "geography",
26✔
192
        "cube",
26✔
193
        "ltree",
26✔
194
        "vector",
26✔
195
        "halfvec",
26✔
196
    ]
26✔
197

26✔
198
    /**
26✔
199
     * Returns type of upsert supported by driver if any
26✔
200
     */
26✔
201
    supportedUpsertTypes: UpsertType[] = ["on-conflict-do-update"]
26✔
202

26✔
203
    /**
26✔
204
     * Gets list of spatial column data types.
26✔
205
     */
26✔
206
    spatialTypes: ColumnType[] = ["geometry", "geography"]
26✔
207

26✔
208
    /**
26✔
209
     * Gets list of column data types that support length by a driver.
26✔
210
     */
26✔
211
    withLengthColumnTypes: ColumnType[] = [
26✔
212
        "character varying",
26✔
213
        "varchar",
26✔
214
        "character",
26✔
215
        "char",
26✔
216
        "bit",
26✔
217
        "varbit",
26✔
218
        "bit varying",
26✔
219
        "vector",
26✔
220
        "halfvec",
26✔
221
    ]
26✔
222

26✔
223
    /**
26✔
224
     * Gets list of column data types that support precision by a driver.
26✔
225
     */
26✔
226
    withPrecisionColumnTypes: ColumnType[] = [
26✔
227
        "numeric",
26✔
228
        "decimal",
26✔
229
        "interval",
26✔
230
        "time without time zone",
26✔
231
        "time with time zone",
26✔
232
        "timestamp without time zone",
26✔
233
        "timestamp with time zone",
26✔
234
    ]
26✔
235

26✔
236
    /**
26✔
237
     * Gets list of column data types that support scale by a driver.
26✔
238
     */
26✔
239
    withScaleColumnTypes: ColumnType[] = ["numeric", "decimal"]
26✔
240

26✔
241
    /**
26✔
242
     * Orm has special columns and we need to know what database column types should be for those types.
26✔
243
     * Column types are driver dependant.
26✔
244
     */
26✔
245
    mappedDataTypes: MappedColumnTypes = {
26✔
246
        createDate: "timestamp",
26✔
247
        createDateDefault: "now()",
26✔
248
        updateDate: "timestamp",
26✔
249
        updateDateDefault: "now()",
26✔
250
        deleteDate: "timestamp",
26✔
251
        deleteDateNullable: true,
26✔
252
        version: "int4",
26✔
253
        treeLevel: "int4",
26✔
254
        migrationId: "int4",
26✔
255
        migrationName: "varchar",
26✔
256
        migrationTimestamp: "int8",
26✔
257
        cacheId: "int4",
26✔
258
        cacheIdentifier: "varchar",
26✔
259
        cacheTime: "int8",
26✔
260
        cacheDuration: "int4",
26✔
261
        cacheQuery: "text",
26✔
262
        cacheResult: "text",
26✔
263
        metadataType: "varchar",
26✔
264
        metadataDatabase: "varchar",
26✔
265
        metadataSchema: "varchar",
26✔
266
        metadataTable: "varchar",
26✔
267
        metadataName: "varchar",
26✔
268
        metadataValue: "text",
26✔
269
    }
26✔
270

26✔
271
    /**
26✔
272
     * The prefix used for the parameters
26✔
273
     */
26✔
274
    parametersPrefix: string = "$"
26✔
275

26✔
276
    /**
26✔
277
     * Default values of length, precision and scale depends on column data type.
26✔
278
     * Used in the cases when length/precision/scale is not specified by user.
26✔
279
     */
26✔
280
    dataTypeDefaults: DataTypeDefaults = {
26✔
281
        character: { length: 1 },
26✔
282
        bit: { length: 1 },
26✔
283
        interval: { precision: 6 },
26✔
284
        "time without time zone": { precision: 6 },
26✔
285
        "time with time zone": { precision: 6 },
26✔
286
        "timestamp without time zone": { precision: 6 },
26✔
287
        "timestamp with time zone": { precision: 6 },
26✔
288
    }
26✔
289

26✔
290
    /**
26✔
291
     * Max length allowed by Postgres for aliases.
26✔
292
     * @see https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
26✔
293
     */
26✔
294
    maxAliasLength = 63
26✔
295

26✔
296
    isGeneratedColumnsSupported: boolean = false
26✔
297

26✔
298
    cteCapabilities: CteCapabilities = {
26✔
299
        enabled: true,
26✔
300
        writable: true,
26✔
301
        requiresRecursiveHint: true,
26✔
302
        materializedHint: true,
26✔
303
    }
26✔
304

26✔
305
    // -------------------------------------------------------------------------
26✔
306
    // Constructor
26✔
307
    // -------------------------------------------------------------------------
26✔
308

26✔
309
    constructor(connection?: DataSource) {
26✔
310
        if (!connection) {
2,966✔
311
            return
286✔
312
        }
286✔
313

2,680!
314
        this.connection = connection
2,680✔
315
        this.options = connection.options as PostgresConnectionOptions
2,680✔
316
        this.isReplicated = this.options.replication ? true : false
2,966!
317
        if (this.options.useUTC) {
2,966!
318
            process.env.PGTZ = "UTC"
×
319
        }
×
320
        // load postgres package
2,680✔
321
        this.loadDependencies()
2,680✔
322

2,680✔
323
        this.database = DriverUtils.buildDriverOptions(
2,680✔
324
            this.options.replication
2,680✔
325
                ? this.options.replication.master
2,724✔
326
                : this.options,
2,966!
327
        ).database
2,966✔
328
        this.schema = DriverUtils.buildDriverOptions(this.options).schema
2,966✔
329

2,966✔
330
        // ObjectUtils.assign(this.options, DriverUtils.buildDriverOptions(connection.options)); // todo: do it better way
2,966✔
331
        // validate options to make sure everything is set
2,966✔
332
        // todo: revisit validation with replication in mind
2,966✔
333
        // if (!this.options.host)
2,966✔
334
        //     throw new DriverOptionNotSetError("host");
2,966✔
335
        // if (!this.options.username)
2,966✔
336
        //     throw new DriverOptionNotSetError("username");
2,966✔
337
        // if (!this.options.database)
2,966✔
338
        //     throw new DriverOptionNotSetError("database");
2,966✔
339
    }
2,966✔
340

26✔
341
    // -------------------------------------------------------------------------
26✔
342
    // Public Implemented Methods
26✔
343
    // -------------------------------------------------------------------------
26✔
344

26✔
345
    /**
26✔
346
     * Performs connection to the database.
26✔
347
     * Based on pooling options, it can either create connection immediately,
26✔
348
     * either create a pool and create connection when needed.
26✔
349
     */
26✔
350
    async connect(): Promise<void> {
26✔
351
        if (this.options.replication) {
2,680✔
352
            this.slaves = await Promise.all(
32✔
353
                this.options.replication.slaves.map((slave) => {
32✔
354
                    return this.createPool(this.options, slave)
32✔
355
                }),
32✔
356
            )
32✔
357
            this.master = await this.createPool(
32✔
358
                this.options,
32✔
359
                this.options.replication.master,
32✔
360
            )
32✔
361
        } else {
2,680✔
362
            this.master = await this.createPool(this.options, this.options)
2,648✔
363
        }
2,648✔
364

2,680✔
365
        const queryRunner = this.createQueryRunner("master")
2,680✔
366

2,680✔
367
        this.version = await queryRunner.getVersion()
2,680✔
368

2,680✔
369
        if (!this.database) {
2,680!
370
            this.database = await queryRunner.getCurrentDatabase()
×
371
        }
×
372

2,680✔
373
        if (!this.searchSchema) {
2,680✔
374
            this.searchSchema = await queryRunner.getCurrentSchema()
2,680✔
375
        }
2,680✔
376

2,680✔
377
        await queryRunner.release()
2,680✔
378

2,680✔
379
        if (!this.schema) {
2,680✔
380
            this.schema = this.searchSchema
2,640✔
381
        }
2,640✔
382
    }
2,680✔
383

26✔
384
    /**
26✔
385
     * Makes any action after connection (e.g. create extensions in Postgres driver).
26✔
386
     */
26✔
387
    async afterConnect(): Promise<void> {
26✔
388
        const extensionsMetadata = await this.checkMetadataForExtensions()
2,672✔
389
        const [connection, release] = await this.obtainMasterConnection()
2,672✔
390

2,672✔
391
        const installExtensions =
2,672✔
392
            this.options.installExtensions === undefined ||
2,672✔
393
            this.options.installExtensions
8✔
394
        if (installExtensions && extensionsMetadata.hasExtensions) {
2,672✔
395
            await this.enableExtensions(extensionsMetadata, connection)
308✔
396
        }
308✔
397

2,672✔
398
        this.isGeneratedColumnsSupported = VersionUtils.isGreaterOrEqual(
2,672✔
399
            this.version,
2,672✔
400
            "12.0",
2,672✔
401
        )
2,672✔
402

2,672✔
403
        await release()
2,672✔
404
    }
2,672✔
405

26✔
406
    protected async enableExtensions(extensionsMetadata: any, connection: any) {
26✔
407
        const { logger } = this.connection
308✔
408

308✔
409
        const {
308✔
410
            hasUuidColumns,
308✔
411
            hasCitextColumns,
308✔
412
            hasHstoreColumns,
308✔
413
            hasCubeColumns,
308✔
414
            hasGeometryColumns,
308✔
415
            hasLtreeColumns,
308✔
416
            hasVectorColumns,
308✔
417
            hasExclusionConstraints,
308✔
418
        } = extensionsMetadata
308✔
419

308✔
420
        if (hasUuidColumns)
308✔
421
            try {
308✔
422
                await this.executeQuery(
132✔
423
                    connection,
132✔
424
                    `CREATE EXTENSION IF NOT EXISTS "${
132✔
425
                        this.options.uuidExtension || "uuid-ossp"
132✔
426
                    }"`,
132✔
427
                )
132✔
428
            } catch (_) {
132!
429
                logger.log(
×
430
                    "warn",
×
431
                    `At least one of the entities has uuid column, but the '${
×
432
                        this.options.uuidExtension || "uuid-ossp"
×
433
                    }' extension cannot be installed automatically. Please install it manually using superuser rights, or select another uuid extension.`,
×
434
                )
×
435
            }
×
436
        if (hasCitextColumns)
308✔
437
            try {
308✔
438
                await this.executeQuery(
20✔
439
                    connection,
20✔
440
                    `CREATE EXTENSION IF NOT EXISTS "citext"`,
20✔
441
                )
20✔
442
            } catch (_) {
20!
443
                logger.log(
×
444
                    "warn",
×
445
                    "At least one of the entities has citext column, but the 'citext' extension cannot be installed automatically. Please install it manually using superuser rights",
×
446
                )
×
447
            }
×
448
        if (hasHstoreColumns)
308✔
449
            try {
308✔
450
                await this.executeQuery(
20✔
451
                    connection,
20✔
452
                    `CREATE EXTENSION IF NOT EXISTS "hstore"`,
20✔
453
                )
20✔
454
            } catch (_) {
20!
455
                logger.log(
×
456
                    "warn",
×
457
                    "At least one of the entities has hstore column, but the 'hstore' extension cannot be installed automatically. Please install it manually using superuser rights",
×
458
                )
×
459
            }
×
460
        if (hasGeometryColumns)
308✔
461
            try {
308✔
462
                await this.executeQuery(
8✔
463
                    connection,
8✔
464
                    `CREATE EXTENSION IF NOT EXISTS "postgis"`,
8✔
465
                )
8✔
466
            } catch (_) {
8!
467
                logger.log(
×
468
                    "warn",
×
469
                    "At least one of the entities has a geometry column, but the 'postgis' extension cannot be installed automatically. Please install it manually using superuser rights",
×
470
                )
×
471
            }
×
472
        if (hasCubeColumns)
308✔
473
            try {
308✔
474
                await this.executeQuery(
4✔
475
                    connection,
4✔
476
                    `CREATE EXTENSION IF NOT EXISTS "cube"`,
4✔
477
                )
4✔
478
            } catch (_) {
4!
479
                logger.log(
×
480
                    "warn",
×
481
                    "At least one of the entities has a cube column, but the 'cube' extension cannot be installed automatically. Please install it manually using superuser rights",
×
482
                )
×
483
            }
×
484
        if (hasLtreeColumns)
308✔
485
            try {
308✔
486
                await this.executeQuery(
4✔
487
                    connection,
4✔
488
                    `CREATE EXTENSION IF NOT EXISTS "ltree"`,
4✔
489
                )
4✔
490
            } catch (_) {
4!
491
                logger.log(
×
492
                    "warn",
×
493
                    "At least one of the entities has a ltree column, but the 'ltree' extension cannot be installed automatically. Please install it manually using superuser rights",
×
494
                )
×
495
            }
×
496
        if (hasVectorColumns)
308✔
497
            try {
308✔
498
                await this.executeQuery(
8✔
499
                    connection,
8✔
500
                    `CREATE EXTENSION IF NOT EXISTS "vector"`,
8✔
501
                )
8✔
502
            } catch (_) {
8!
503
                logger.log(
×
504
                    "warn",
×
505
                    "At least one of the entities has a vector column, but the 'vector' extension (pgvector) cannot be installed automatically. Please install it manually using superuser rights",
×
506
                )
×
507
            }
×
508
        if (hasExclusionConstraints)
308✔
509
            try {
308✔
510
                // The btree_gist extension provides operator support in PostgreSQL exclusion constraints
132✔
511
                await this.executeQuery(
132✔
512
                    connection,
132✔
513
                    `CREATE EXTENSION IF NOT EXISTS "btree_gist"`,
132✔
514
                )
132✔
515
            } catch (_) {
132!
516
                logger.log(
×
517
                    "warn",
×
518
                    "At least one of the entities has an exclusion constraint, but the 'btree_gist' extension cannot be installed automatically. Please install it manually using superuser rights",
×
519
                )
×
520
            }
×
521
    }
308✔
522

26✔
523
    protected async checkMetadataForExtensions() {
26✔
524
        const hasUuidColumns = this.connection.entityMetadatas.some(
2,672✔
525
            (metadata) => {
2,672✔
526
                return (
6,204✔
527
                    metadata.generatedColumns.filter(
6,204✔
528
                        (column) => column.generationStrategy === "uuid",
6,204✔
529
                    ).length > 0
6,204✔
530
                )
6,204✔
531
            },
2,672✔
532
        )
2,672✔
533
        const hasCitextColumns = this.connection.entityMetadatas.some(
2,672✔
534
            (metadata) => {
2,672✔
535
                return (
6,352✔
536
                    metadata.columns.filter(
6,352✔
537
                        (column) => column.type === "citext",
6,352✔
538
                    ).length > 0
6,352✔
539
                )
6,352✔
540
            },
2,672✔
541
        )
2,672✔
542
        const hasHstoreColumns = this.connection.entityMetadatas.some(
2,672✔
543
            (metadata) => {
2,672✔
544
                return (
6,352✔
545
                    metadata.columns.filter(
6,352✔
546
                        (column) => column.type === "hstore",
6,352✔
547
                    ).length > 0
6,352✔
548
                )
6,352✔
549
            },
2,672✔
550
        )
2,672✔
551
        const hasCubeColumns = this.connection.entityMetadatas.some(
2,672✔
552
            (metadata) => {
2,672✔
553
                return (
6,360✔
554
                    metadata.columns.filter((column) => column.type === "cube")
6,360✔
555
                        .length > 0
6,360✔
556
                )
6,360✔
557
            },
2,672✔
558
        )
2,672✔
559
        const hasGeometryColumns = this.connection.entityMetadatas.some(
2,672✔
560
            (metadata) => {
2,672✔
561
                return (
6,360✔
562
                    metadata.columns.filter(
6,360✔
563
                        (column) => this.spatialTypes.indexOf(column.type) >= 0,
6,360✔
564
                    ).length > 0
6,360✔
565
                )
6,360✔
566
            },
2,672✔
567
        )
2,672✔
568
        const hasLtreeColumns = this.connection.entityMetadatas.some(
2,672✔
569
            (metadata) => {
2,672✔
570
                return (
6,360✔
571
                    metadata.columns.filter((column) => column.type === "ltree")
6,360✔
572
                        .length > 0
6,360✔
573
                )
6,360✔
574
            },
2,672✔
575
        )
2,672✔
576
        const hasVectorColumns = this.connection.entityMetadatas.some(
2,672✔
577
            (metadata) => {
2,672✔
578
                return metadata.columns.some(
6,360✔
579
                    (column) =>
6,360✔
580
                        column.type === "vector" || column.type === "halfvec",
6,360✔
581
                )
6,360✔
582
            },
2,672✔
583
        )
2,672✔
584
        const hasExclusionConstraints = this.connection.entityMetadatas.some(
2,672✔
585
            (metadata) => {
2,672✔
586
                return metadata.exclusions.length > 0
5,512✔
587
            },
2,672✔
588
        )
2,672✔
589

2,672✔
590
        return {
2,672✔
591
            hasUuidColumns,
2,672✔
592
            hasCitextColumns,
2,672✔
593
            hasHstoreColumns,
2,672✔
594
            hasCubeColumns,
2,672✔
595
            hasGeometryColumns,
2,672✔
596
            hasLtreeColumns,
2,672✔
597
            hasVectorColumns,
2,672✔
598
            hasExclusionConstraints,
2,672✔
599
            hasExtensions:
2,672✔
600
                hasUuidColumns ||
2,672✔
601
                hasCitextColumns ||
2,672✔
602
                hasHstoreColumns ||
2,672✔
603
                hasGeometryColumns ||
2,672✔
604
                hasCubeColumns ||
2,672✔
605
                hasLtreeColumns ||
2,672✔
606
                hasVectorColumns ||
2,672✔
607
                hasExclusionConstraints,
2,672✔
608
        }
2,672✔
609
    }
2,672✔
610

26✔
611
    /**
26✔
612
     * Closes connection with database.
26✔
613
     */
26✔
614
    async disconnect(): Promise<void> {
26✔
615
        if (!this.master) {
2,680!
616
            throw new ConnectionIsNotSetError("postgres")
×
617
        }
×
618

2,680✔
619
        await this.closePool(this.master)
2,680✔
620
        await Promise.all(this.slaves.map((slave) => this.closePool(slave)))
2,680✔
621
        this.master = undefined
2,680✔
622
        this.slaves = []
2,680✔
623
    }
2,680✔
624

26✔
625
    /**
26✔
626
     * Creates a schema builder used to build and sync a schema.
26✔
627
     */
26✔
628
    createSchemaBuilder() {
26✔
629
        return new RdbmsSchemaBuilder(this.connection)
7,700✔
630
    }
7,700✔
631

26✔
632
    /**
26✔
633
     * Creates a query runner used to execute database queries.
26✔
634
     */
26✔
635
    createQueryRunner(mode: ReplicationMode): PostgresQueryRunner {
26✔
636
        return new PostgresQueryRunner(this, mode)
57,692✔
637
    }
57,692✔
638

26✔
639
    /**
26✔
640
     * Prepares given value to a value to be persisted, based on its column type and metadata.
26✔
641
     */
26✔
642
    preparePersistentValue(value: any, columnMetadata: ColumnMetadata): any {
26✔
643
        if (columnMetadata.transformer)
767,112✔
644
            value = ApplyValueTransformers.transformTo(
767,112✔
645
                columnMetadata.transformer,
212✔
646
                value,
212✔
647
            )
212✔
648

767,112✔
649
        if (value === null || value === undefined) return value
767,112✔
650

750,408✔
651
        if (columnMetadata.type === Boolean) {
767,112✔
652
            return value === true ? 1 : 0
8,016✔
653
        } else if (columnMetadata.type === "date") {
767,112✔
654
            return DateUtils.mixedDateToDateString(value)
24✔
655
        } else if (columnMetadata.type === "time") {
742,392✔
656
            return DateUtils.mixedDateToTimeString(value)
20✔
657
        } else if (
742,368✔
658
            columnMetadata.type === "datetime" ||
742,348✔
659
            columnMetadata.type === Date ||
742,348✔
660
            columnMetadata.type === "timestamp" ||
742,348✔
661
            columnMetadata.type === "timestamp with time zone" ||
742,348✔
662
            columnMetadata.type === "timestamp without time zone"
702,100✔
663
        ) {
742,348✔
664
            return DateUtils.mixedDateToDate(value)
40,256✔
665
        } else if (
742,348✔
666
            ["json", "jsonb", ...this.spatialTypes].indexOf(
702,092✔
667
                columnMetadata.type,
702,092✔
668
            ) >= 0
702,092✔
669
        ) {
702,092✔
670
            return JSON.stringify(value)
40,212✔
671
        } else if (
702,092✔
672
            columnMetadata.type === "vector" ||
661,880✔
673
            columnMetadata.type === "halfvec"
661,800✔
674
        ) {
661,880✔
675
            if (Array.isArray(value)) {
108✔
676
                return `[${value.join(",")}]`
108✔
677
            } else {
108!
678
                return value
×
679
            }
×
680
        } else if (columnMetadata.type === "hstore") {
661,880✔
681
            if (typeof value === "string") {
24✔
682
                return value
8✔
683
            } else {
24✔
684
                // https://www.postgresql.org/docs/9.0/hstore.html
16✔
685
                const quoteString = (value: unknown) => {
16✔
686
                    // If a string to be quoted is `null` or `undefined`, we return a literal unquoted NULL.
112✔
687
                    // This way, NULL values can be stored in the hstore object.
112✔
688
                    if (value === null || typeof value === "undefined") {
112✔
689
                        return "NULL"
4✔
690
                    }
4✔
691
                    // Convert non-null values to string since HStore only stores strings anyway.
108✔
692
                    // To include a double quote or a backslash in a key or value, escape it with a backslash.
108✔
693
                    return `"${`${value}`.replace(/(?=["\\])/g, "\\")}"`
108✔
694
                }
108✔
695
                return Object.keys(value)
16✔
696
                    .map(
16✔
697
                        (key) =>
16✔
698
                            quoteString(key) + "=>" + quoteString(value[key]),
16✔
699
                    )
16✔
700
                    .join(",")
16✔
701
            }
16✔
702
        } else if (columnMetadata.type === "simple-array") {
661,772✔
703
            return DateUtils.simpleArrayToString(value)
8✔
704
        } else if (columnMetadata.type === "simple-json") {
661,748✔
705
            return DateUtils.simpleJsonToString(value)
20✔
706
        } else if (columnMetadata.type === "cube") {
661,740✔
707
            if (columnMetadata.isArray) {
40✔
708
                return `{${value
4✔
709
                    .map((cube: number[]) => `"(${cube.join(",")})"`)
4✔
710
                    .join(",")}}`
4✔
711
            }
4✔
712
            return `(${value.join(",")})`
36✔
713
        } else if (columnMetadata.type === "ltree") {
661,720✔
714
            return value
32✔
715
                .split(".")
32✔
716
                .filter(Boolean)
32✔
717
                .join(".")
32✔
718
                .replace(/[\s]+/g, "_")
32✔
719
        } else if (
661,680✔
720
            (columnMetadata.type === "enum" ||
661,648✔
721
                columnMetadata.type === "simple-enum") &&
661,648✔
722
            !columnMetadata.isArray
260✔
723
        ) {
661,648✔
724
            return "" + value
108✔
725
        }
108✔
726

661,540✔
727
        return value
661,540✔
728
    }
661,540✔
729

26✔
730
    /**
26✔
731
     * Prepares given value to a value to be persisted, based on its column type or metadata.
26✔
732
     */
26✔
733
    prepareHydratedValue(value: any, columnMetadata: ColumnMetadata): any {
26✔
734
        if (value === null || value === undefined)
404,564✔
735
            return columnMetadata.transformer
404,564✔
736
                ? ApplyValueTransformers.transformFrom(
16,116✔
737
                      columnMetadata.transformer,
64✔
738
                      value,
64✔
739
                  )
16,116✔
740
                : value
16,116✔
741

388,448✔
742
        if (columnMetadata.type === Boolean) {
404,564✔
743
            value = value ? true : false
2,824✔
744
        } else if (
404,564✔
745
            columnMetadata.type === "datetime" ||
385,624✔
746
            columnMetadata.type === Date ||
385,624✔
747
            columnMetadata.type === "timestamp" ||
385,624✔
748
            columnMetadata.type === "timestamp with time zone" ||
385,624✔
749
            columnMetadata.type === "timestamp without time zone"
384,196✔
750
        ) {
385,624✔
751
            value = DateUtils.normalizeHydratedDate(value)
1,436✔
752
        } else if (columnMetadata.type === "date") {
385,624✔
753
            value = DateUtils.mixedDateToDateString(value)
64✔
754
        } else if (columnMetadata.type === "time") {
384,188✔
755
            value = DateUtils.mixedTimeToString(value)
20✔
756
        } else if (
384,124✔
757
            columnMetadata.type === "vector" ||
384,104✔
758
            columnMetadata.type === "halfvec"
384,080✔
759
        ) {
384,104✔
760
            if (
32✔
761
                typeof value === "string" &&
32✔
762
                value.startsWith("[") &&
32✔
763
                value.endsWith("]")
32✔
764
            ) {
32✔
765
                if (value === "[]") return []
32!
766
                return value.slice(1, -1).split(",").map(Number)
32✔
767
            }
32✔
768
        } else if (columnMetadata.type === "hstore") {
384,104✔
769
            if (columnMetadata.hstoreType === "object") {
24✔
770
                const unescapeString = (str: string) =>
16✔
771
                    str.replace(/\\./g, (m) => m[1])
108✔
772
                const regexp =
16✔
773
                    /"([^"\\]*(?:\\.[^"\\]*)*)"=>(?:(NULL)|"([^"\\]*(?:\\.[^"\\]*)*)")(?:,|$)/g
16✔
774
                const object: ObjectLiteral = {}
16✔
775
                ;`${value}`.replace(
16✔
776
                    regexp,
16✔
777
                    (_, key, nullValue, stringValue) => {
16✔
778
                        object[unescapeString(key)] = nullValue
56✔
779
                            ? null
56✔
780
                            : unescapeString(stringValue)
56✔
781
                        return ""
56✔
782
                    },
16✔
783
                )
16✔
784
                value = object
16✔
785
            }
16✔
786
        } else if (columnMetadata.type === "simple-array") {
384,072✔
787
            value = DateUtils.stringToSimpleArray(value)
8✔
788
        } else if (columnMetadata.type === "simple-json") {
384,048✔
789
            value = DateUtils.stringToSimpleJson(value)
20✔
790
        } else if (columnMetadata.type === "cube") {
384,040✔
791
            value = value.replace(/[()\s]+/g, "") // remove whitespace
36✔
792
            if (columnMetadata.isArray) {
36✔
793
                /**
4✔
794
                 * Strips these groups from `{"1,2,3","",NULL}`:
4✔
795
                 * 1. ["1,2,3", undefined]  <- cube of arity 3
4✔
796
                 * 2. ["", undefined]         <- cube of arity 0
4✔
797
                 * 3. [undefined, "NULL"]     <- NULL
4✔
798
                 */
4✔
799
                const regexp = /(?:"((?:[\d\s.,])*)")|(?:(NULL))/g
4✔
800
                const unparsedArrayString = value
4✔
801

4✔
802
                value = []
4✔
803
                let cube: RegExpExecArray | null = null
4✔
804
                // Iterate through all regexp matches for cubes/null in array
4✔
805
                while ((cube = regexp.exec(unparsedArrayString)) !== null) {
4✔
806
                    if (cube[1] !== undefined) {
8✔
807
                        value.push(
8✔
808
                            cube[1].split(",").filter(Boolean).map(Number),
8✔
809
                        )
8✔
810
                    } else {
8!
811
                        value.push(undefined)
×
812
                    }
×
813
                }
8✔
814
            } else {
36✔
815
                value = value.split(",").filter(Boolean).map(Number)
32✔
816
            }
32✔
817
        } else if (
384,020✔
818
            columnMetadata.type === "enum" ||
383,984✔
819
            columnMetadata.type === "simple-enum"
383,508✔
820
        ) {
383,984✔
821
            if (columnMetadata.isArray) {
700✔
822
                if (value === "{}") return []
412✔
823

336✔
824
                // manually convert enum array to array of values (pg does not support, see https://github.com/brianc/node-pg-types/issues/56)
336✔
825
                value = (value as string)
336✔
826
                    .slice(1, -1)
336✔
827
                    .split(",")
336✔
828
                    .map((val) => {
336✔
829
                        // replace double quotes from the beginning and from the end
552✔
830
                        if (val.startsWith(`"`) && val.endsWith(`"`))
552✔
831
                            val = val.slice(1, -1)
552✔
832
                        // replace escaped backslash and double quotes
552✔
833
                        return val.replace(/\\(\\|")/g, "$1")
552✔
834
                    })
336✔
835

336✔
836
                // convert to number if that exists in possible enum options
336✔
837
                value = value.map((val: string) => {
336✔
838
                    return !isNaN(+val) &&
552✔
839
                        columnMetadata.enum!.indexOf(parseInt(val)) >= 0
200✔
840
                        ? parseInt(val)
552✔
841
                        : val
552✔
842
                })
336✔
843
            } else {
700✔
844
                // convert to number if that exists in possible enum options
288✔
845
                value =
288✔
846
                    !isNaN(+value) &&
288✔
847
                    columnMetadata.enum!.indexOf(parseInt(value)) >= 0
112✔
848
                        ? parseInt(value)
288✔
849
                        : value
288✔
850
            }
288✔
851
        } else if (columnMetadata.type === Number) {
383,984✔
852
            // convert to number if number
199,092✔
853
            value = !isNaN(+value) ? parseInt(value) : value
199,092!
854
        }
199,092✔
855

388,340✔
856
        if (columnMetadata.transformer)
388,340✔
857
            value = ApplyValueTransformers.transformFrom(
404,564✔
858
                columnMetadata.transformer,
332✔
859
                value,
332✔
860
            )
332✔
861
        return value
388,340✔
862
    }
388,340✔
863

26✔
864
    /**
26✔
865
     * Replaces parameters in the given sql with special escaping character
26✔
866
     * and an array of parameter names to be passed to a query.
26✔
867
     */
26✔
868
    escapeQueryWithParameters(
26✔
869
        sql: string,
67,164✔
870
        parameters: ObjectLiteral,
67,164✔
871
        nativeParameters: ObjectLiteral,
67,164✔
872
    ): [string, any[]] {
67,164✔
873
        const escapedParameters: any[] = Object.keys(nativeParameters).map(
67,164✔
874
            (key) => nativeParameters[key],
67,164✔
875
        )
67,164✔
876
        if (!parameters || !Object.keys(parameters).length)
67,164✔
877
            return [sql, escapedParameters]
67,164✔
878

63,880✔
879
        const parameterIndexMap = new Map<string, number>()
63,880✔
880
        sql = sql.replace(
63,880✔
881
            /:(\.\.\.)?([A-Za-z0-9_.]+)/g,
63,880✔
882
            (full, isArray: string, key: string): string => {
63,880✔
883
                if (!parameters.hasOwnProperty(key)) {
1,034,132✔
884
                    return full
180✔
885
                }
180✔
886

1,033,952✔
887
                if (parameterIndexMap.has(key)) {
1,034,132✔
888
                    return this.parametersPrefix + parameterIndexMap.get(key)
60✔
889
                }
60✔
890

1,033,892✔
891
                const value: any = parameters[key]
1,033,892✔
892

1,033,892✔
893
                if (isArray) {
1,034,132✔
894
                    return value
612✔
895
                        .map((v: any) => {
612✔
896
                            escapedParameters.push(v)
816✔
897
                            return this.createParameter(
816✔
898
                                key,
816✔
899
                                escapedParameters.length - 1,
816✔
900
                            )
816✔
901
                        })
612✔
902
                        .join(", ")
612✔
903
                }
612✔
904

1,033,280✔
905
                if (typeof value === "function") {
1,034,132!
906
                    return value()
×
907
                }
×
908

1,033,280✔
909
                escapedParameters.push(value)
1,033,280✔
910
                parameterIndexMap.set(key, escapedParameters.length)
1,033,280✔
911
                return this.createParameter(key, escapedParameters.length - 1)
1,033,280✔
912
            },
63,880✔
913
        ) // todo: make replace only in value statements, otherwise problems
63,880✔
914
        return [sql, escapedParameters]
63,880✔
915
    }
63,880✔
916

26✔
917
    /**
26✔
918
     * Escapes a column name.
26✔
919
     */
26✔
920
    escape(columnName: string): string {
26✔
921
        return '"' + columnName + '"'
16,483,820✔
922
    }
16,483,820✔
923

26✔
924
    /**
26✔
925
     * Build full table name with schema name and table name.
26✔
926
     * E.g. myDB.mySchema.myTable
26✔
927
     */
26✔
928
    buildTableName(tableName: string, schema?: string): string {
26✔
929
        const tablePath = [tableName]
1,629,744✔
930

1,629,744✔
931
        if (schema) {
1,629,744✔
932
            tablePath.unshift(schema)
1,589,000✔
933
        }
1,589,000✔
934

1,629,744✔
935
        return tablePath.join(".")
1,629,744✔
936
    }
1,629,744✔
937

26✔
938
    /**
26✔
939
     * Parse a target table name or other types and return a normalized table definition.
26✔
940
     */
26✔
941
    parseTableName(
26✔
942
        target: EntityMetadata | Table | View | TableForeignKey | string,
2,637,056✔
943
    ): { database?: string; schema?: string; tableName: string } {
2,637,056✔
944
        const driverDatabase = this.database
2,637,056✔
945
        const driverSchema = this.schema
2,637,056✔
946

2,637,056✔
947
        if (InstanceChecker.isTable(target) || InstanceChecker.isView(target)) {
2,637,056✔
948
            const parsed = this.parseTableName(target.name)
874,396✔
949

874,396✔
950
            return {
874,396✔
951
                database: target.database || parsed.database || driverDatabase,
874,396!
952
                schema: target.schema || parsed.schema || driverSchema,
874,396!
953
                tableName: parsed.tableName,
874,396✔
954
            }
874,396✔
955
        }
874,396✔
956

1,762,660✔
957
        if (InstanceChecker.isTableForeignKey(target)) {
2,637,056✔
958
            const parsed = this.parseTableName(target.referencedTableName)
16,008✔
959

16,008✔
960
            return {
16,008✔
961
                database:
16,008✔
962
                    target.referencedDatabase ||
16,008✔
963
                    parsed.database ||
16,008!
964
                    driverDatabase,
16,008✔
965
                schema:
16,008✔
966
                    target.referencedSchema || parsed.schema || driverSchema,
16,008!
967
                tableName: parsed.tableName,
16,008✔
968
            }
16,008✔
969
        }
16,008✔
970

1,746,652✔
971
        if (InstanceChecker.isEntityMetadata(target)) {
2,637,056✔
972
            // EntityMetadata tableName is never a path
807,416✔
973

807,416✔
974
            return {
807,416✔
975
                database: target.database || driverDatabase,
807,416✔
976
                schema: target.schema || driverSchema,
807,416✔
977
                tableName: target.tableName,
807,416✔
978
            }
807,416✔
979
        }
807,416✔
980

939,236✔
981
        const parts = target.split(".")
939,236✔
982

939,236✔
983
        return {
939,236✔
984
            database: driverDatabase,
939,236✔
985
            schema: (parts.length > 1 ? parts[0] : undefined) || driverSchema,
2,637,056✔
986
            tableName: parts.length > 1 ? parts[1] : parts[0],
2,637,056✔
987
        }
2,637,056✔
988
    }
2,637,056✔
989

26✔
990
    /**
26✔
991
     * Creates a database type from a given column metadata.
26✔
992
     */
26✔
993
    normalizeType(column: {
26✔
994
        type?: ColumnType
181,548✔
995
        length?: number | string
181,548✔
996
        precision?: number | null
181,548✔
997
        scale?: number
181,548✔
998
        isArray?: boolean
181,548✔
999
    }): string {
181,548✔
1000
        if (
181,548✔
1001
            column.type === Number ||
181,548✔
1002
            column.type === "int" ||
181,548✔
1003
            column.type === "int4"
96,020✔
1004
        ) {
181,548✔
1005
            return "integer"
87,804✔
1006
        } else if (column.type === String || column.type === "varchar") {
181,548✔
1007
            return "character varying"
64,664✔
1008
        } else if (column.type === Date || column.type === "timestamp") {
93,744✔
1009
            return "timestamp without time zone"
4,900✔
1010
        } else if (column.type === "timestamptz") {
29,080✔
1011
            return "timestamp with time zone"
188✔
1012
        } else if (column.type === "time") {
24,180✔
1013
            return "time without time zone"
792✔
1014
        } else if (column.type === "timetz") {
23,992✔
1015
            return "time with time zone"
128✔
1016
        } else if (column.type === Boolean || column.type === "bool") {
23,200✔
1017
            return "boolean"
3,884✔
1018
        } else if (column.type === "simple-array") {
23,072✔
1019
            return "text"
140✔
1020
        } else if (column.type === "simple-json") {
19,188✔
1021
            return "text"
200✔
1022
        } else if (column.type === "simple-enum") {
19,048✔
1023
            return "enum"
544✔
1024
        } else if (column.type === "int2") {
18,848✔
1025
            return "smallint"
152✔
1026
        } else if (column.type === "int8") {
18,304✔
1027
            return "bigint"
280✔
1028
        } else if (column.type === "decimal") {
18,152✔
1029
            return "numeric"
424✔
1030
        } else if (column.type === "float8" || column.type === "float") {
17,872✔
1031
            return "double precision"
196✔
1032
        } else if (column.type === "float4") {
17,448✔
1033
            return "real"
128✔
1034
        } else if (column.type === "char") {
17,252✔
1035
            return "character"
288✔
1036
        } else if (column.type === "varbit") {
17,124✔
1037
            return "bit varying"
128✔
1038
        } else {
16,836✔
1039
            return (column.type as string) || ""
16,708!
1040
        }
16,708✔
1041
    }
181,548✔
1042

26✔
1043
    /**
26✔
1044
     * Normalizes "default" value of the column.
26✔
1045
     */
26✔
1046
    normalizeDefault(columnMetadata: ColumnMetadata): string | undefined {
26✔
1047
        const defaultValue = columnMetadata.default
140,508✔
1048

140,508✔
1049
        if (defaultValue === null || defaultValue === undefined) {
140,508✔
1050
            return undefined
130,084✔
1051
        }
130,084✔
1052

10,424✔
1053
        if (columnMetadata.isArray && Array.isArray(defaultValue)) {
140,508✔
1054
            return `'{${defaultValue.map((val) => String(val)).join(",")}}'`
324✔
1055
        }
324✔
1056

10,100✔
1057
        if (
10,100✔
1058
            (columnMetadata.type === "enum" ||
10,100✔
1059
                columnMetadata.type === "simple-enum" ||
140,508✔
1060
                typeof defaultValue === "number" ||
140,508✔
1061
                typeof defaultValue === "string") &&
140,508✔
1062
            defaultValue !== undefined
6,268✔
1063
        ) {
140,508✔
1064
            return `'${defaultValue}'`
6,268✔
1065
        }
6,268✔
1066

3,832✔
1067
        if (typeof defaultValue === "boolean") {
140,508✔
1068
            return defaultValue ? "true" : "false"
72✔
1069
        }
72✔
1070

3,760✔
1071
        if (typeof defaultValue === "function") {
140,508✔
1072
            const value = defaultValue()
3,696✔
1073

3,696✔
1074
            return this.normalizeDatetimeFunction(value)
3,696✔
1075
        }
3,696✔
1076

64✔
1077
        if (typeof defaultValue === "object") {
64✔
1078
            return `'${JSON.stringify(defaultValue)}'`
64✔
1079
        }
64✔
1080

×
1081
        return `${defaultValue}`
×
1082
    }
×
1083

26✔
1084
    /**
26✔
1085
     * Compares "default" value of the column.
26✔
1086
     * Postgres sorts json values before it is saved, so in that case a deep comparison has to be performed to see if has changed.
26✔
1087
     */
26✔
1088
    private defaultEqual(
26✔
1089
        columnMetadata: ColumnMetadata,
65,060✔
1090
        tableColumn: TableColumn,
65,060✔
1091
    ): boolean {
65,060✔
1092
        if (
65,060✔
1093
            ["json", "jsonb"].includes(columnMetadata.type as string) &&
65,060✔
1094
            !["function", "undefined"].includes(typeof columnMetadata.default)
472✔
1095
        ) {
65,060✔
1096
            const tableColumnDefault =
124✔
1097
                typeof tableColumn.default === "string"
124✔
1098
                    ? JSON.parse(
124✔
1099
                          tableColumn.default.substring(
76✔
1100
                              1,
76✔
1101
                              tableColumn.default.length - 1,
76✔
1102
                          ),
76✔
1103
                      )
124✔
1104
                    : tableColumn.default
124✔
1105

124✔
1106
            return OrmUtils.deepCompare(
124✔
1107
                columnMetadata.default,
124✔
1108
                tableColumnDefault,
124✔
1109
            )
124✔
1110
        }
124✔
1111

64,936✔
1112
        const columnDefault = this.lowerDefaultValueIfNecessary(
64,936✔
1113
            this.normalizeDefault(columnMetadata),
64,936✔
1114
        )
64,936✔
1115
        return columnDefault === tableColumn.default
64,936✔
1116
    }
64,936✔
1117

26✔
1118
    /**
26✔
1119
     * Normalizes "isUnique" value of the column.
26✔
1120
     */
26✔
1121
    normalizeIsUnique(column: ColumnMetadata): boolean {
26✔
1122
        return column.entityMetadata.uniques.some(
159,080✔
1123
            (uq) => uq.columns.length === 1 && uq.columns[0] === column,
159,080✔
1124
        )
159,080✔
1125
    }
159,080✔
1126

26✔
1127
    /**
26✔
1128
     * Returns default column lengths, which is required on column creation.
26✔
1129
     */
26✔
1130
    getColumnLength(column: ColumnMetadata): string {
26✔
1131
        return column.length ? column.length.toString() : ""
75,972✔
1132
    }
75,972✔
1133

26✔
1134
    /**
26✔
1135
     * Creates column type definition including length, precision and scale
26✔
1136
     */
26✔
1137
    createFullType(column: TableColumn): string {
26✔
1138
        let type = column.type
63,300✔
1139

63,300✔
1140
        if (column.length) {
63,300✔
1141
            type += "(" + column.length + ")"
2,064✔
1142
        } else if (
63,300✔
1143
            column.precision !== null &&
61,236✔
1144
            column.precision !== undefined &&
61,236✔
1145
            column.scale !== null &&
61,236✔
1146
            column.scale !== undefined
408✔
1147
        ) {
61,236✔
1148
            type += "(" + column.precision + "," + column.scale + ")"
144✔
1149
        } else if (
61,236✔
1150
            column.precision !== null &&
61,092✔
1151
            column.precision !== undefined
61,092✔
1152
        ) {
61,092✔
1153
            type += "(" + column.precision + ")"
264✔
1154
        }
264✔
1155

63,300✔
1156
        if (column.type === "time without time zone") {
63,300✔
1157
            type =
316✔
1158
                "TIME" +
316✔
1159
                (column.precision !== null && column.precision !== undefined
316✔
1160
                    ? "(" + column.precision + ")"
316✔
1161
                    : "")
316✔
1162
        } else if (column.type === "time with time zone") {
63,300✔
1163
            type =
188✔
1164
                "TIME" +
188✔
1165
                (column.precision !== null && column.precision !== undefined
188✔
1166
                    ? "(" + column.precision + ")"
188✔
1167
                    : "") +
188✔
1168
                " WITH TIME ZONE"
188✔
1169
        } else if (column.type === "timestamp without time zone") {
62,984✔
1170
            type =
2,048✔
1171
                "TIMESTAMP" +
2,048✔
1172
                (column.precision !== null && column.precision !== undefined
2,048✔
1173
                    ? "(" + column.precision + ")"
2,048✔
1174
                    : "")
2,048✔
1175
        } else if (column.type === "timestamp with time zone") {
62,796✔
1176
            type =
228✔
1177
                "TIMESTAMP" +
228✔
1178
                (column.precision !== null && column.precision !== undefined
228✔
1179
                    ? "(" + column.precision + ")"
228✔
1180
                    : "") +
228✔
1181
                " WITH TIME ZONE"
228✔
1182
        } else if (this.spatialTypes.indexOf(column.type as ColumnType) >= 0) {
60,748✔
1183
            if (column.spatialFeatureType != null && column.srid != null) {
136✔
1184
                type = `${column.type}(${column.spatialFeatureType},${column.srid})`
40✔
1185
            } else if (column.spatialFeatureType != null) {
136✔
1186
                type = `${column.type}(${column.spatialFeatureType})`
32✔
1187
            } else {
96✔
1188
                type = column.type
64✔
1189
            }
64✔
1190
        } else if (column.type === "vector" || column.type === "halfvec") {
60,520✔
1191
            type =
160✔
1192
                column.type + (column.length ? "(" + column.length + ")" : "")
160✔
1193
        }
160✔
1194

63,300✔
1195
        if (column.isArray) type += " array"
63,300✔
1196

63,300✔
1197
        return type
63,300✔
1198
    }
63,300✔
1199

26✔
1200
    /**
26✔
1201
     * Obtains a new database connection to a master server.
26✔
1202
     * Used for replication.
26✔
1203
     * If replication is not setup then returns default connection's database connection.
26✔
1204
     */
26✔
1205
    async obtainMasterConnection(): Promise<[any, Function]> {
26✔
1206
        if (!this.master) {
57,676!
1207
            throw new TypeORMError("Driver not Connected")
×
1208
        }
×
1209

57,676✔
1210
        return new Promise((ok, fail) => {
57,676✔
1211
            this.master.connect((err: any, connection: any, release: any) => {
57,676✔
1212
                err ? fail(err) : ok([connection, release])
57,676!
1213
            })
57,676✔
1214
        })
57,676✔
1215
    }
57,676✔
1216

26✔
1217
    /**
26✔
1218
     * Obtains a new database connection to a slave server.
26✔
1219
     * Used for replication.
26✔
1220
     * If replication is not setup then returns master (default) connection's database connection.
26✔
1221
     */
26✔
1222
    async obtainSlaveConnection(): Promise<[any, Function]> {
26✔
1223
        if (!this.slaves.length) {
12!
1224
            return this.obtainMasterConnection()
×
1225
        }
×
1226

12✔
1227
        const random = Math.floor(Math.random() * this.slaves.length)
12✔
1228

12✔
1229
        return new Promise((ok, fail) => {
12✔
1230
            this.slaves[random].connect(
12✔
1231
                (err: any, connection: any, release: any) => {
12✔
1232
                    err ? fail(err) : ok([connection, release])
12!
1233
                },
12✔
1234
            )
12✔
1235
        })
12✔
1236
    }
12✔
1237

26✔
1238
    /**
26✔
1239
     * Creates generated map of values generated or returned by database after INSERT query.
26✔
1240
     *
26✔
1241
     * todo: slow. optimize Object.keys(), OrmUtils.mergeDeep and column.createValueMap parts
26✔
1242
     */
26✔
1243
    createGeneratedMap(metadata: EntityMetadata, insertResult: ObjectLiteral) {
26✔
1244
        if (!insertResult) return undefined
224,696✔
1245

160,492✔
1246
        return Object.keys(insertResult).reduce((map, key) => {
160,492✔
1247
            const column = metadata.findColumnWithDatabaseName(key)
164,168✔
1248
            if (column) {
164,168✔
1249
                OrmUtils.mergeDeep(
164,168✔
1250
                    map,
164,168✔
1251
                    column.createValueMap(insertResult[key]),
164,168✔
1252
                )
164,168✔
1253
                // OrmUtils.mergeDeep(map, column.createValueMap(this.prepareHydratedValue(insertResult[key], column))); // TODO: probably should be like there, but fails on enums, fix later
164,168✔
1254
            }
164,168✔
1255
            return map
164,168✔
1256
        }, {} as ObjectLiteral)
160,492✔
1257
    }
160,492✔
1258

26✔
1259
    /**
26✔
1260
     * Differentiate columns of this table and columns from the given column metadatas columns
26✔
1261
     * and returns only changed.
26✔
1262
     */
26✔
1263
    findChangedColumns(
26✔
1264
        tableColumns: TableColumn[],
23,440✔
1265
        columnMetadatas: ColumnMetadata[],
23,440✔
1266
    ): ColumnMetadata[] {
23,440✔
1267
        return columnMetadatas.filter((columnMetadata) => {
23,440✔
1268
            const tableColumn = tableColumns.find(
79,540✔
1269
                (c) => c.name === columnMetadata.databaseName,
79,540✔
1270
            )
79,540✔
1271
            if (!tableColumn) return false // we don't need new columns, we only need exist and changed
79,540✔
1272

79,524✔
1273
            const isColumnChanged =
79,524✔
1274
                tableColumn.name !== columnMetadata.databaseName ||
79,524✔
1275
                tableColumn.type !== this.normalizeType(columnMetadata) ||
79,540✔
1276
                tableColumn.length !== columnMetadata.length ||
79,540✔
1277
                tableColumn.isArray !== columnMetadata.isArray ||
79,540✔
1278
                tableColumn.precision !== columnMetadata.precision ||
79,540✔
1279
                (columnMetadata.scale !== undefined &&
79,460✔
1280
                    tableColumn.scale !== columnMetadata.scale) ||
79,540✔
1281
                tableColumn.comment !==
79,460✔
1282
                    this.escapeComment(columnMetadata.comment) ||
79,540✔
1283
                (!tableColumn.isGenerated &&
79,428✔
1284
                    !this.defaultEqual(columnMetadata, tableColumn)) || // we included check for generated here, because generated columns already can have default values
79,540✔
1285
                tableColumn.isPrimary !== columnMetadata.isPrimary ||
79,540✔
1286
                tableColumn.isNullable !== columnMetadata.isNullable ||
79,540✔
1287
                tableColumn.isUnique !==
79,316✔
1288
                    this.normalizeIsUnique(columnMetadata) ||
79,540✔
1289
                tableColumn.enumName !== columnMetadata.enumName ||
79,540✔
1290
                (tableColumn.enum &&
79,304✔
1291
                    columnMetadata.enum &&
79,304✔
1292
                    !OrmUtils.isArraysEqual(
1,100✔
1293
                        tableColumn.enum,
1,100✔
1294
                        columnMetadata.enum.map((val) => val + ""),
1,100✔
1295
                    )) || // enums in postgres are always strings
79,540✔
1296
                tableColumn.isGenerated !== columnMetadata.isGenerated ||
79,540✔
1297
                (tableColumn.spatialFeatureType || "").toLowerCase() !==
79,288✔
1298
                    (columnMetadata.spatialFeatureType || "").toLowerCase() ||
79,540✔
1299
                tableColumn.srid !== columnMetadata.srid ||
79,540✔
1300
                tableColumn.generatedType !== columnMetadata.generatedType ||
79,540✔
1301
                (tableColumn.asExpression || "").trim() !==
79,288✔
1302
                    (columnMetadata.asExpression || "").trim() ||
79,540✔
1303
                tableColumn.collation !== columnMetadata.collation
79,288✔
1304

79,540✔
1305
            // DEBUG SECTION
79,540✔
1306
            // if (isColumnChanged) {
79,540✔
1307
            //     console.log("table:", columnMetadata.entityMetadata.tableName)
79,540✔
1308
            //     console.log(
79,540✔
1309
            //         "name:",
79,540✔
1310
            //         tableColumn.name,
79,540✔
1311
            //         columnMetadata.databaseName,
79,540✔
1312
            //     )
79,540✔
1313
            //     console.log(
79,540✔
1314
            //         "type:",
79,540✔
1315
            //         tableColumn.type,
79,540✔
1316
            //         this.normalizeType(columnMetadata),
79,540✔
1317
            //     )
79,540✔
1318
            //     console.log(
79,540✔
1319
            //         "length:",
79,540✔
1320
            //         tableColumn.length,
79,540✔
1321
            //         columnMetadata.length,
79,540✔
1322
            //     )
79,540✔
1323
            //     console.log(
79,540✔
1324
            //         "isArray:",
79,540✔
1325
            //         tableColumn.isArray,
79,540✔
1326
            //         columnMetadata.isArray,
79,540✔
1327
            //     )
79,540✔
1328
            //     console.log(
79,540✔
1329
            //         "precision:",
79,540✔
1330
            //         tableColumn.precision,
79,540✔
1331
            //         columnMetadata.precision,
79,540✔
1332
            //     )
79,540✔
1333
            //     console.log("scale:", tableColumn.scale, columnMetadata.scale)
79,540✔
1334
            //     console.log(
79,540✔
1335
            //         "comment:",
79,540✔
1336
            //         tableColumn.comment,
79,540✔
1337
            //         this.escapeComment(columnMetadata.comment),
79,540✔
1338
            //     )
79,540✔
1339
            //     console.log(
79,540✔
1340
            //         "enumName:",
79,540✔
1341
            //         tableColumn.enumName,
79,540✔
1342
            //         columnMetadata.enumName,
79,540✔
1343
            //     )
79,540✔
1344
            //     console.log(
79,540✔
1345
            //         "enum:",
79,540✔
1346
            //         tableColumn.enum &&
79,540✔
1347
            //             columnMetadata.enum &&
79,540✔
1348
            //             !OrmUtils.isArraysEqual(
79,540✔
1349
            //                 tableColumn.enum,
79,540✔
1350
            //                 columnMetadata.enum.map((val) => val + ""),
79,540✔
1351
            //             ),
79,540✔
1352
            //     )
79,540✔
1353
            //     console.log(
79,540✔
1354
            //         "isPrimary:",
79,540✔
1355
            //         tableColumn.isPrimary,
79,540✔
1356
            //         columnMetadata.isPrimary,
79,540✔
1357
            //     )
79,540✔
1358
            //     console.log(
79,540✔
1359
            //         "isNullable:",
79,540✔
1360
            //         tableColumn.isNullable,
79,540✔
1361
            //         columnMetadata.isNullable,
79,540✔
1362
            //     )
79,540✔
1363
            //     console.log(
79,540✔
1364
            //         "isUnique:",
79,540✔
1365
            //         tableColumn.isUnique,
79,540✔
1366
            //         this.normalizeIsUnique(columnMetadata),
79,540✔
1367
            //     )
79,540✔
1368
            //     console.log(
79,540✔
1369
            //         "isGenerated:",
79,540✔
1370
            //         tableColumn.isGenerated,
79,540✔
1371
            //         columnMetadata.isGenerated,
79,540✔
1372
            //     )
79,540✔
1373
            //     console.log(
79,540✔
1374
            //         "generatedType:",
79,540✔
1375
            //         tableColumn.generatedType,
79,540✔
1376
            //         columnMetadata.generatedType,
79,540✔
1377
            //     )
79,540✔
1378
            //     console.log(
79,540✔
1379
            //         "asExpression:",
79,540✔
1380
            //         (tableColumn.asExpression || "").trim(),
79,540✔
1381
            //         (columnMetadata.asExpression || "").trim(),
79,540✔
1382
            //     )
79,540✔
1383
            //     console.log(
79,540✔
1384
            //         "collation:",
79,540✔
1385
            //         tableColumn.collation,
79,540✔
1386
            //         columnMetadata.collation,
79,540✔
1387
            //     )
79,540✔
1388
            //     console.log(
79,540✔
1389
            //         "isGenerated 2:",
79,540✔
1390
            //         !tableColumn.isGenerated &&
79,540✔
1391
            //             this.lowerDefaultValueIfNecessary(
79,540✔
1392
            //                 this.normalizeDefault(columnMetadata),
79,540✔
1393
            //             ) !== tableColumn.default,
79,540✔
1394
            //     )
79,540✔
1395
            //     console.log(
79,540✔
1396
            //         "spatialFeatureType:",
79,540✔
1397
            //         (tableColumn.spatialFeatureType || "").toLowerCase(),
79,540✔
1398
            //         (columnMetadata.spatialFeatureType || "").toLowerCase(),
79,540✔
1399
            //     )
79,540✔
1400
            //     console.log("srid", tableColumn.srid, columnMetadata.srid)
79,540✔
1401
            //     console.log("==========================================")
79,540✔
1402
            // }
79,540✔
1403

79,540✔
1404
            return isColumnChanged
79,540✔
1405
        })
23,440✔
1406
    }
23,440✔
1407

26✔
1408
    private lowerDefaultValueIfNecessary(value: string | undefined) {
26✔
1409
        // Postgres saves function calls in default value as lowercase #2733
64,936✔
1410
        if (!value) {
64,936✔
1411
            return value
59,556✔
1412
        }
59,556✔
1413
        return value
5,380✔
1414
            .split(`'`)
5,380✔
1415
            .map((v, i) => {
5,380✔
1416
                return i % 2 === 1 ? v : v.toLowerCase()
13,116✔
1417
            })
5,380✔
1418
            .join(`'`)
5,380✔
1419
    }
5,380✔
1420

26✔
1421
    /**
26✔
1422
     * Returns true if driver supports RETURNING / OUTPUT statement.
26✔
1423
     */
26✔
1424
    isReturningSqlSupported(): boolean {
26✔
1425
        return true
85,540✔
1426
    }
85,540✔
1427

26✔
1428
    /**
26✔
1429
     * Returns true if driver supports uuid values generation on its own.
26✔
1430
     */
26✔
1431
    isUUIDGenerationSupported(): boolean {
26✔
1432
        return true
568✔
1433
    }
568✔
1434

26✔
1435
    /**
26✔
1436
     * Returns true if driver supports fulltext indices.
26✔
1437
     */
26✔
1438
    isFullTextColumnTypeSupported(): boolean {
26✔
1439
        return false
204✔
1440
    }
204✔
1441

26✔
1442
    get uuidGenerator(): string {
26✔
1443
        return this.options.uuidExtension === "pgcrypto"
740✔
1444
            ? "gen_random_uuid()"
740✔
1445
            : "uuid_generate_v4()"
740✔
1446
    }
740✔
1447

26✔
1448
    /**
26✔
1449
     * Creates an escaped parameter.
26✔
1450
     */
26✔
1451
    createParameter(parameterName: string, index: number): string {
26✔
1452
        return this.parametersPrefix + (index + 1)
1,035,564✔
1453
    }
1,035,564✔
1454

26✔
1455
    // -------------------------------------------------------------------------
26✔
1456
    // Public Methods
26✔
1457
    // -------------------------------------------------------------------------
26✔
1458

26✔
1459
    /**
26✔
1460
     * Loads postgres query stream package.
26✔
1461
     */
26✔
1462
    loadStreamDependency() {
26✔
1463
        try {
8✔
1464
            return PlatformTools.load("pg-query-stream")
8✔
1465
        } catch {
8!
1466
            // todo: better error for browser env
×
1467
            throw new TypeORMError(
×
1468
                `To use streams you should install pg-query-stream package. Please run "npm i pg-query-stream".`,
×
1469
            )
×
1470
        }
×
1471
    }
8✔
1472

26✔
1473
    // -------------------------------------------------------------------------
26✔
1474
    // Protected Methods
26✔
1475
    // -------------------------------------------------------------------------
26✔
1476

26✔
1477
    /**
26✔
1478
     * If driver dependency is not given explicitly, then try to load it via "require".
26✔
1479
     */
26✔
1480
    protected loadDependencies(): void {
26✔
1481
        try {
2,680✔
1482
            const postgres = this.options.driver || PlatformTools.load("pg")
2,680✔
1483
            this.postgres = postgres
2,680✔
1484
            try {
2,680✔
1485
                const pgNative =
2,680✔
1486
                    this.options.nativeDriver || PlatformTools.load("pg-native")
2,680✔
1487
                if (pgNative && this.postgres.native)
2,680!
1488
                    this.postgres = this.postgres.native
2,680!
1489
            } catch (e) {}
2,680✔
1490
        } catch (e) {
2,680!
1491
            // todo: better error for browser env
×
1492
            throw new DriverPackageNotInstalledError("Postgres", "pg")
×
1493
        }
×
1494
    }
2,680✔
1495

26✔
1496
    /**
26✔
1497
     * Creates a new connection pool for a given database credentials.
26✔
1498
     */
26✔
1499
    protected async createPool(
26✔
1500
        options: PostgresConnectionOptions,
2,712✔
1501
        credentials: PostgresConnectionCredentialsOptions,
2,712✔
1502
    ): Promise<any> {
2,712✔
1503
        const { logger } = this.connection
2,712✔
1504
        credentials = Object.assign({}, credentials)
2,712✔
1505

2,712✔
1506
        // build connection options for the driver
2,712✔
1507
        // See: https://github.com/brianc/node-postgres/tree/master/packages/pg-pool#create
2,712✔
1508
        const connectionOptions = Object.assign(
2,712✔
1509
            {},
2,712✔
1510
            {
2,712✔
1511
                connectionString: credentials.url,
2,712✔
1512
                host: credentials.host,
2,712✔
1513
                user: credentials.username,
2,712✔
1514
                password: credentials.password,
2,712✔
1515
                database: credentials.database,
2,712✔
1516
                port: credentials.port,
2,712✔
1517
                ssl: credentials.ssl,
2,712✔
1518
                connectionTimeoutMillis: options.connectTimeoutMS,
2,712✔
1519
                application_name:
2,712✔
1520
                    options.applicationName ?? credentials.applicationName,
2,712✔
1521
                max: options.poolSize,
2,712✔
1522
            },
2,712✔
1523
            options.extra || {},
2,712✔
1524
        )
2,712✔
1525

2,712✔
1526
        if (options.parseInt8 !== undefined) {
2,712✔
1527
            if (
4✔
1528
                this.postgres.defaults &&
4✔
1529
                Object.getOwnPropertyDescriptor(
4✔
1530
                    this.postgres.defaults,
4✔
1531
                    "parseInt8",
4✔
1532
                )?.set
4✔
1533
            ) {
4✔
1534
                this.postgres.defaults.parseInt8 = options.parseInt8
4✔
1535
            } else {
4!
1536
                logger.log(
×
1537
                    "warn",
×
1538
                    "Attempted to set parseInt8 option, but the postgres driver does not support setting defaults.parseInt8. This option will be ignored.",
×
1539
                )
×
1540
            }
×
1541
        }
4✔
1542

2,712✔
1543
        // create a connection pool
2,712✔
1544
        const pool = new this.postgres.Pool(connectionOptions)
2,712✔
1545

2,712✔
1546
        const poolErrorHandler =
2,712✔
1547
            options.poolErrorHandler ||
2,712✔
1548
            ((error: any) =>
2,712✔
1549
                logger.log("warn", `Postgres pool raised an error. ${error}`))
2,712✔
1550

2,712✔
1551
        /*
2,712✔
1552
          Attaching an error handler to pool errors is essential, as, otherwise, errors raised will go unhandled and
2,712✔
1553
          cause the hosting app to crash.
2,712✔
1554
         */
2,712✔
1555
        pool.on("error", poolErrorHandler)
2,712✔
1556

2,712✔
1557
        return new Promise((ok, fail) => {
2,712✔
1558
            pool.connect((err: any, connection: any, release: Function) => {
2,712✔
1559
                if (err) return fail(err)
2,712!
1560

2,712✔
1561
                if (options.logNotifications) {
2,712✔
1562
                    connection.on("notice", (msg: any) => {
4✔
1563
                        msg && this.connection.logger.log("info", msg.message)
12✔
1564
                    })
4✔
1565
                    connection.on("notification", (msg: any) => {
4✔
1566
                        msg &&
4✔
1567
                            this.connection.logger.log(
4✔
1568
                                "info",
4✔
1569
                                `Received NOTIFY on channel ${msg.channel}: ${msg.payload}.`,
4✔
1570
                            )
4✔
1571
                    })
4✔
1572
                }
4✔
1573
                release()
2,712✔
1574
                ok(pool)
2,712✔
1575
            })
2,712✔
1576
        })
2,712✔
1577
    }
2,712✔
1578

26✔
1579
    /**
26✔
1580
     * Closes connection pool.
26✔
1581
     */
26✔
1582
    protected async closePool(pool: any): Promise<void> {
26✔
1583
        while (this.connectedQueryRunners.length) {
2,712✔
1584
            await this.connectedQueryRunners[0].release()
64✔
1585
        }
64✔
1586

2,712✔
1587
        return new Promise<void>((ok, fail) => {
2,712✔
1588
            pool.end((err: any) => (err ? fail(err) : ok()))
2,712!
1589
        })
2,712✔
1590
    }
2,712✔
1591

26✔
1592
    /**
26✔
1593
     * Executes given query.
26✔
1594
     */
26✔
1595
    protected executeQuery(connection: any, query: string) {
26✔
1596
        this.connection.logger.logQuery(query)
328✔
1597

328✔
1598
        return new Promise((ok, fail) => {
328✔
1599
            connection.query(query, (err: any, result: any) =>
328✔
1600
                err ? fail(err) : ok(result),
328!
1601
            )
328✔
1602
        })
328✔
1603
    }
328✔
1604

26✔
1605
    /**
26✔
1606
     * If parameter is a datetime function, e.g. "CURRENT_TIMESTAMP", normalizes it.
26✔
1607
     * Otherwise returns original input.
26✔
1608
     */
26✔
1609
    protected normalizeDatetimeFunction(value: string) {
26✔
1610
        // check if input is datetime function
3,696✔
1611
        const upperCaseValue = value.toUpperCase()
3,696✔
1612
        const isDatetimeFunction =
3,696✔
1613
            upperCaseValue.indexOf("CURRENT_TIMESTAMP") !== -1 ||
3,696✔
1614
            upperCaseValue.indexOf("CURRENT_DATE") !== -1 ||
3,696✔
1615
            upperCaseValue.indexOf("CURRENT_TIME") !== -1 ||
3,696✔
1616
            upperCaseValue.indexOf("LOCALTIMESTAMP") !== -1 ||
3,696✔
1617
            upperCaseValue.indexOf("LOCALTIME") !== -1
2,988✔
1618

3,696✔
1619
        if (isDatetimeFunction) {
3,696✔
1620
            // extract precision, e.g. "(3)"
888✔
1621
            const precision = value.match(/\(\d+\)/)
888✔
1622

888✔
1623
            if (upperCaseValue.indexOf("CURRENT_TIMESTAMP") !== -1) {
888✔
1624
                return precision
228✔
1625
                    ? `('now'::text)::timestamp${precision[0]} with time zone`
228✔
1626
                    : "now()"
228✔
1627
            } else if (upperCaseValue === "CURRENT_DATE") {
888✔
1628
                return "('now'::text)::date"
120✔
1629
            } else if (upperCaseValue.indexOf("CURRENT_TIME") !== -1) {
660✔
1630
                return precision
180✔
1631
                    ? `('now'::text)::time${precision[0]} with time zone`
180✔
1632
                    : "('now'::text)::time with time zone"
180✔
1633
            } else if (upperCaseValue.indexOf("LOCALTIMESTAMP") !== -1) {
540✔
1634
                return precision
180✔
1635
                    ? `('now'::text)::timestamp${precision[0]} without time zone`
180✔
1636
                    : "('now'::text)::timestamp without time zone"
180✔
1637
            } else if (upperCaseValue.indexOf("LOCALTIME") !== -1) {
180✔
1638
                return precision
180✔
1639
                    ? `('now'::text)::time${precision[0]} without time zone`
180✔
1640
                    : "('now'::text)::time without time zone"
180✔
1641
            }
180✔
1642
        }
888✔
1643

2,808✔
1644
        return value
2,808✔
1645
    }
2,808✔
1646

26✔
1647
    /**
26✔
1648
     * Escapes a given comment.
26✔
1649
     */
26✔
1650
    protected escapeComment(comment?: string) {
26✔
1651
        if (!comment) return comment
79,460✔
1652

400✔
1653
        comment = comment.replace(/\u0000/g, "") // Null bytes aren't allowed in comments
400✔
1654

400✔
1655
        return comment
400✔
1656
    }
400✔
1657
}
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