fix(quaint): cast BigInt to text in JSON aggregation#5745
Conversation
Cast BigInt columns to ::text inside JSONB_BUILD_OBJECT to preserve precision when parsed by JavaScript. JavaScript's JSON.parse loses precision for integers larger than 2^53-1 (Number.MAX_SAFE_INTEGER). This is analogous to the existing MONEY -> ::numeric cast. Closes prisma#1
There was a problem hiding this comment.
Pull request overview
This PR addresses a critical precision loss issue when using BigInt fields in PostgreSQL's JSONB_BUILD_OBJECT with Prisma 7's relationJoins. JavaScript's JSON.parse cannot accurately represent integers larger than Number.MAX_SAFE_INTEGER (2^53 - 1), causing data corruption. The fix casts BigInt columns to ::text inside JSONB_BUILD_OBJECT calls, ensuring values are returned as JSON strings to preserve full precision.
Changes:
- Added BigInt to text casting logic in PostgreSQL visitor's
visit_json_build_obj_exprmethod - Added test case to verify BigInt casting behavior in JSONB_BUILD_OBJECT
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Merging this PR will not alter performance
Comparing Footnotes
|
## Problem
When using `relationJoins` with BigInt fields in Prisma 7, JavaScript's
`JSON.parse` loses precision for integers larger than
`Number.MAX_SAFE_INTEGER` (2^53 - 1).
This happens because PostgreSQL's `JSONB_BUILD_OBJECT` returns BigInt
values as JSON numbers, which JavaScript cannot represent precisely.
Example:
```
// Original BigInt ID: 312590077454712834
// After JSON.parse: 312590077454712830 (corrupted!)
```
## Solution
Cast BigInt columns to `::text` inside `JSONB_BUILD_OBJECT` calls,
similar to how MONEY is already cast to `::numeric`.
```sql
-- Before
JSONB_BUILD_OBJECT('id', "id")
-- After
JSONB_BUILD_OBJECT('id', "id"::text)
```
This ensures BigInt values are returned as JSON strings, preserving full
precision when parsed in JavaScript.
## Changes
- Added BigInt → `::text` cast in `visit_json_build_obj_expr`
(quaint/src/visitor/postgres.rs)
- Added test case for BigInt casting
## Related
- prisma/prisma#29009 (TypeScript-level workaround in prisma fork)
## Test plan
- [x] Added unit test for BigInt casting in `test_json_build_object`
- [ ] CI tests pass
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
) ## Summary This PR adds a regression test for a critical bug where BigInt values lose precision when using driver adapters (like `@prisma/adapter-pg`) with the `relationJoins` preview feature enabled. Fixes #29010 ### The Problem When `previewFeatures = ["relationJoins"]` is enabled, Prisma uses lateral joins to fetch related data. The related records are returned as JSON-aggregated columns. When parsing this JSON data, standard `JSON.parse()` converts large integers (exceeding `Number.MAX_SAFE_INTEGER`) to JavaScript Numbers, silently corrupting the values. **Example corruption:** - Original value: `312590077454712834` - After JSON.parse: `312590077454712800` (precision lost!) ### The Fix The fix is implemented at the SQL level in `prisma-engines`: - Postgres: cast BigInt to text in `JSONB_BUILD_OBJECT` (prisma/prisma-engines#5745) - MySQL + CockroachDB: cast BigInt/INT8 values to string in JSON builders (prisma/prisma-engines#5752) BigInt columns are cast to string before JSON aggregation, so values arrive as strings and are correctly parsed without precision loss. **Changes in this PR:** - Added regression test in `packages/client/tests/functional/issues/29010-bigint-precision-relation-joins/` - Minor type safety improvement (explicit `as Value` cast) in `data-mapper.ts` ## Reproduction <details> <summary>Schema</summary> ```prisma generator client { provider = "prisma-client-js" previewFeatures = ["driverAdapters", "relationJoins"] } datasource db { provider = "postgresql" url = env("DATABASE_URL") } model User { id BigInt @id @default(autoincrement()) files UserFile[] } model UserFile { id String @id @default(uuid()) userId BigInt user User @relation(fields: [userId], references: [id]) } ``` </details> <details> <summary>Test Code</summary> ```typescript // Create test data const user = await prisma.user.create({ data: {} }) const file = await prisma.userFile.create({ data: { userId: user.id } }) // Fetch with include (triggers relationJoins) const result = await prisma.userFile.findUnique({ where: { id: file.id }, include: { user: true } }) // Before fix: result.userId !== result.user.id (BigInt corrupted in nested relation) // After fix: result.userId === result.user.id (correct) ``` </details> ## Test Plan - [x] Regression test added for BigInt precision in relationJoins queries - [x] Test covers PostgreSQL, CockroachDB, and MySQL (requires updated Wasm artifacts from prisma-engines#5752) - [x] Tests nested relationJoins queries ## Breaking Changes None. This is a bugfix that preserves intended behavior. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Bug Fixes** * Improved JSON data parsing and type handling in data mapping operations. * **Tests** * Added comprehensive test coverage for BigInt precision with relationJoins across PostgreSQL, CockroachDB, and MySQL database providers. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Oleksii Orlenko <alex@aqrln.net>
## Problem
When using `relationJoins` with BigInt fields in Prisma 7, JavaScript's
`JSON.parse` loses precision for integers larger than
`Number.MAX_SAFE_INTEGER` (2^53 - 1).
This happens because PostgreSQL's `JSONB_BUILD_OBJECT` returns BigInt
values as JSON numbers, which JavaScript cannot represent precisely.
Example:
```
// Original BigInt ID: 312590077454712834
// After JSON.parse: 312590077454712830 (corrupted!)
```
## Solution
Cast BigInt columns to `::text` inside `JSONB_BUILD_OBJECT` calls,
similar to how MONEY is already cast to `::numeric`.
```sql
-- Before
JSONB_BUILD_OBJECT('id', "id")
-- After
JSONB_BUILD_OBJECT('id', "id"::text)
```
This ensures BigInt values are returned as JSON strings, preserving full
precision when parsed in JavaScript.
## Changes
- Added BigInt → `::text` cast in `visit_json_build_obj_expr`
(quaint/src/visitor/postgres.rs)
- Added test case for BigInt casting
## Related
- prisma/prisma#29009 (TypeScript-level workaround in prisma fork)
## Test plan
- [x] Added unit test for BigInt casting in `test_json_build_object`
- [ ] CI tests pass
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Problem
When using
relationJoinswith BigInt fields in Prisma 7, JavaScript'sJSON.parseloses precision for integers larger thanNumber.MAX_SAFE_INTEGER(2^53 - 1).This happens because PostgreSQL's
JSONB_BUILD_OBJECTreturns BigInt values as JSON numbers, which JavaScript cannot represent precisely.Example:
Solution
Cast BigInt columns to
::textinsideJSONB_BUILD_OBJECTcalls, similar to how MONEY is already cast to::numeric.This ensures BigInt values are returned as JSON strings, preserving full precision when parsed in JavaScript.
Changes
::textcast invisit_json_build_obj_expr(quaint/src/visitor/postgres.rs)Related
Test plan
test_json_build_object