Skip to content

Commit

Permalink
feat: add parseInt8 option to postgres driver. Closes #9341 (#9435)
Browse files Browse the repository at this point in the history
* feat: add parseInt8 option to postgres driver

* chore: formatting
  • Loading branch information
danmana committed Dec 3, 2022
1 parent 93e6b3d commit 2473ff0
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 1 deletion.
5 changes: 5 additions & 0 deletions docs/data-source-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ Different RDBMS-es have their own specific options.

- `applicationName` - A string visible in statistics and logs to help referencing an application to a connection (default: `undefined`)

- `parseInt8` - A boolean to enable parsing 64-bit integers (int8) as JavaScript integers.
By default int8 (bigint) values are returned as strings to avoid overflows.
JavaScript doesn't have support for 64-bit integers, the maximum safe integer in js is: Number.MAX_SAFE_INTEGER (`+2^53`). Be careful when enabling `parseInt8`.
Note: This option is ignored if the undelying driver does not support it.

## `sqlite` data source options

- `database` - Database path. For example "mydb.sql"
Expand Down
17 changes: 17 additions & 0 deletions src/driver/postgres/PostgresConnectionOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,21 @@ export interface PostgresConnectionOptions
* the service using this connection. Defaults to 'undefined'
*/
readonly applicationName?: string

/**
* Return 64-bit integers (int8) as JavaScript integers.
*
* Because JavaScript doesn't have support for 64-bit integers node-postgres cannot confidently
* parse int8 data type results as numbers because if you have a huge number it will overflow
* and the result you'd get back from node-postgres would not be the result in the database.
* That would be a very bad thing so node-postgres just returns int8 results as strings and leaves the parsing up to you.
*
* Enabling parseInt8 will cause node-postgres to parse int8 results as numbers.
* Note: the maximum safe integer in js is: Number.MAX_SAFE_INTEGER (`+2^53`)
*
* @see [JavaScript Number objects](http://ecma262-5.com/ELS5_HTML.htm#Section_8.5)
* @see [node-postgres int8 explanation](https://github.com/brianc/node-pg-types#:~:text=on%20projects%3A%20return-,64%2Dbit%20integers,-(int8)%20as)
* @see [node-postgres defaults.parseInt8 implementation](https://github.com/brianc/node-postgres/blob/pg%408.8.0/packages/pg/lib/defaults.js#L80)
*/
readonly parseInt8?: boolean
}
19 changes: 18 additions & 1 deletion src/driver/postgres/PostgresDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1449,6 +1449,7 @@ export class PostgresDriver implements Driver {
options: PostgresConnectionOptions,
credentials: PostgresConnectionCredentialsOptions,
): Promise<any> {
const { logger } = this.connection
credentials = Object.assign({}, credentials)

// build connection options for the driver
Expand All @@ -1470,9 +1471,25 @@ export class PostgresDriver implements Driver {
options.extra || {},
)

if (options.parseInt8 !== undefined) {
if (
this.postgres.defaults &&
Object.getOwnPropertyDescriptor(
this.postgres.defaults,
"parseInt8",
)?.set
) {
this.postgres.defaults.parseInt8 = options.parseInt8
} else {
logger.log(
"warn",
"Attempted to set parseInt8 option, but the postgres driver does not support setting defaults.parseInt8. This option will be ignored.",
)
}
}

// create a connection pool
const pool = new this.postgres.Pool(connectionOptions)
const { logger } = this.connection

const poolErrorHandler =
options.poolErrorHandler ||
Expand Down
13 changes: 13 additions & 0 deletions test/github-issues/9341/entity/TestEntity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Column, Entity, PrimaryGeneratedColumn } from "../../../../src"

@Entity()
export class TestEntity {
@PrimaryGeneratedColumn()
id: number

@Column({ type: "bigint", nullable: true })
big_int: number

@Column({ type: "numeric", nullable: true })
big_decimal: number
}
53 changes: 53 additions & 0 deletions test/github-issues/9341/issue-9341.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import "reflect-metadata"
import { expect } from "chai"
import { DataSource } from "../../../src/data-source/DataSource"
import {
closeTestingConnections,
createTestingConnections,
reloadTestingDatabases,
} from "../../utils/test-utils"
import { TestEntity } from "./entity/TestEntity"

describe('github issues > #9341 "bigNumberStrings:false" is not working for postgres', () => {
let connections: DataSource[]
before(
async () =>
(connections = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
enabledDrivers: ["postgres"],
driverSpecific: {
parseInt8: true,
},
})),
)
beforeEach(() => reloadTestingDatabases(connections))
after(() => closeTestingConnections(connections))

it("should fetch big int as number not string when using parseInt8=true", async () => {
for (const connection of connections) {
const origin = await connection.getRepository(TestEntity).save({
big_int: Number.MAX_SAFE_INTEGER,
big_decimal: 1.23456789,
})

const result = await connection.getRepository(TestEntity).findOne({
where: { id: origin.id },
})

// count also returns bigint (as string by default)
const [{ count }] = await connection.query(
`select count(*) from (VALUES (1), (2), (3)) as tmp`,
)

// big int should be number
expect(typeof result?.big_int).to.eq("number")
expect(result?.big_int).to.eq(Number.MAX_SAFE_INTEGER)
// big decimal should remain string, only int8 is parsed
expect(typeof result?.big_decimal).to.eq("string")
expect(result?.big_decimal).to.eq("1.23456789")
// count should be number (it is int8)
expect(typeof count).to.eq("number")
expect(count).to.eq(3)
}
})
})

0 comments on commit 2473ff0

Please sign in to comment.