-
Notifications
You must be signed in to change notification settings - Fork 2.4k
test(client): preserve BigInt precision in relationJoins queries #29009
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
jacek-prisma
merged 18 commits into
prisma:main
from
polaz:fix/bigint-precision-relation-joins
Feb 25, 2026
+103
−0
Merged
Changes from 1 commit
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
e71b882
fix(client): preserve BigInt precision in relationJoins queries
polaz 09e2f8f
Merge branch 'main' into fix/bigint-precision-relation-joins
polaz 10c0579
perf(client): add fast-path check to safeJsonParse
polaz 65dbafa
fix(client): prevent regex from matching inside JSON strings
polaz 2860041
fix(client): handle top-level large integer JSON values
polaz 1cfaff0
fix(client): add type assertion in test for TypeScript compatibility
polaz f589730
test(client-engine-runtime): remove redundant precision loss demonstr…
polaz 1793950
Merge branch 'main' into fix/bigint-precision-relation-joins
polaz 021b1df
Merge branch 'main' into fix/bigint-precision-relation-joins
polaz 8459517
fix(client): remove safeJsonParse workaround, add regression test
polaz 1078097
Merge branch 'main' into fix/bigint-precision-relation-joins
polaz 10a62f9
test(client): drop obsolete utils tests and run bigint test broadly
polaz c3503c2
Merge branch 'main' into fix/bigint-precision-relation-joins
polaz 8cbf355
Merge branch 'main' into fix/bigint-precision-relation-joins
polaz 9daf4fd
Merge branch 'main' into fix/bigint-precision-relation-joins
aqrln 7c0601a
Merge branch 'main' into fix/bigint-precision-relation-joins
polaz d824fa7
Merge branch 'main' into fix/bigint-precision-relation-joins
polaz faa78d5
fix: remove superfluous `as Value` type cast
polaz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next
Next commit
fix(client): preserve BigInt precision in relationJoins queries
- Add `safeJsonParse()` function that pre-processes JSON strings to quote large integers (16+ digits) before parsing, preventing precision loss - Update `data-mapper.ts`, `in-memory-processing.ts`, and `serialize-sql.ts` to use `safeJsonParse()` instead of `JSON.parse()` when parsing JSON strings that may contain BigInt values - Add comprehensive test suite for `safeJsonParse()` in `utils.test.ts` When using driver adapters with `relationJoins` preview feature, PostgreSQL returns related data as JSON-aggregated columns via lateral joins. Standard `JSON.parse()` converts integers exceeding `Number.MAX_SAFE_INTEGER` to JavaScript Number, silently corrupting the values. For example, `312590077454712834` becomes `312590077454712800`. This fix ensures BigInt foreign key values in nested relations are preserved by converting large integers to strings during JSON parsing. The existing data mapper then converts these strings to the correct BigInt type.
- Loading branch information
commit e71b882845f49028598e65f10a2124aca53d1936
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 2 additions & 2 deletions
4
packages/client-engine-runtime/src/interpreter/in-memory-processing.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| import { safeJsonParse, safeJsonStringify } from './utils' | ||
|
|
||
| describe('safeJsonParse', () => { | ||
| test('parses regular JSON normally', () => { | ||
| expect(safeJsonParse('{"name":"Alice","age":30}')).toEqual({ name: 'Alice', age: 30 }) | ||
| }) | ||
|
|
||
| test('parses arrays', () => { | ||
| expect(safeJsonParse('[1,2,3]')).toEqual([1, 2, 3]) | ||
| }) | ||
|
|
||
| test('parses nested objects', () => { | ||
| expect(safeJsonParse('{"user":{"id":123,"name":"Bob"}}')).toEqual({ | ||
| user: { id: 123, name: 'Bob' }, | ||
| }) | ||
| }) | ||
|
|
||
| test('preserves 15-digit integers as numbers (safe)', () => { | ||
| // 15-digit integers are always safe | ||
| expect(safeJsonParse('{"id":123456789012345}')).toEqual({ id: 123456789012345 }) | ||
| }) | ||
|
|
||
| test('converts 16+ digit integers to strings (may exceed MAX_SAFE_INTEGER)', () => { | ||
| // 16-digit integers may exceed MAX_SAFE_INTEGER, so convert to string | ||
| // This includes MAX_SAFE_INTEGER (9007199254740991) which has 16 digits | ||
| const result = safeJsonParse('{"id":9007199254740991}') | ||
| expect(result).toEqual({ id: '9007199254740991' }) | ||
| }) | ||
|
polaz marked this conversation as resolved.
Outdated
|
||
|
|
||
| test('converts large integers (18 digits) to strings to preserve precision', () => { | ||
| // 312590077454712834 is a typical BigInt ID that exceeds MAX_SAFE_INTEGER | ||
| // Without safeJsonParse, JSON.parse would lose precision | ||
| const result = safeJsonParse('{"userId":312590077454712834}') | ||
| expect(result).toEqual({ userId: '312590077454712834' }) | ||
| // The string value preserves exact precision | ||
| expect((result as { userId: string }).userId).toBe('312590077454712834') | ||
| }) | ||
|
|
||
| test('handles multiple large integers in the same object', () => { | ||
| const result = safeJsonParse('{"userId":312590077454712834,"fileId":912590077454712834}') | ||
| expect(result).toEqual({ | ||
| userId: '312590077454712834', | ||
| fileId: '912590077454712834', | ||
| }) | ||
| }) | ||
|
|
||
| test('handles large integers in arrays', () => { | ||
| const result = safeJsonParse('[312590077454712834,412590077454712834]') | ||
| expect(result).toEqual(['312590077454712834', '412590077454712834']) | ||
| }) | ||
|
|
||
| test('handles negative large integers', () => { | ||
| const result = safeJsonParse('{"id":-312590077454712834}') | ||
| expect(result).toEqual({ id: '-312590077454712834' }) | ||
| }) | ||
|
|
||
| test('handles large integers in nested structures', () => { | ||
| const result = safeJsonParse('{"user":{"id":312590077454712834,"files":[{"fileId":412590077454712834}]}}') | ||
| expect(result).toEqual({ | ||
| user: { | ||
| id: '312590077454712834', | ||
| files: [{ fileId: '412590077454712834' }], | ||
| }, | ||
| }) | ||
| }) | ||
|
|
||
| test('handles mixed arrays with small and large integers', () => { | ||
| const result = safeJsonParse('[1, 312590077454712834, 3]') | ||
| expect(result).toEqual([1, '312590077454712834', 3]) | ||
| }) | ||
|
|
||
| test('demonstrates the precision loss problem it solves', () => { | ||
| const originalId = '312590077454712834' | ||
| const json = `{"userId":${originalId}}` | ||
|
|
||
| // Standard JSON.parse would lose precision | ||
| const unsafeParsed = JSON.parse(json) | ||
| expect(unsafeParsed.userId).not.toBe(312590077454712834n) // Not BigInt, it's a Number | ||
| // The exact corruption value varies by JS engine, just verify it's corrupted | ||
| expect(unsafeParsed.userId.toString()).not.toBe('312590077454712834') | ||
|
|
||
| // safeJsonParse preserves the value as string | ||
| const safeParsed = safeJsonParse(json) | ||
| expect((safeParsed as { userId: string }).userId).toBe('312590077454712834') // Exact value preserved | ||
| }) | ||
| }) | ||
|
polaz marked this conversation as resolved.
Outdated
|
||
|
|
||
| describe('safeJsonStringify', () => { | ||
| test('serializes BigInt values as strings', () => { | ||
| const obj = { id: BigInt('312590077454712834') } | ||
| expect(safeJsonStringify(obj)).toBe('{"id":"312590077454712834"}') | ||
| }) | ||
|
|
||
| test('serializes Uint8Array as base64', () => { | ||
| const obj = { data: new Uint8Array([1, 2, 3, 4]) } | ||
| expect(safeJsonStringify(obj)).toBe('{"data":"AQIDBA=="}') | ||
| }) | ||
|
|
||
| test('handles nested BigInt values', () => { | ||
| const obj = { | ||
| user: { | ||
| id: BigInt('312590077454712834'), | ||
| files: [{ fileId: BigInt('412590077454712834') }], | ||
| }, | ||
| } | ||
| const result = JSON.parse(safeJsonStringify(obj)) | ||
|
polaz marked this conversation as resolved.
Outdated
|
||
| expect(result.user.id).toBe('312590077454712834') | ||
| expect(result.user.files[0].fileId).toBe('412590077454712834') | ||
| }) | ||
| }) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.