fix(core): a bare read fetches scalars, not relations (#848) - #853
Conversation
A context.db read with no `include` (and no fragment `query`) used to auto-include every readable relationship, recursing 5 levels deep and evaluating every related list's query access along the way — the root cause behind #566, #830, and #844. It now returns the row's own columns plus its virtual fields only, matching Prisma's own semantics, for findUnique, findMany, and a singleton's get() alike (get() also gains caller-include support). Relations are fetched only when a caller names them, at which point the existing #566/#830 merge-with-access-control path is unchanged. See docs/adr/0024-a-read-with-no-include-fetches-scalars-not-relations.md. Closes #848 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PifFTifspW7ud4ptUGK4Mx
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: ab288f3 The changes in this PR will be included in the next version bump. This PR includes changesets to release 9 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Self-reviewOverviewImplements #848 / ADR-0024: a Correctness
Test coverage
Risks / things I considered and ruled out
Verification run
No further changes identified as needed. Generated by Claude Code |
Coverage Report for Core Package Coverage (./packages/core)
File CoverageNo changed files found. |
Coverage Report for UI Package Coverage (./packages/ui)
File CoverageNo changed files found. |
Coverage Report for CLI Package Coverage (./packages/cli)
File Coverage
|
||||||||||||||||||||||||||||||||||||||
Coverage Report for Auth Package Coverage (./packages/auth)
File CoverageNo changed files found. |
Coverage Report for Storage Package Coverage (./packages/storage)
File CoverageNo changed files found. |
Coverage Report for RAG Package Coverage (./packages/rag)
File CoverageNo changed files found. |
Coverage Report for Storage S3 Package Coverage (./packages/storage-s3)
File CoverageNo changed files found. |
Coverage Report for Storage Vercel Package Coverage (./packages/storage-vercel)
File CoverageNo changed files found. |
Summary
context.dbread with noinclude(and no fragmentquery) used to auto-include every readable relationship of the list, recursing up toREAD_INCLUDE_MAX_DEPTH(5) and evaluating every related list's operation-levelqueryaccess along the way — Prisma's own semantics for the same call are "scalars only." This is identified as the shared root cause behind Explicit include bypasses relation-level access filtering on reads — caller include REPLACES the access-controlled include #566, Access control fails OPEN past include depth 5: caller includes pass through unscoped and nested rows skip field-read filtering #830, and 0.33.0: reads insideresolveOutputrecurse without bound — server OOMs on any access-scoped read #844.findUnique,findMany, and a singleton's.get()alike — under sudo and under a session, uniformly..get()also gains caller-includesupport it never had.includeor a fragmentquery— themergeIncludeWithAccessControl/buildIncludeWithAccessControlmachinery (and the Explicit include bypasses relation-level access filtering on reads — caller include REPLACES the access-controlled include #566/Access control fails OPEN past include depth 5: caller includes pass through unscoped and nested rows skip field-read filtering #830 fixes it carries) is untouched; it's simply no longer invoked unprompted.docs/adr/0024-a-read-with-no-include-fetches-scalars-not-relations.md, docs: record that a bare read fetches scalars, not relations (#848) #849, already merged).Implementation
packages/core/src/context/index.ts:createFindUnique,createFindMany, andcreateGet(singleton) only callbuildIncludeWithAccessControlwhen the caller supplied aninclude; a bare read sendsinclude: undefinedstraight through.createGetgains the same fragment/include/sudo handling the other two reads already had.packages/cli/src/generator/types.ts: generates a<List>GetArgstype for singleton lists and typesget()to accept it (include/query/select), mirroringFindUniqueArgs.docs/content/concepts/{queries,access-control}.md,docs/content/reference/context-api.md,packages/core/CLAUDE.md) updated to describe the new default; one example (examples/blog/test-context-nested-virtual.ts) fixed to pass an explicitincludeinstead of relying on the old auto-include.includebehavior) is unmodified — only bare-read call sites in tests were updated. The#844cycle-guard reproduction now demonstrates the cycle no longer occurs on the reporter's bare hook-issued read (with the mock's return value made to actually respect theincludeargument, so it faithfully models Prisma); the genuine two-list cycle regression is untouched and still throws.packages/core/tests/bare-read-scalars.test.ts(bare vs. explicit-include behavior, including "no relatedqueryaccess is evaluated on a bare read") and a new describe block inpackages/core/tests/singleton.test.tsfor.get()'s newincludesupport.Test plan
pnpm testinpackages/core— 923/923 passingpnpm testinpackages/cli— 323/323 passingpnpm buildfor@opensaas/stack-coreand@opensaas/stack-cli— cleanpnpm lint— clean (pre-existing unrelated warnings only)pnpm manypkg fix— no changespnpm format— cleanexamples/blogtypes and typechecked the fixed example script against them.changeset/silent-relations-detour.md, minor) leading with the silent nature of the break, both detection strategies, and the migrationCloses #848
🤖 Generated with Claude Code
https://claude.ai/code/session_01PifFTifspW7ud4ptUGK4Mx
Generated by Claude Code