• 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.53
/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 set on the first
26✔
77
     * connection attempt.
26✔
78
     */
26✔
79
    version?: string
26✔
80

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

26✔
297
    isGeneratedColumnsSupported: boolean = false
26✔
298

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

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

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

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

2,684✔
324
        this.database = DriverUtils.buildDriverOptions(
2,684✔
325
            this.options.replication
2,684✔
326
                ? this.options.replication.master
2,728✔
327
                : this.options,
2,970!
328
        ).database
2,970✔
329
        this.schema = DriverUtils.buildDriverOptions(this.options).schema
2,970✔
330

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

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

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

2,684✔
366
        if (!this.version || !this.database || !this.searchSchema) {
2,684!
367
            const queryRunner = this.createQueryRunner("master")
2,684✔
368

2,684✔
369
            if (!this.version) {
2,684✔
370
                this.version = await queryRunner.getVersion()
2,684✔
371
            }
2,684✔
372

2,684✔
373
            if (!this.database) {
2,684!
374
                this.database = await queryRunner.getCurrentDatabase()
×
375
            }
×
376

2,684✔
377
            if (!this.searchSchema) {
2,684✔
378
                this.searchSchema = await queryRunner.getCurrentSchema()
2,684✔
379
            }
2,684✔
380

2,684✔
381
            await queryRunner.release()
2,684✔
382
        }
2,684✔
383

2,684✔
384
        if (!this.schema) {
2,684✔
385
            this.schema = this.searchSchema
2,644✔
386
        }
2,644✔
387
    }
2,684✔
388

26✔
389
    /**
26✔
390
     * Makes any action after connection (e.g. create extensions in Postgres driver).
26✔
391
     */
26✔
392
    async afterConnect(): Promise<void> {
26✔
393
        const extensionsMetadata = await this.checkMetadataForExtensions()
2,676✔
394
        const [connection, release] = await this.obtainMasterConnection()
2,676✔
395

2,676✔
396
        const installExtensions =
2,676✔
397
            this.options.installExtensions === undefined ||
2,676✔
398
            this.options.installExtensions
8✔
399
        if (installExtensions && extensionsMetadata.hasExtensions) {
2,676✔
400
            await this.enableExtensions(extensionsMetadata, connection)
308✔
401
        }
308✔
402

2,676✔
403
        this.isGeneratedColumnsSupported = VersionUtils.isGreaterOrEqual(
2,676✔
404
            this.version,
2,676✔
405
            "12.0",
2,676✔
406
        )
2,676✔
407

2,676✔
408
        await release()
2,676✔
409
    }
2,676✔
410

26✔
411
    protected async enableExtensions(extensionsMetadata: any, connection: any) {
26✔
412
        const { logger } = this.connection
308✔
413

308✔
414
        const {
308✔
415
            hasUuidColumns,
308✔
416
            hasCitextColumns,
308✔
417
            hasHstoreColumns,
308✔
418
            hasCubeColumns,
308✔
419
            hasGeometryColumns,
308✔
420
            hasLtreeColumns,
308✔
421
            hasVectorColumns,
308✔
422
            hasExclusionConstraints,
308✔
423
        } = extensionsMetadata
308✔
424

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

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

2,676✔
595
        return {
2,676✔
596
            hasUuidColumns,
2,676✔
597
            hasCitextColumns,
2,676✔
598
            hasHstoreColumns,
2,676✔
599
            hasCubeColumns,
2,676✔
600
            hasGeometryColumns,
2,676✔
601
            hasLtreeColumns,
2,676✔
602
            hasVectorColumns,
2,676✔
603
            hasExclusionConstraints,
2,676✔
604
            hasExtensions:
2,676✔
605
                hasUuidColumns ||
2,676✔
606
                hasCitextColumns ||
2,676✔
607
                hasHstoreColumns ||
2,676✔
608
                hasGeometryColumns ||
2,676✔
609
                hasCubeColumns ||
2,676✔
610
                hasLtreeColumns ||
2,676✔
611
                hasVectorColumns ||
2,676✔
612
                hasExclusionConstraints,
2,676✔
613
        }
2,676✔
614
    }
2,676✔
615

26✔
616
    /**
26✔
617
     * Closes connection with database.
26✔
618
     */
26✔
619
    async disconnect(): Promise<void> {
26✔
620
        if (!this.master) {
2,684!
621
            throw new ConnectionIsNotSetError("postgres")
×
622
        }
×
623

2,684✔
624
        await this.closePool(this.master)
2,684✔
625
        await Promise.all(this.slaves.map((slave) => this.closePool(slave)))
2,684✔
626
        this.master = undefined
2,684✔
627
        this.slaves = []
2,684✔
628
    }
2,684✔
629

26✔
630
    /**
26✔
631
     * Creates a schema builder used to build and sync a schema.
26✔
632
     */
26✔
633
    createSchemaBuilder() {
26✔
634
        return new RdbmsSchemaBuilder(this.connection)
7,744✔
635
    }
7,744✔
636

26✔
637
    /**
26✔
638
     * Creates a query runner used to execute database queries.
26✔
639
     */
26✔
640
    createQueryRunner(mode: ReplicationMode): PostgresQueryRunner {
26✔
641
        return new PostgresQueryRunner(this, mode)
57,980✔
642
    }
57,980✔
643

26✔
644
    /**
26✔
645
     * Prepares given value to a value to be persisted, based on its column type and metadata.
26✔
646
     */
26✔
647
    preparePersistentValue(value: any, columnMetadata: ColumnMetadata): any {
26✔
648
        if (columnMetadata.transformer)
767,512✔
649
            value = ApplyValueTransformers.transformTo(
767,512✔
650
                columnMetadata.transformer,
212✔
651
                value,
212✔
652
            )
212✔
653

767,512✔
654
        if (value === null || value === undefined) return value
767,512✔
655

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

661,908✔
734
        return value
661,908✔
735
    }
661,908✔
736

26✔
737
    /**
26✔
738
     * Prepares given value to a value to be persisted, based on its column type or metadata.
26✔
739
     */
