Skip to content

test(client): preserve BigInt precision in relationJoins queries#29009

Merged
jacek-prisma merged 18 commits into
prisma:mainfrom
polaz:fix/bigint-precision-relation-joins
Feb 25, 2026
Merged

test(client): preserve BigInt precision in relationJoins queries#29009
jacek-prisma merged 18 commits into
prisma:mainfrom
polaz:fix/bigint-precision-relation-joins

Conversation

@polaz

@polaz polaz commented Jan 8, 2026

Copy link
Copy Markdown
Contributor

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:

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

Schema
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])
}
Test Code
// 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)

Test Plan

  • Regression test added for BigInt precision in relationJoins queries
  • Test covers PostgreSQL, CockroachDB, and MySQL (requires updated Wasm artifacts from prisma-engines#5752)
  • Tests nested relationJoins queries

Breaking Changes

None. This is a bugfix that preserves intended behavior.

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.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Jan 8, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

The changes add type casting for JSON parsing in data mapping and introduce comprehensive functional tests for BigInt precision preservation with the relationJoins preview feature across multiple database providers.

Changes

Cohort / File(s) Summary
Type Casting Fix
packages/client-engine-runtime/src/interpreter/data-mapper.ts
Casts JSON.parse(data) result to Value type in mapArrayOrObject to preserve type information when decoding string values to JSON; error handling and control flow unchanged
BigInt Precision Test Suite
packages/client/tests/functional/issues/29010-bigint-precision-relation-joins/prisma/_schema.ts
Introduces Prisma test schema with BigInt IDs and one-to-many relation between User and Post models with relationJoins preview feature enabled
BigInt Precision Test Definitions
packages/client/tests/functional/issues/29010-bigint-precision-relation-joins/tests.ts
Adds functional tests validating BigInt precision preservation in simple and nested relationJoin queries; tests skip on unsupported databases (MongoDB, SQLite, SQL Server)
Test Matrix Configuration
packages/client/tests/functional/issues/29010-bigint-precision-relation-joins/_matrix.ts
Defines test matrix covering three SQL providers (PostgreSQL, CockroachDB, MySQL) for BigInt precision validation

Suggested labels

lgtm

Suggested reviewers

  • aqrln
  • igalklebanov
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR fully addresses issue #29010 by adding a regression test suite covering BigInt precision in relationJoins across multiple SQL providers and nested scenarios, plus a type-safety cast in data-mapper.ts.
Out of Scope Changes check ✅ Passed All changes are in scope: data-mapper.ts type cast supports BigInt parsing, and new test files directly validate the BigInt precision fix for relationJoins as specified in issue #29010.
Title check ✅ Passed The title accurately summarizes the main change: adding a regression test to ensure BigInt precision is preserved in relationJoins queries, which is the core objective of this PR.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@CLAassistant

CLAassistant commented Jan 8, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

- 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.
@polaz
polaz force-pushed the fix/bigint-precision-relation-joins branch from 220b556 to e71b882 Compare January 10, 2026 03:38
@aqrln aqrln added this to the 7.3.0 milestone Jan 16, 2026
@codspeed-hq

codspeed-hq Bot commented Jan 16, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 17 untouched benchmarks
⏩ 30 skipped benchmarks1


Comparing polaz:fix/bigint-precision-relation-joins (faa78d5) with main (620ac9e)

Open in CodSpeed

Footnotes

  1. 30 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Skip the expensive regex replacement when no 16+ digit integers exist
in the JSON string. This handles the common case where JSON contains
no large integers that would lose precision.

The simple `/\d{16}/.test()` check is O(n) worst case but returns early
for most strings, making it effectively O(1) for typical JSON payloads
without BigInts.
Copilot AI review requested due to automatic review settings January 16, 2026 13:58
@polaz

polaz commented Jan 16, 2026

Copy link
Copy Markdown
Contributor Author

Performance Fix

I've pushed a commit that addresses the 97.68% performance regression flagged by CodSpeed.

Root Cause

The original implementation ran an expensive regex replacement on every JSON parse call, even when no large integers existed in the data.

Solution

Added a fast-path check that skips the regex replacement for the common case:

const HAS_LARGE_INT = /\d{16}/

export function safeJsonParse(json: string): unknown {
  // Fast path: skip expensive regex if no 16+ digit sequences exist
  if (!HAS_LARGE_INT.test(json)) {
    return JSON.parse(json)
  }
  // Slow path: only when large integers might be present
  // ...
}

Why This Works

  • The simple /\d{16}/.test() check returns early for most strings
  • Only JSON containing potential large integers (16+ digits) triggers the replacement
  • The BigInt precision fix still applies when needed (relationJoins with BigInt foreign keys)

All existing tests pass. Waiting for CI to confirm the performance improvement.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a critical bug where BigInt values lose precision when using driver adapters with the relationJoins preview feature. When relation data is fetched via PostgreSQL lateral joins and returned as JSON-aggregated columns, standard JSON.parse() converts large integers exceeding Number.MAX_SAFE_INTEGER to JavaScript Numbers, causing silent data corruption.

Changes:

  • Added safeJsonParse() function that pre-processes JSON strings to quote large integers (16+ digits) before parsing
  • Replaced JSON.parse() calls with safeJsonParse() in data processing modules
  • Added comprehensive test coverage (15 tests) for the new functionality

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/client-engine-runtime/src/utils.ts Implements safeJsonParse() with regex-based detection and conversion of 16+ digit integers to strings
packages/client-engine-runtime/src/utils.test.ts Adds 15 unit tests covering various edge cases for safeJsonParse()
packages/client-engine-runtime/src/interpreter/serialize-sql.ts Replaces JSON.parse() with safeJsonParse() when deserializing JSON column types
packages/client-engine-runtime/src/interpreter/in-memory-processing.ts Replaces JSON.parse() with safeJsonParse() during in-memory record processing
packages/client-engine-runtime/src/interpreter/data-mapper.ts Replaces JSON.parse() with safeJsonParse() when mapping array/object data

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/client-engine-runtime/src/utils.ts Outdated
Comment thread packages/client-engine-runtime/src/utils.test.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/client-engine-runtime/src/utils.ts`:
- Line 146: The current LARGE_INT_PATTERN regex incorrectly matches digits
inside JSON string literals; update LARGE_INT_PATTERN to a two-alternative regex
that first matches string literals and second matches large integers outside
strings (e.g. use the suggested
/"(?:[^"\\]|\\.)*"|(:\s*|[,\[]\s*)(-?\d{16,})(?=\s*[,}\]])/g) and update the
replacement logic where LARGE_INT_PATTERN is used to return the full match
unchanged when capture group 2 is undefined (meaning a string literal was
matched) and only transform when group 2 contains the large integer; reference
LARGE_INT_PATTERN in utils.ts and ensure the replacer checks the second capture
group before modifying the match.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e71b882 and 10c0579.

📒 Files selected for processing (1)
  • packages/client-engine-runtime/src/utils.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for new file names (e.g., query-utils.ts, filter-operators.test.ts)
Avoid creating barrel files (index.ts that re-export from other modules). Import directly from the source file unless the ./index.ts file already exists
Avoid adding useless code comments that do not add new information. Do not write inline comments that describe what the code does; only write comments explaining why the code was written or relevant context/background
Write documentation comments for exported items using proper doc comment syntax

Files:

  • packages/client-engine-runtime/src/utils.ts
🧠 Learnings (1)
📚 Learning: 2026-01-05T16:15:18.374Z
Learnt from: jacek-prisma
Repo: prisma/prisma PR: 28992
File: packages/client/tests/functional/issues/28591-mapped-enums/test.ts:9-18
Timestamp: 2026-01-05T16:15:18.374Z
Learning: Prisma: After the TML-1663 fix, when using enums with map directives, only the mapped (lowercase) input values are accepted. For example, enum SuggestionStatus { PENDING map("pending") } will accept 'pending' but not 'PENDING'. This is a behavioral change from before. Update tests and usage accordingly to validate inputs against mapped values, and ensure any input normalization or validation relies on the mapped string rather than the enum name.

Applied to files:

  • packages/client-engine-runtime/src/utils.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Agent
  • GitHub Check: CodeQL analysis (javascript-typescript)
🔇 Additional comments (1)
packages/client-engine-runtime/src/utils.ts (1)

168-181: Good implementation with effective fast-path optimization.

The fast-path check using a simple /\d{16}/ test before applying the expensive global regex replacement is a smart optimization that avoids overhead for typical JSON payloads without large integers. The documentation clearly explains the problem being solved (precision loss with relationJoins) and the approach taken.

Assuming the regex edge case noted above is addressed or deemed acceptable for the known input sources, the implementation is sound.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread packages/client-engine-runtime/src/utils.ts Outdated
Address review feedback:
- Fix regex to skip string literals by adding capturing group
- Update comment wording for consistency (16 or more digits)
- Use safeJsonParse in test for round-trip consistency
- Add tests for large integers inside string values

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@packages/client-engine-runtime/src/utils.test.ts`:
- Around line 3-105: Add a regression test in
packages/client-engine-runtime/src/utils.test.ts to verify safeJsonParse
correctly handles top-level scalar JSON numbers that are large (i.e., a JSON
string that is just a large integer like "312590077454712834" or negative
variant) and returns the value as a string rather than a corrupted Number;
locate the existing safeJsonParse test suite (describe('safeJsonParse')) and add
a new test case that calls safeJsonParse with a top-level numeric JSON string
and asserts the returned value equals the stringified integer to ensure scalar
handling isn't regressed.

In `@packages/client-engine-runtime/src/utils.ts`:
- Around line 166-183: safeJsonParse currently only rewrites large ints when
they follow `:`, `,`, or `[` (LARGE_INT_PATTERN), so a JSON payload that's just
a top-level large integer still parses as a Number; update safeJsonParse to
detect full-string large-integer payloads and quote them before JSON.parse (or
extend LARGE_INT_PATTERN to also match start/end-of-string numeric-only input).
Concretely, in safeJsonParse check json.trim() against a full-number regex (e.g.
optional sign + 16+ digits) or add anchors to LARGE_INT_PATTERN, and when
matched wrap the entire numeric string in quotes (or transform to a quoted
value) so JSON.parse returns a string and preserves precision; keep existing
logic for other cases using HAS_LARGE_INT and LARGE_INT_PATTERN unchanged.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 10c0579 and 65dbafa.

📒 Files selected for processing (2)
  • packages/client-engine-runtime/src/utils.test.ts
  • packages/client-engine-runtime/src/utils.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for new file names (e.g., query-utils.ts, filter-operators.test.ts)
Avoid creating barrel files (index.ts that re-export from other modules). Import directly from the source file unless the ./index.ts file already exists
Avoid adding useless code comments that do not add new information. Do not write inline comments that describe what the code does; only write comments explaining why the code was written or relevant context/background
Write documentation comments for exported items using proper doc comment syntax

Files:

  • packages/client-engine-runtime/src/utils.test.ts
  • packages/client-engine-runtime/src/utils.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Test files named *.test.ts are excluded from build output via esbuild config; place tests alongside source files

Files:

  • packages/client-engine-runtime/src/utils.test.ts
🧠 Learnings (1)
📚 Learning: 2026-01-05T16:15:18.374Z
Learnt from: jacek-prisma
Repo: prisma/prisma PR: 28992
File: packages/client/tests/functional/issues/28591-mapped-enums/test.ts:9-18
Timestamp: 2026-01-05T16:15:18.374Z
Learning: Prisma: After the TML-1663 fix, when using enums with map directives, only the mapped (lowercase) input values are accepted. For example, enum SuggestionStatus { PENDING map("pending") } will accept 'pending' but not 'PENDING'. This is a behavioral change from before. Update tests and usage accordingly to validate inputs against mapped values, and ensure any input normalization or validation relies on the mapped string rather than the enum name.

Applied to files:

  • packages/client-engine-runtime/src/utils.test.ts
  • packages/client-engine-runtime/src/utils.ts
🧬 Code graph analysis (1)
packages/client-engine-runtime/src/utils.test.ts (1)
packages/client-engine-runtime/src/utils.ts (2)
  • safeJsonParse (166-184)
  • safeJsonStringify (119-128)

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread packages/client-engine-runtime/src/utils.test.ts Outdated
Comment thread packages/client-engine-runtime/src/utils.ts Outdated
Address review feedback:
- Add TOP_LEVEL_LARGE_INT pattern for bare scalar integers
- Return string directly when JSON is just a large integer
- Add regression test for top-level large integers

This handles edge cases like JSON columns storing numeric scalars.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/client-engine-runtime/src/utils.test.ts`:
- Around line 131-134: The test assigns result from
safeJsonParse(safeJsonStringify(obj)) and then indexes into result.user which
causes a TypeScript error because safeJsonParse returns unknown; update the test
to assert the expected shape by casting the round-trip value (the result
variable) to the appropriate type (or to any if a quick fix) so subsequent
property access (result.user.id, result.user.files[0].fileId) type-checks;
locate the usage of safeJsonParse and safeJsonStringify in the test and add the
type assertion to the result declaration.
♻️ Duplicate comments (1)
packages/client-engine-runtime/src/utils.ts (1)

179-183: Handle leading/trailing whitespace in top-level integer check.

JSON.parse tolerates leading/trailing whitespace (e.g., JSON.parse(' 123 ') returns 123), but TOP_LEVEL_LARGE_INT requires an exact match. Input like ' 312590077454712834 ' would bypass the top-level check, skip the slow path's LARGE_INT_PATTERN (which requires a prefix character), and fall through to JSON.parse, losing precision.

Proposed fix
   // Handle top-level large integers (JSON can be a bare scalar value).
   // The main regex only matches integers after :, ,, or [ so this case needs special handling.
-  if (TOP_LEVEL_LARGE_INT.test(json)) {
-    return json
+  const trimmed = json.trim()
+  if (TOP_LEVEL_LARGE_INT.test(trimmed)) {
+    return trimmed
   }
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 65dbafa and 2860041.

📒 Files selected for processing (2)
  • packages/client-engine-runtime/src/utils.test.ts
  • packages/client-engine-runtime/src/utils.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for new file names (e.g., query-utils.ts, filter-operators.test.ts)
Avoid creating barrel files (index.ts that re-export from other modules). Import directly from the source file unless the ./index.ts file already exists
Avoid adding useless code comments that do not add new information. Do not write inline comments that describe what the code does; only write comments explaining why the code was written or relevant context/background
Write documentation comments for exported items using proper doc comment syntax

Files:

  • packages/client-engine-runtime/src/utils.ts
  • packages/client-engine-runtime/src/utils.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Test files named *.test.ts are excluded from build output via esbuild config; place tests alongside source files

Files:

  • packages/client-engine-runtime/src/utils.test.ts
🧠 Learnings (1)
📚 Learning: 2026-01-05T16:15:18.374Z
Learnt from: jacek-prisma
Repo: prisma/prisma PR: 28992
File: packages/client/tests/functional/issues/28591-mapped-enums/test.ts:9-18
Timestamp: 2026-01-05T16:15:18.374Z
Learning: Prisma: After the TML-1663 fix, when using enums with map directives, only the mapped (lowercase) input values are accepted. For example, enum SuggestionStatus { PENDING map("pending") } will accept 'pending' but not 'PENDING'. This is a behavioral change from before. Update tests and usage accordingly to validate inputs against mapped values, and ensure any input normalization or validation relies on the mapped string rather than the enum name.

Applied to files:

  • packages/client-engine-runtime/src/utils.ts
  • packages/client-engine-runtime/src/utils.test.ts
🧬 Code graph analysis (1)
packages/client-engine-runtime/src/utils.test.ts (1)
packages/client-engine-runtime/src/utils.ts (2)
  • safeJsonParse (172-196)
  • safeJsonStringify (119-128)
🔇 Additional comments (3)
packages/client-engine-runtime/src/utils.test.ts (1)

1-111: Comprehensive test coverage for safeJsonParse.

The tests thoroughly cover the key scenarios: regular JSON, arrays, nested objects, top-level large integers (both positive and negative), boundary cases (15-digit safe vs 16-digit conversion), string literal preservation, and escaped quotes. The precision loss demonstration test (lines 97-110) effectively documents the problem being solved.

packages/client-engine-runtime/src/utils.ts (2)

130-156: Well-documented regex patterns with clear capturing group explanations.

The documentation thoroughly explains the pattern structure, capturing groups, and purpose. The two-alternative approach correctly handles string literals to prevent JSON corruption.


185-196: Replacement logic correctly preserves string literals.

The two-alternative regex approach with the stringLiteral !== undefined check ensures that large integers inside JSON string values are preserved unchanged, while numeric values are quoted to prevent precision loss. This addresses the JSON corruption issue identified in previous reviews.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread packages/client-engine-runtime/src/utils.test.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/client-engine-runtime/src/utils.test.ts Outdated
@aqrln

aqrln commented Jan 20, 2026

Copy link
Copy Markdown
Member

Thank you for the pull request.

I'm not sure about this approach to be honest. I would prefer if we added the casts to the SQL queries so that those values are strings in the JSON in the first place.

…ation test

The test case was duplicating coverage already provided by
"converts large integers (18 digits) to strings to preserve precision".
@polaz

polaz commented Jan 20, 2026

Copy link
Copy Markdown
Contributor Author

Thank you for the pull request.

I'm not sure about this approach to be honest. I would prefer if we added the casts to the SQL queries so that those values are strings in the JSON in the first place.

i'm not happy with the approach as well - but had to highlight the problem as encountered in actual app

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/client-engine-runtime/src/utils.test.ts`:
- Around line 29-34: Update the test comment in the 'converts 16+ digit integers
to strings (may exceed MAX_SAFE_INTEGER)' test to accurately state that the
chosen value 9007199254740991 equals Number.MAX_SAFE_INTEGER (not exceeds it)
and that the implementation conservatively converts all 16‑digit or longer
integers to strings; locate the comment in the test surrounding the
safeJsonParse call and replace the wording to reflect that behavior and the
exact relation to MAX_SAFE_INTEGER.

Comment thread packages/client-engine-runtime/src/utils.test.ts Outdated
@polaz

polaz commented Jan 20, 2026

Copy link
Copy Markdown
Contributor Author

Thank you for the pull request.

I'm not sure about this approach to be honest. I would prefer if we added the casts to the SQL queries so that those values are strings in the JSON in the first place.

Please take a look at prisma/prisma-engines#5745, if i got it correctly

@polaz

polaz commented Jan 21, 2026

Copy link
Copy Markdown
Contributor Author

Replying to review threads:

Thread 1 (aqrln - "this looks superfluous"): Removed the redundant test in commit 1cfaff0.

Thread 2 (CodeRabbit - comment accuracy): This test was for safeJsonParse which has been removed. The comment is no longer relevant.

Threads 3-5 (Copilot - missing safeJsonParse): The PR description was outdated. The fix is now implemented at the SQL level in prisma-engines (prisma/prisma-engines#5745), where BigInt values are cast to ::text in JSONB_BUILD_OBJECT before JavaScript parses them.

The safeJsonParse workaround was initially added but then removed because the SQL-level fix handles precision at the database level. The functional tests will pass once the Wasm modules are updated with the prisma-engines fix.

@polaz
polaz requested a review from aqrln January 21, 2026 08:55
Aijeyomah pushed a commit to Aijeyomah/prisma-engines that referenced this pull request Jan 26, 2026
## 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>
Comment thread packages/client-engine-runtime/src/utils.test.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@polaz
polaz requested a review from aqrln January 28, 2026 08:53
@aqrln aqrln modified the milestones: 7.3.0, 7.4.0 Jan 28, 2026
@aqrln aqrln changed the title fix(client): preserve BigInt precision in relationJoins queries test(client): preserve BigInt precision in relationJoins queries Jan 28, 2026

@aqrln aqrln left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks so much!

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Jan 28, 2026
@aqrln

aqrln commented Jan 28, 2026

Copy link
Copy Markdown
Member

@polaz tests now show that the bug still exists in MySQL and, more surprisingly, in CockroachDB.

MySQL needs a similar fix in its implementation of visit_json_build_object method. I'm not fully sure what's going on with CockroachDB, but I assume it's probably because the native type is called INT and not BIGINT there, so the (Some(TypeFamily::Int), Some("BIGINT")) pattern doesn't match it.

Would you be interested in fixing those two as well? If not, then let's exclude them from this test for now and create the corresponding issues.

@polaz

polaz commented Jan 29, 2026

Copy link
Copy Markdown
Contributor Author

I addressed the MySQL/CockroachDB precision issue in prisma-engines: prisma/prisma-engines#5752 (casts BigInt/INT8 to text in JSON builders, plus tests). Once Wasm updates land, the functional tests should pass for those providers as well.

@polaz
polaz requested a review from aqrln January 29, 2026 11:09
Comment thread packages/client-engine-runtime/src/interpreter/data-mapper.ts Outdated
`Value` is `unknown` and `JSON.parse` returns `any`, so the cast adds nothing.
@polaz
polaz requested a review from jacek-prisma February 25, 2026 01:26
@jacek-prisma
jacek-prisma merged commit 28d0981 into prisma:main Feb 25, 2026
242 checks passed
@polaz
polaz deleted the fix/bigint-precision-relation-joins branch February 25, 2026 11:55
dimsssss pushed a commit to dimsssss/prisma-engines that referenced this pull request Feb 26, 2026
## 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-6.x-candidate lgtm This PR has been approved by a maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BigInt precision loss in nested relations with relationJoins preview feature

5 participants