26✔
740
    prepareHydratedValue(value: any, columnMetadata: ColumnMetadata): any {
26✔
741
        if (value === null || value === undefined)
404,908✔
742
            return columnMetadata.transformer
404,908✔
743
                ? ApplyValueTransformers.transformFrom(
16,144✔
744
                      columnMetadata.transformer,
64✔
745
                      value,
64✔
746
                  )
16,144✔
747
                : value
16,144✔
748

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

4✔
811
                value = []
4✔
812
                let cube: RegExpExecArray | null = null
4✔
813
                // Iterate through all regexp matches for cubes/null in array
4✔
814
                while ((cube = regexp.exec(unparsedArrayString)) !== null) {
4✔
815
                    if (cube[1] !== undefined) {
8✔
816
                        value.push(
8✔
817
                            cube[1].split(",").filter(Boolean).map(Number),
8✔
818
                        )
8✔
819
                    } else {
8!
820
                        value.push(undefined)
×
821
                    }
×
822
                }
8✔
823
            } else {
36✔
824
                value = value.split(",").filter(Boolean).map(Number)
32✔
825
            }
32✔
826
        } else if (
384,328✔
827
            columnMetadata.type === "enum" ||
384,292✔
828
            columnMetadata.type === "simple-enum"
383,816✔
829
        ) {
384,292✔
830
            if (columnMetadata.isArray) {
700✔
831
                if (value === "{}") return []
412✔
832

336✔
833
                // manually convert enum array to array of values (pg does not support, see https://github.com/brianc/node-pg-types/issues/56)
336✔
834
                value = (value as string)
336✔
835
                    .slice(1, -1)
336✔
836
                    .split(",")
336✔
837
                    .map((val) => {
336✔
838
                        // replace double quotes from the beginning and from the end
552✔
839
                        if (val.startsWith(`"`) && val.endsWith(`"`))
552✔
840
                            val = val.slice(1, -1)
552✔
841
                        // replace escaped backslash and double quotes
552✔
842
                        return val.replace(/\\(\\|")/g, "$1")
552✔
843
                    })
336✔
844

336✔
845
                // convert to number if that exists in possible enum options
336✔
846
                value = value.map((val: string) => {
336✔
847
                    return !isNaN(+val) &&
552✔
848
                        columnMetadata.enum!.indexOf(parseInt(val)) >= 0
200✔
849
                        ? parseInt(val)
552✔
850
                        : val
552✔
851
                })
336✔
852
            } else {
700✔
853
                // convert to number if that exists in possible enum options
288✔
854
                value =
288✔
855
                    !isNaN(+value) &&
288✔
856
                    columnMetadata.enum!.indexOf(parseInt(value)) >= 0
112✔
857
                        ? parseInt(value)
288✔
858
                        : value
288✔
859
            }
288✔
860
        } else if (columnMetadata.type === Number) {
384,292✔
861
            // convert to number if number
199,344✔
862
            value = !isNaN(+value) ? parseInt(value) : value
199,344!
863
        }
199,344✔
864

388,656✔
865
        if (columnMetadata.transformer)
388,656✔
866
            value = ApplyValueTransformers.transformFrom(
404,908✔
867
                columnMetadata.transformer,
332✔
868
                value,
332✔
869
            )
332✔
870
        return value
388,656✔
871
    }
388,656✔
872

26✔
873
    /**
26✔
874
     * Replaces parameters in the given sql with special escaping character
26✔
875
     * and an array of parameter names to be passed to a query.
26✔
876
     */
26✔
877
    escapeQueryWithParameters(
26✔
878
        sql: string,
67,304✔
879
        parameters: ObjectLiteral,
67,304✔
880
        nativeParameters: ObjectLiteral,
67,304✔
881
    ): [string, any[]] {
67,304✔
882
        const escapedParameters: any[] = Object.keys(nativeParameters).map(
67,304✔
883
            (key) => nativeParameters[key],
67,304✔
884
        )
67,304✔
885
        if (!parameters || !Object.keys(parameters).length)
67,304✔
886
            return [sql, escapedParameters]
67,304✔
887

64,020✔
888
        const parameterIndexMap = new Map<string, number>()
64,020✔
889
        sql = sql.replace(
64,020✔
890
            /:(\.\.\.)?([A-Za-z0-9_.]+)/g,
64,020✔
891
            (full, isArray: string, key: string): string => {
64,020✔
892
                if (!parameters.hasOwnProperty(key)) {
1,034,348✔
893
                    return full
180✔
894
                }
180✔
895

1,034,168✔
896
                if (parameterIndexMap.has(key)) {
1,034,348✔
897
                    return this.parametersPrefix + parameterIndexMap.get(key)
60✔
898
                }
60✔
899

1,034,108✔
900
                const value: any = parameters[key]
1,034,108✔
901

1,034,108✔
902
                if (isArray) {
1,034,348✔
903
                    return value
612✔
904
                        .map((v: any) => {
612✔
905
                            escapedParameters.push(v)
816✔
906
                            return this.createParameter(
816✔
907
                                key,
816✔
908
                                escapedParameters.length - 1,
816✔
909
                            )
816✔
910
                        })
612✔
911
                        .join(", ")
612✔
912
                }
612✔
913

1,033,496✔
914
                if (typeof value === "function") {
1,034,348!
915
                    return value()
×
916
                }
×
917

1,033,496✔
918
                escapedParameters.push(value)
1,033,496✔
919
                parameterIndexMap.set(key, escapedParameters.length)
1,033,496✔
920
                return this.createParameter(key, escapedParameters.length - 1)
1,033,496✔
921
            },
64,020✔
922
        ) // todo: make replace only in value statements, otherwise problems
64,020✔
923
        return [sql, escapedParameters]
64,020✔
924
    }
64,020✔
925

26✔
926
    /**
26✔
927
     * Escapes a column name.
26✔
928
     */
26✔
929
    escape(columnName: string): string {
26✔
930
        return '"' + columnName + '"'
16,485,820✔
931
    }
16,485,820✔
932

26✔
933
    /**
26✔
934
     * Build full table name with schema name and table name.
26✔
935
     * E.g. myDB.mySchema.myTable
26✔
936
     */
26✔
937
    buildTableName(tableName: string, schema?: string): string {
26✔
938
        const tablePath = [tableName]
1,634,366✔
939

1,634,366✔
940
        if (schema) {
1,634,366✔
941
            tablePath.unshift(schema)
1,593,586✔
942
        }
1,593,586✔
943

1,634,366✔
944
        return tablePath.join(".")
1,634,366✔
945
    }
1,634,366✔
946

26✔
947
    /**
26✔
948
     * Parse a target table name or other types and return a normalized table definition.
26✔
949
     */
26✔
950
    parseTableName(
26✔
951
        target: EntityMetadata | Table | View | TableForeignKey | string,
2,644,628✔
952
    ): { database?: string; schema?: string; tableName: string } {
2,644,628✔
953
        const driverDatabase = this.database
2,644,628✔
954
        const driverSchema = this.schema
2,644,628✔
955

2,644,628✔
956
        if (InstanceChecker.isTable(target) || InstanceChecker.isView(target)) {
2,644,628✔
957
            const parsed = this.parseTableName(target.name)
876,830✔
958

876,830✔
959
            return {
876,830✔
960
                database: target.database || parsed.database || driverDatabase,
876,830!
961
                schema: target.schema || parsed.schema || driverSchema,
876,830!
962
                tableName: parsed.tableName,
876,830✔
963
            }
876,830✔
964
        }
876,830✔
965

1,767,798✔
966
        if (InstanceChecker.isTableForeignKey(target)) {
2,644,628✔
967
            const parsed = this.parseTableName(target.referencedTableName)
16,096✔
968

16,096✔
969
            return {
16,096✔
970
                database:
16,096✔
971
                    target.referencedDatabase ||
16,096✔
972
                    parsed.database ||
16,096!
973
                    driverDatabase,
16,096✔
974
                schema:
16,096✔
975
                    target.referencedSchema || parsed.schema || driverSchema,
16,096!
976
                tableName: parsed.tableName,
16,096✔
977
            }
16,096✔
978
        }
16,096✔
979

1,751,702✔
980
        if (InstanceChecker.isEntityMetadata(target)) {
2,644,628✔
981
            // EntityMetadata tableName is never a path
809,756✔
982

809,756✔
983
            return {
809,756✔
984
                database: target.database || driverDatabase,
809,756✔
985
                schema: target.schema || driverSchema,
809,756✔
986
                tableName: target.tableName,
809,756✔
987
            }
809,756✔
988
        }
809,756✔
989

941,946✔
990
        const parts = target.split(".")
941,946✔
991

941,946✔
992
        return {
941,946✔
993
            database: driverDatabase,
941,946✔
994
            schema: (parts.length > 1 ? parts[0] : undefined) || driverSchema,
2,644,628✔
995
            tableName: parts.length > 1 ? parts[1] : parts[0],
2,644,628✔
996
        }
2,644,628✔
997
    }
2,644,628✔
998

26✔
999
    /**
26✔
1000
     * Creates a database type from a given column metadata.
26✔
1001
     */
26✔
1002
    normalizeType(column: {
26✔
1003
        type?: ColumnType
182,264✔
1004
        length?: number | string
182,264✔
1005
        precision?: number | null
182,264✔
1006
        scale?: number
182,264✔
1007
        isArray?: boolean
182,264✔
1008
    }): string {
182,264✔
1009
        if (
182,264✔
1010
            column.type === Number ||
182,264✔
1011
            column.type === "int" ||
182,264✔
1012
            column.type === "int4"
96,252✔
1013
        ) {
182,264✔
1014
            return "integer"
88,288✔
1015
        } else if (column.type === String || column.type === "varchar") {
182,264✔
1016
            return "character varying"
64,848✔
1017
        } else if (column.type === Date || column.type === "timestamp") {
93,976✔
1018
            return "timestamp without time zone"
4,928✔
1019
        } else if (column.type === "timestamptz") {
29,128✔
1020
            return "timestamp with time zone"
188✔
1021
        } else if (column.type === "time") {
24,200✔
1022
            return "time without time zone"
792✔
1023
        } else if (column.type === "timetz") {
24,012✔
1024
            return "time with time zone"
128✔
1025
        } else if (column.type === Boolean || column.type === "bool") {
23,220✔
1026
            return "boolean"
3,884✔
1027
        } else if (column.type === "simple-array") {
23,092✔
1028
            return "text"
140✔
1029
        } else if (column.type === "simple-json") {
19,208✔
1030
            return "text"
200✔
1031
        } else if (column.type === "simple-enum") {
19,068✔
1032
            return "enum"
544✔
1033
        } else if (column.type === "int2") {
18,868✔
1034
            return "smallint"
152✔
1035
        } else if (column.type === "int8") {
18,324✔
1036
            return "bigint"
280✔
1037
        } else if (column.type === "decimal") {
18,172✔
1038
            return "numeric"
424✔
1039
        } else if (column.type === "float8" || column.type === "float") {
17,892✔
1040
            return "double precision"
196✔
1041
        } else if (column.type === "float4") {
17,468✔
1042
            return "real"
128✔
1043
        } else if (column.type === "char") {
17,272✔
1044
            return "character"
288✔
1045
        } else if (column.type === "varbit") {
17,144✔
1046
            return "bit varying"
128✔
1047
        } else {
16,856✔
1048
            return (column.type as string) || ""
16,728!
1049
        }
16,728✔
1050
    }
182,264✔
1051

26✔
1052
    /**
26✔
1053
     * Normalizes "default" value of the column.
26✔
1054
     */
26✔
1055
    normalizeDefault(columnMetadata: ColumnMetadata): string | undefined {
26✔
1056
        const defaultValue = columnMetadata.default
141,120✔
1057

141,120✔
1058
        if (defaultValue === null || defaultValue === undefined) {
141,120✔
1059
            return undefined
130,696✔
1060
        }
130,696✔
1061

10,424✔
1062
        if (columnMetadata.isArray && Array.isArray(defaultValue)) {
141,120✔
1063
            return `'{${defaultValue.map((val) => String(val)).join(",")}}'`
324✔
1064
        }
324✔
1065

10,100✔
1066
        if (
10,100✔
1067
            (columnMetadata.type === "enum" ||
10,100✔
1068
                columnMetadata.type === "simple-enum" ||
141,120✔
1069
                typeof defaultValue === "number" ||
141,120✔
1070
                typeof defaultValue === "string") &&
141,120✔
1071
            defaultValue !== undefined
6,268✔
1072
        ) {
141,120✔
1073
            return `'${defaultValue}'`
6,268✔
1074
        }
6,268✔
1075

3,832✔
1076
        if (typeof defaultValue === "boolean") {
141,120✔
1077
            return defaultValue ? "true" : "false"
72✔
1078
        }
72✔
1079

3,760✔
1080
        if (typeof defaultValue === "function") {
141,120✔
1081
            const value = defaultValue()
3,696✔
1082

3,696✔
1083
            return this.normalizeDatetimeFunction(value)
3,696✔
1084
        }
3,696✔
1085

64✔
1086
        if (typeof defaultValue === "object") {
64✔
1087
            return `'${JSON.stringify(defaultValue)}'`
64✔
1088
        }
64✔
1089

×
1090
        return `${defaultValue}`
×
1091
    }
×
1092

26✔
1093
    /**
26✔
1094
     * Compares "default" value of the column.
26✔
1095
     * 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✔
1096
     */
26✔
1097
    private defaultEqual(
26✔
1098
        columnMetadata: ColumnMetadata,
65,312✔
1099
        tableColumn: TableColumn,
65,312✔
1100
    ): boolean {
65,312✔
1101
        if (
65,312✔
1102
            ["json", "jsonb"].includes(columnMetadata.type as string) &&
65,312✔
1103
            !["function", "undefined"].includes(typeof columnMetadata.default)
472✔
1104
        ) {
65,312✔
1105
            const tableColumnDefault =
124✔
1106
                typeof tableColumn.default === "string"
124✔
1107
                    ? JSON.parse(
124✔
1108
                          tableColumn.default.substring(
76✔
1109
                              1,
76✔
1110
                              tableColumn.default.length - 1,
76✔
1111
                          ),
76✔
1112
                      )
124✔
1113
                    : tableColumn.default
124✔
1114

124✔
1115
            return OrmUtils.deepCompare(
124✔
1116
                columnMetadata.default,
124✔
1117
                tableColumnDefault,
124✔
1118
            )
124✔
1119
        }
124✔
1120

65,188✔
1121
        const columnDefault = this.lowerDefaultValueIfNecessary(
65,188✔
1122
            this.normalizeDefault(columnMetadata),
65,188✔
1123
        )
65,188✔
1124
        return columnDefault === tableColumn.default
65,188✔
1125
    }
65,188✔
1126

26✔
1127
    /**
26✔
1128
     * Normalizes "isUnique" value of the column.
26✔
1129
     */
26✔
1130
    normalizeIsUnique(column: ColumnMetadata): boolean {
26✔
1131
        return column.entityMetadata.uniques.some(
159,784✔
1132
            (uq) => uq.columns.length === 1 && uq.columns[0] === column,
159,784✔
1133
        )
159,784✔
1134
    }
159,784✔
1135

26✔
1136
    /**
26✔
1137
     * Returns default column lengths, which is required on column creation.
26✔
1138
     */
26✔
1139
    getColumnLength(column: ColumnMetadata): string {
26✔
1140
        return column.length ? column.length.toString() : ""
76,332✔
1141
    }
76,332✔
1142

26✔
1143
    /**
26✔
1144
     * Creates column type definition including length, precision and scale
26✔
1145
     */
26✔
1146
    createFullType(column: TableColumn): string {
26✔
1147
        let type = column.type
63,532✔
1148

63,532✔
1149
        if (column.length) {
63,532✔
1150
            type += "(" + column.length + ")"
2,064✔
1151
        } else if (
63,532✔
1152
            column.precision !== null &&
61,468✔
1153
            column.precision !== undefined &&
61,468✔
1154
            column.scale !== null &&
61,468✔
1155
            column.scale !== undefined
408✔
1156
        ) {
61,468✔
1157
            type += "(" + column.precision + "," + column.scale + ")"
144✔
1158
        } else if (
61,468✔
1159
            column.precision !== null &&
61,324✔
1160
            column.precision !== undefined
61,324✔
1161
        ) {
61,324✔
1162
            type += "(" + column.precision + ")"
264✔
1163
        }
264✔
1164

63,532✔
1165
        if (column.type === "time without time zone") {
63,532✔
1166
            type =
316✔
1167
                "TIME" +
316✔
1168
                (column.precision !== null && column.precision !== undefined
316✔
1169
                    ? "(" + column.precision + ")"
316✔
1170
                    : "")
316✔
1171
        } else if (column.type === "time with time zone") {
63,532✔
1172
            type =
188✔
1173
                "TIME" +
188✔
1174
                (column.precision !== null && column.precision !== undefined
188✔
1175
                    ? "(" + column.precision + ")"
188✔
1176
                    : "") +
188✔
1177
                " WITH TIME ZONE"
188✔
1178
        } else if (column.type === "timestamp without time zone") {
63,216✔
1179
            type =
2,060✔
1180
                "TIMESTAMP" +
2,060✔
1181
                (column.precision !== null && column.precision !== undefined
2,060✔
1182
                    ? "(" + column.precision + ")"
2,060✔
1183
                    : "")
2,060✔
1184
        } else if (column.type === "timestamp with time zone") {
63,028✔
1185
            type =
228✔
1186
                "TIMESTAMP" +
228✔
1187
                (column.precision !== null && column.precision !== undefined
228✔
1188
                    ? "(" + column.precision + ")"
228✔
1189
                    : "") +
228✔
1190
                " WITH TIME ZONE"
228✔
1191
        } else if (this.spatialTypes.indexOf(column.type as ColumnType) >= 0) {
60,968✔
1192
            if (column.spatialFeatureType != null && column.srid != null) {
136✔
1193
                type = `${column.type}(${column.spatialFeatureType},${column.srid})`
40✔
1194
            } else if (column.spatialFeatureType != null) {
136✔
1195
                type = `${column.type}(${column.spatialFeatureType})`
32✔
1196
            } else {
96✔
1197
                type = column.type
64✔
1198
            }
64✔
1199
        } else if (column.type === "vector" || column.type === "halfvec") {
60,740✔
1200
            type =
160✔
1201
                column.type + (column.length ? "(" + column.length + ")" : "")
160✔
1202
        }
160✔
1203

63,532✔
1204
        if (column.isArray) type += " array"
63,532✔
1205

63,532✔
1206
        return type
63,532✔
1207
    }
63,532✔
1208

26✔
1209
    /**
26✔
1210
     * Obtains a new database connection to a master server.
26✔
1211
     * Used for replication.
26✔
1212
     * If replication is not setup then returns default connection's database connection.
26✔
1213
     */
26✔
1214
    async obtainMasterConnection(): Promise<[any, Function]> {
26✔
1215
        if (!this.master) {
57,964!
1216
            throw new TypeORMError("Driver not Connected")
×
1217
        }
×
1218

57,964✔
1219
        return new Promise((ok, fail) => {
57,964✔
1220
            this.master.connect((err: any, connection: any, release: any) => {
57,964✔
1221
                if (err) {
57,964!
NEW
1222
                    fail(err)
×
1223
                } else {
57,964✔
1224
                    ok([connection, release])
57,964✔
1225
                }
57,964✔
1226
            })
57,964✔
1227
        })
57,964✔
1228
    }
57,964✔
1229

26✔
1230
    /**
26✔
1231
     * Obtains a new database connection to a slave server.
26✔
1232
     * Used for replication.
26✔
1233
     * If replication is not setup then returns master (default) connection's database connection.
26✔
1234
     */
26✔
1235
    async obtainSlaveConnection(): Promise<[any, Function]> {
26✔
1236
        if (!this.slaves.length) {
12!
1237
            return this.obtainMasterConnection()
×
1238
        }
×
1239

12✔
1240
        const random = Math.floor(Math.random() * this.slaves.length)
12✔
1241

12✔
1242
        return new Promise((ok, fail) => {
12✔
1243
            this.slaves[random].connect(
12✔
1244
                (err: any, connection: any, release: any) => {
12✔
1245
                    if (err) {
12!
NEW
1246
                        fail(err)
×
1247
                    } else {
12✔
1248
                        ok([connection, release])
12✔
1249
                    }
12✔
1250
                },
12✔
1251
            )
12✔
1252
        })
12✔
1253
    }
12✔
1254

26✔
1255
    /**
26✔
1256
     * Creates generated map of values generated or returned by database after INSERT query.
26✔
1257
     *
26✔
1258
     * todo: slow. optimize Object.keys(), OrmUtils.mergeDeep and column.createValueMap parts
26✔
1259
     */
26✔
1260
    createGeneratedMap(metadata: EntityMetadata, insertResult: ObjectLiteral) {
26✔
1261
        if (!insertResult) return undefined
224,892✔
1262

160,664✔
1263
        return Object.keys(insertResult).reduce((map, key) => {
160,664✔
1264
            const column = metadata.findColumnWithDatabaseName(key)
164,352✔
1265
            if (column) {
164,352✔
1266
                OrmUtils.mergeDeep(
164,352✔
1267
                    map,
164,352✔
1268
                    column.createValueMap(insertResult[key]),
164,352✔
1269
                )
164,352✔
1270
                // OrmUtils.mergeDeep(map, column.createValueMap(this.prepareHydratedValue(insertResult[key], column))); // TODO: probably should be like there, but fails on enums, fix later
164,352✔
1271
            }
164,352✔
1272
            return map
164,352✔
1273
        }, {} as ObjectLiteral)
160,664✔
1274
    }
160,664✔
1275

26✔
1276
    /**
26✔
1277
     * Differentiate columns of this table and columns from the given column metadatas columns
26✔
1278
     * and returns only changed.
26✔
1279
     */
26✔
1280
    findChangedColumns(
26✔
1281
        tableColumns: TableColumn[],
23,552✔
1282
        columnMetadatas: ColumnMetadata[],
23,552✔
1283
    ): ColumnMetadata[] {
23,552✔
1284
        return columnMetadatas.filter((columnMetadata) => {
23,552✔
1285
            const tableColumn = tableColumns.find(
79,892✔
1286
                (c) => c.name === columnMetadata.databaseName,
79,892✔
1287
            )
79,892✔
1288
            if (!tableColumn) return false // we don't need new columns, we only need exist and changed
79,892✔
1289

79,876✔
1290
            const isColumnChanged =
79,876✔
1291
                tableColumn.name !== columnMetadata.databaseName ||
79,876✔
1292
                tableColumn.type !== this.normalizeType(columnMetadata) ||
79,892✔
1293
                tableColumn.length !== columnMetadata.length ||
79,892✔
1294
                tableColumn.isArray !== columnMetadata.isArray ||
79,892✔
1295
                tableColumn.precision !== columnMetadata.precision ||
79,892✔
1296
                (columnMetadata.scale !== undefined &&
79,812✔
1297
                    tableColumn.scale !== columnMetadata.scale) ||
79,892✔
1298
                tableColumn.comment !==
79,812✔
1299
                    this.escapeComment(columnMetadata.comment) ||
79,892✔
1300
                (!tableColumn.isGenerated &&
79,780✔
1301
                    !this.defaultEqual(columnMetadata, tableColumn)) || // we included check for generated here, because generated columns already can have default values
79,892✔
1302
                tableColumn.isPrimary !== columnMetadata.isPrimary ||
79,892✔
1303
                tableColumn.isNullable !== columnMetadata.isNullable ||
79,892✔
1304
                tableColumn.isUnique !==
79,668✔
1305
                    this.normalizeIsUnique(columnMetadata) ||
79,892✔
1306
                tableColumn.enumName !== columnMetadata.enumName ||
79,892✔
1307
                (tableColumn.enum &&
79,656✔
1308
                    columnMetadata.enum &&
79,656✔
1309
                    !OrmUtils.isArraysEqual(
1,100✔
1310
                        tableColumn.enum,
1,100✔
1311
                        columnMetadata.enum.map((val) => val + ""),
1,100✔
1312
                    )) || // enums in postgres are always strings
79,892✔
1313
                tableColumn.isGenerated !== columnMetadata.isGenerated ||
79,892✔
1314
                (tableColumn.spatialFeatureType || "").toLowerCase() !==
79,640✔
1315
                    (columnMetadata.spatialFeatureType || "").toLowerCase() ||
79,892✔
1316
                tableColumn.srid !== columnMetadata.srid ||
79,892✔
1317
                tableColumn.generatedType !== columnMetadata.generatedType ||
79,892✔
1318
                (tableColumn.asExpression || "").trim() !==
79,640✔
1319
                    (columnMetadata.asExpression || "").trim() ||
79,892✔
1320
                tableColumn.collation !== columnMetadata.collation
79,640✔
1321

79,892✔
1322
            // DEBUG SECTION
79,892✔
1323
            // if (isColumnChanged) {
79,892✔
1324
            //     console.log("table:", columnMetadata.entityMetadata.tableName)
79,892✔
1325
            //     console.log(
79,892✔
1326
            //         "name:",
79,892✔
1327
            //         tableColumn.name,
79,892✔
1328
            //         columnMetadata.databaseName,
79,892✔
1329
            //     )
79,892✔
1330
            //     console.log(
79,892✔
1331
            //         "type:",
79,892✔
1332
            //         tableColumn.type,
79,892✔
1333
            //         this.normalizeType(columnMetadata),
79,892✔
1334
            //     )
79,892✔
1335
            //     console.log(
79,892✔
1336
            //         "length:",
79,892✔
1337
            //         tableColumn.length,
79,892✔
1338
            //         columnMetadata.length,
79,892✔
1339
            //     )
79,892✔
1340
            //     console.log(
79,892✔
1341
            //         "isArray:",
79,892✔
1342
            //         tableColumn.isArray,
79,892✔
1343
            //         columnMetadata.isArray,
79,892✔
1344
            //     )
79,892✔
1345
            //     console.log(
79,892✔
1346
            //         "precision:",
79,892✔
1347
            //         tableColumn.precision,
79,892✔
1348
            //         columnMetadata.precision,
79,892✔
1349
            //     )
79,892✔
1350
            //     console.log("scale:", tableColumn.scale, columnMetadata.scale)
79,892✔
1351
            //     console.log(
79,892✔
1352
            //         "comment:",
79,892✔
1353
            //         tableColumn.comment,
79,892✔
1354
            //         this.escapeComment(columnMetadata.comment),
79,892✔
1355
            //     )
79,892✔
1356
            //     console.log(
79,892✔
1357
            //         "enumName:",
79,892✔
1358
            //         tableColumn.enumName,
79,892✔
1359
            //         columnMetadata.enumName,
79,892✔
1360
            //     )
79,892✔
1361
            //     console.log(
79,892✔
1362
            //         "enum:",
79,892✔
1363
            //         tableColumn.enum &&
79,892✔
1364
            //             columnMetadata.enum &&
79,892✔
1365
            //             !OrmUtils.isArraysEqual(
79,892✔
1366
            //                 tableColumn.enum,
79,892✔
1367
            //                 columnMetadata.enum.map((val) => val + ""),
79,892✔
1368
            //             ),
79,892✔
1369
            //     )
79,892✔
1370
            //     console.log(
79,892✔
1371
            //         "isPrimary:",
79,892✔
1372
            //         tableColumn.isPrimary,
79,892✔
1373
            //         columnMetadata.isPrimary,
79,892✔
1374
            //     )
79,892✔
1375
            //     console.log(
79,892✔
1376
            //         "isNullable:",
79,892✔
1377
            //         tableColumn.isNullable,
79,892✔
1378
            //         columnMetadata.isNullable,
79,892✔
1379
            //     )
79,892✔
1380
            //     console.log(
79,892✔
1381
            //         "isUnique:",
79,892✔
1382
            //         tableColumn.isUnique,
79,892✔
1383
            //         this.normalizeIsUnique(columnMetadata),
79,892✔
1384
            //     )
79,892✔
1385
            //     console.log(
79,892✔
1386
            //         "isGenerated:",
79,892✔
1387
            //         tableColumn.isGenerated,
79,892✔
1388
            //         columnMetadata.isGenerated,
79,892✔
1389
            //     )
79,892✔
1390
            //     console.log(
79,892✔
1391
            //         "generatedType:",
79,892✔
1392
            //         tableColumn.generatedType,
79,892✔
1393
            //         columnMetadata.generatedType,
79,892✔
1394
            //     )
79,892✔
1395
            //     console.log(
79,892✔
1396
            //         "asExpression:",
79,892✔
1397
            //         (tableColumn.asExpression || "").trim(),
79,892✔
1398
            //         (columnMetadata.asExpression || "").trim(),
79,892✔
1399
            //     )
79,892✔
1400
            //     console.log(
79,892✔
1401
            //         "collation:",
79,892✔
1402
            //         tableColumn.collation,
79,892✔
1403
            //         columnMetadata.collation,
79,892✔
1404
            //     )
79,892✔
1405
            //     console.log(
79,892✔
1406
            //         "isGenerated 2:",
79,892✔
1407
            //         !tableColumn.isGenerated &&
79,892✔
1408
            //             this.lowerDefaultValueIfNecessary(
79,892✔
1409
            //                 this.normalizeDefault(columnMetadata),
79,892✔
1410
            //             ) !== tableColumn.default,
79,892✔
1411
            //     )
79,892✔
1412
            //     console.log(
79,892✔
1413
            //         "spatialFeatureType:",
79,892✔
1414
            //         (tableColumn.spatialFeatureType || "").toLowerCase(),
79,892✔
1415
            //         (columnMetadata.spatialFeatureType || "").toLowerCase(),
79,892✔
1416
            //     )
79,892✔
1417
            //     console.log("srid", tableColumn.srid, columnMetadata.srid)
79,892✔
1418
            //     console.log("==========================================")
79,892✔
1419
            // }
79,892✔
1420

79,892✔
1421
            return isColumnChanged
79,892✔
1422
        })
23,552✔
1423
    }
23,552✔
1424

26✔
1425
    private lowerDefaultValueIfNecessary(value: string | undefined) {
26✔
1426
        // Postgres saves function calls in default value as lowercase #2733
65,188✔
1427
        if (!value) {
65,188✔
1428
            return value
59,808✔
1429
        }
59,808✔
1430
        return value
5,380✔
1431
            .split(`'`)
5,380✔
1432
            .map((v, i) => {
5,380✔
1433
                return i % 2 === 1 ? v : v.toLowerCase()
13,116✔
1434
            })
5,380✔
1435
            .join(`'`)
5,380✔
1436
    }
5,380✔
1437

26✔
1438
    /**
26✔
1439
     * Returns true if driver supports RETURNING / OUTPUT statement.
26✔
1440
     */
26✔
1441
    isReturningSqlSupported(): boolean {
26✔
1442
        return true
86,060✔
1443
    }
86,060✔
1444

26✔
1445
    /**
26✔
1446
     * Returns true if driver supports uuid values generation on its own.
26✔
1447
     */
26✔
1448
    isUUIDGenerationSupported(): boolean {
26✔
1449
        return true
568✔
1450
    }
568✔
1451

26✔
1452
    /**
26✔
1453
     * Returns true if driver supports fulltext indices.
26✔
1454
     */
26✔
1455
    isFullTextColumnTypeSupported(): boolean {
26✔
1456
        return false
204✔
1457
    }
204✔
1458

26✔
1459
    get uuidGenerator(): string {
26✔
1460
        return this.options.uuidExtension === "pgcrypto"
740✔
1461
            ? "gen_random_uuid()"
740✔
1462
            : "uuid_generate_v4()"
740✔
1463
    }
740✔
1464

26✔
1465
    /**
26✔
1466
     * Creates an escaped parameter.
26✔
1467
     */
26✔
1468
    createParameter(parameterName: string, index: number): string {
26✔
1469
        return this.parametersPrefix + (index + 1)
1,035,780✔
1470
    }
1,035,780✔
1471

26✔
1472
    // -------------------------------------------------------------------------
26✔
1473
    // Public Methods
26✔
1474
    // -------------------------------------------------------------------------
26✔
1475

26✔
1476
    /**
26✔
1477
     * Loads postgres query stream package.
26✔
1478
     */
26✔
1479
    loadStreamDependency() {
26✔
1480
        try {
8✔
1481
            return PlatformTools.load("pg-query-stream")
8✔
1482
        } catch {
8!
1483
            // todo: better error for browser env
×
1484
            throw new TypeORMError(
×
1485
                `To use streams you should install pg-query-stream package. Please run "npm i pg-query-stream".`,
×
1486
            )
×
1487
        }
×
1488
    }
8✔
1489

26✔
1490
    // -------------------------------------------------------------------------
26✔
1491
    // Protected Methods
26✔
1492
    // -------------------------------------------------------------------------
26✔
1493

26✔
1494
    /**
26✔
1495
     * If driver dependency is not given explicitly, then try to load it via "require".
26✔
1496
     */
26✔
1497
    protected loadDependencies(): void {
26✔
1498
        try {
2,684✔
1499
            const postgres = this.options.driver || PlatformTools.load("pg")
2,684✔
1500
            this.postgres = postgres
2,684✔
1501
            try {
2,684✔
1502
                const pgNative =
2,684✔
1503
                    this.options.nativeDriver || PlatformTools.load("pg-native")
2,684✔
1504
                if (pgNative && this.postgres.native)
2,684!
1505
                    this.postgres = this.postgres.native
2,684!
1506
            } catch (e) {}
2,684✔
1507
        } catch (e) {
2,684!
1508
            // todo: better error for browser env
×
1509
            throw new DriverPackageNotInstalledError("Postgres", "pg")
×
1510
        }
×
1511
    }
2,684✔
1512

26✔
1513
    /**
26✔
1514
     * Creates a new connection pool for a given database credentials.
26✔
1515
     */
26✔
1516
    protected async createPool(
26✔
1517
        options: PostgresConnectionOptions,
2,716✔
1518
        credentials: PostgresConnectionCredentialsOptions,
2,716✔
1519
    ): Promise<any> {
2,716✔
1520
        const { logger } = this.connection
2,716✔
1521
        credentials = Object.assign({}, credentials)
2,716✔
1522

2,716✔
1523
        // build connection options for the driver
2,716✔
1524
        // See: https://github.com/brianc/node-postgres/tree/master/packages/pg-pool#create
2,716✔
1525
        const connectionOptions = Object.assign(
2,716✔
1526
            {},
2,716✔
1527
            {
2,716✔
1528
                connectionString: credentials.url,
2,716✔
1529
                host: credentials.host,
2,716✔
1530
                user: credentials.username,
2,716✔
1531
                password: credentials.password,
2,716✔
1532
                database: credentials.database,
2,716✔
1533
                port: credentials.port,
2,716✔
1534
                ssl: credentials.ssl,
2,716✔
1535
                connectionTimeoutMillis: options.connectTimeoutMS,
2,716✔
1536
                application_name:
2,716✔
1537
                    options.applicationName ?? credentials.applicationName,
2,716✔
1538
                max: options.poolSize,
2,716✔
1539
            },
2,716✔
1540
            options.extra || {},
2,716✔
1541
        )
2,716✔
1542

2,716✔
1543
        if (options.parseInt8 !== undefined) {
2,716✔
1544
            if (
4✔
1545
                this.postgres.defaults &&
4✔
1546
                Object.getOwnPropertyDescriptor(
4✔
1547
                    this.postgres.defaults,
4✔
1548
                    "parseInt8",
4✔
1549
                )?.set
4✔
1550
            ) {
4✔
1551
                this.postgres.defaults.parseInt8 = options.parseInt8
4✔
1552
            } else {
4!
1553
                logger.log(
×
1554
                    "warn",
×
1555
                    "Attempted to set parseInt8 option, but the postgres driver does not support setting defaults.parseInt8. This option will be ignored.",
×
1556
                )
×
1557
            }
×
1558
        }
4✔
1559

2,716✔
1560
        // create a connection pool
2,716✔
1561
        const pool = new this.postgres.Pool(connectionOptions)
2,716✔
1562

2,716✔
1563
        const poolErrorHandler =
2,716✔
1564
            options.poolErrorHandler ||
2,716✔
1565
            ((error: any) =>
2,716✔
1566
                logger.log("warn", `Postgres pool raised an error. ${error}`))
2,716✔
1567

2,716✔
1568
        /*
2,716✔
1569
          Attaching an error handler to pool errors is essential, as, otherwise, errors raised will go unhandled and
2,716✔
1570
          cause the hosting app to crash.
2,716✔
1571
         */
2,716✔
1572
        pool.on("error", poolErrorHandler)
2,716✔
1573

2,716✔
1574
        return new Promise((ok, fail) => {
2,716✔
1575
            pool.connect((err: any, connection: any, release: Function) => {
2,716✔
1576
                if (err) return fail(err)
2,716!
1577

2,716✔
1578
                if (options.logNotifications) {
2,716✔
1579
                    connection.on("notice", (msg: any) => {
4✔
1580
                        if (msg) {
12✔
1581
                            this.connection.logger.log("info", msg.message)
12✔
1582
                        }
12✔
1583
                    })
4✔
1584
                    connection.on("notification", (msg: any) => {
4✔
1585
                        if (msg) {
4✔
1586
                            this.connection.logger.log(
4✔
1587
                                "info",
4✔
1588
                                `Received NOTIFY on channel ${msg.channel}: ${msg.payload}.`,
4✔
1589
                            )
4✔
1590
                        }
4✔
1591
                    })
4✔
1592
                }
4✔
1593
                release()
2,716✔
1594
                ok(pool)
2,716✔
1595
            })
2,716✔
1596
        })
2,716✔
1597
    }
2,716✔
1598

26✔
1599
    /**
26✔
1600
     * Closes connection pool.
26✔
1601
     */
26✔
1602
    protected async closePool(pool: any): Promise<void> {
26✔
1603
        while (this.connectedQueryRunners.length) {
2,716✔
1604
            await this.connectedQueryRunners[0].release()
64✔
1605
        }
64✔
1606

2,716✔
1607
        return new Promise<void>((ok, fail) => {
2,716✔
1608
            pool.end((err: any) => (err ? fail(err) : ok()))
2,716!
1609
        })
2,716✔
1610
    }
2,716✔
1611

26✔
1612
    /**
26✔
1613
     * Executes given query.
26✔
1614
     */
26✔
1615
    protected executeQuery(connection: any, query: string) {
26✔
1616
        this.connection.logger.logQuery(query)
328✔
1617

328✔
1618
        return new Promise((ok, fail) => {
328✔
1619
            connection.query(query, (err: any, result: any) =>
328✔
1620
                err ? fail(err) : ok(result),
328!
1621
            )
328✔
1622
        })
328✔
1623
    }
328✔
1624

26✔
1625
    /**
26✔
1626
     * If parameter is a datetime function, e.g. "CURRENT_TIMESTAMP", normalizes it.
26✔
1627
     * Otherwise returns original input.
26✔
1628
     */
26✔
1629
    protected normalizeDatetimeFunction(value: string) {
26✔
1630
        // check if input is datetime function
3,696✔
1631
        const upperCaseValue = value.toUpperCase()
3,696✔
1632
        const isDatetimeFunction =
3,696✔
1633
            upperCaseValue.indexOf("CURRENT_TIMESTAMP") !== -1 ||
3,696✔
1634
            upperCaseValue.indexOf("CURRENT_DATE") !== -1 ||
3,696✔
1635
            upperCaseValue.indexOf("CURRENT_TIME") !== -1 ||
3,696✔
1636
            upperCaseValue.indexOf("LOCALTIMESTAMP") !== -1 ||
3,696✔
1637
            upperCaseValue.indexOf("LOCALTIME") !== -1
2,988✔
1638

3,696✔
1639
        if (isDatetimeFunction) {
3,696✔
1640
            // extract precision, e.g. "(3)"
888✔
1641
            const precision = value.match(/\(\d+\)/)
888✔
1642

888✔
1643
            if (upperCaseValue.indexOf("CURRENT_TIMESTAMP") !== -1) {
888✔
1644
                return precision
228✔
1645
                    ? `('now'::text)::timestamp${precision[0]} with time zone`
228✔
1646
                    : "now()"
228✔
1647
            } else if (upperCaseValue === "CURRENT_DATE") {
888✔
1648
                return "('now'::text)::date"
120✔
1649
            } else if (upperCaseValue.indexOf("CURRENT_TIME") !== -1) {
660✔
1650
                return precision
180✔
1651
                    ? `('now'::text)::time${precision[0]} with time zone`
180✔
1652
                    : "('now'::text)::time with time zone"
180✔
1653
            } else if (upperCaseValue.indexOf("LOCALTIMESTAMP") !== -1) {
540✔
1654
                return precision
180✔
1655
                    ? `('now'::text)::timestamp${precision[0]} without time zone`
180✔
1656
                    : "('now'::text)::timestamp without time zone"
180✔
1657
            } else if (upperCaseValue.indexOf("LOCALTIME") !== -1) {
180✔
1658
                return precision
180✔
1659
                    ? `('now'::text)::time${precision[0]} without time zone`
180✔
1660
                    : "('now'::text)::time without time zone"
180✔
1661
            }
180✔
1662
        }
888✔
1663

2,808✔
1664
        return value
2,808✔
1665
    }
2,808✔
1666

26✔
1667
    /**
26✔
1668
     * Escapes a given comment.
26✔
1669
     */
26✔
1670
    protected escapeComment(comment?: string) {
26✔
1671
        if (!comment) return comment
79,812✔
1672

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

400✔
1675
        return comment
400✔
1676
    }
400✔
1677
}
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