Skip to content

fix(infer/emit): drop dangling cross-schema relations + emit enum input literals (#783, #788; verify #784/#785)#797

Open
wmadden-electric wants to merge 3 commits into
mainfrom
tml-infer-emit-roundtrip
Open

fix(infer/emit): drop dangling cross-schema relations + emit enum input literals (#783, #788; verify #784/#785)#797
wmadden-electric wants to merge 3 commits into
mainfrom
tml-infer-emit-roundtrip

Conversation

@wmadden-electric

@wmadden-electric wmadden-electric commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Batches the rest of the Supabase brownfield infer → emit cluster (A + B + C). Two are live bugs fixed here; two were v0.12 reports already resolved on v0.13 and are pinned/verified.

#783 — relation fields to un-inferred tables (fixed)

infer emitted a relation navigation field for every FK, even when the referenced table wasn't inferred — e.g. a Supabase publicauth.users FK with the auth schema excluded. That produced users users @relation(...) with no model users, so contract emit failed on the unresolved target. inferRelations now skips FKs whose referencedTable is absent from the inferred tables, keeping the scalar FK column and dropping only the dangling navigation field + back-relation. (Design choice: omit the nav field, don't fabricate a stub model.)

#788 — enum input types widened to string (fixed)

FieldOutputTypes for an enum resolved to the literal member union, but FieldInputTypes fell back to CodecTypes['pg/enum@1']['input'] (≈string), so create/update weren't exhaustiveness-checked. Added a symmetric input renderer: optional CodecDescriptor.renderInputType + optional CodecLookup.renderInputTypeFor (existing lookups unchanged), wired through the control stack and resolveFieldType. PgEnumDescriptor.renderInputType returns the same literal union as its output renderer. Only enum opts in; every other codec falls back unchanged.

#785 — boolean defaults not lowered (already fixed on v0.12 → v0.13)

Does not reproduce on v0.13: the interpreter lowers @default(true/false) to { kind: 'literal', value: true|false }, infer emits the PSL attribute, and the Postgres planner renders boolean literal defaults. Added a regression test pinning the interpreter behavior.

#784types{} alias typeParams break migration plan (already fixed on v0.12 → v0.13)

Does not reproduce on v0.13. The named-type subsystem now lowers aliases correctly: Slug = String @db.VarChar(191) keeps typeParams: { length: 191 } on the storage.types entry and the field references it via typeRef (the planner resolves params from there); AccountId = String @db.Uuid lowers to nativeType: 'uuid' with no params (uuid has none), which is what the planner needs. This is asserted by the existing interpreter.types.test.ts and the committed pgvector-named-type parity golden, and the named-type → schema-IR plan path is covered by the pgvector planner tests.

Tests / checks

Failing-first tests for #783 and #788; regression test for #785. Full suites pass: family-sql (368), sql-contract-psl (229), framework-components (396), emitter (171), sql-emitter (101), target-postgres (279). Lint, typecheck, and lint:deps clean.

Independent of #791 (the enum storage-coordinate refactor); branched off main.

Closes #783
Closes #784
Closes #785
Closes #788

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added support for boolean literal defaults in schema declarations.
    • Enhanced type system to generate custom input type expressions for improved type safety in generated contracts.
  • Bug Fixes

    • Improved schema relation inference to correctly handle foreign keys referencing tables not present in the schema.

wmadden and others added 3 commits June 10, 2026 08:25
…rred tables

`contract infer` emitted a relation navigation field for every foreign key,
even when the referenced table was not part of the inferred schema — e.g. a
Supabase `public` → `auth.users` FK with the `auth` schema excluded. That
produced `users users @relation(...)` with no `model users`, so `contract emit`
failed on the unresolved relation target.

Skip relation inference for any FK whose `referencedTable` is absent from the
inferred tables. The scalar FK column is preserved (it comes from the column
walk); only the dangling navigation field and its back-relation are omitted.

Closes #783

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Will Madden <madden@prisma.io>
…for #785)

#785 reported boolean `@default(true/false)` from infer not lowering into
contract.json on v0.12. It no longer reproduces on v0.13: the interpreter
lowers boolean literals to `{ kind: 'literal', value: true|false }` on the
storage contract, the infer side emits the PSL attribute, and the Postgres
planner renders boolean literal defaults. This adds a regression test pinning
the interpreter behavior so it can't silently regress.

Closes #785

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Will Madden <madden@prisma.io>
… output

`FieldOutputTypes` for an enum column resolved to the precise literal member
union (via the codec's `renderOutputType`), but `FieldInputTypes` always fell
back to `CodecTypes['pg/enum@1']['input']` (effectively `string`). Create/update
call sites therefore weren't exhaustiveness-checked against enum members.

Add a symmetric input renderer:
- `CodecDescriptor.renderInputType?(params)` (optional) and a matching
  `CodecLookup.renderInputTypeFor?(id, params)` (optional, so existing lookups
  need no change). The control stack builds the input-renderer map alongside the
  output one.
- `domain-type-generation`'s `resolveFieldType` renders the input position via
  `renderInputTypeFor` when present, mirroring the output path; absent renderers
  keep the `CodecTypes[...]['input']` fallback.
- `PgEnumDescriptor.renderInputType` returns the same literal union as its
  output renderer, since enum writes accept the same members as reads.

Only enum opts in; every other codec falls back unchanged.

Closes #788

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Will Madden <madden@prisma.io>
@wmadden-electric wmadden-electric requested a review from a team as a code owner June 10, 2026 06:39
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: ec623b9b-97e2-4b34-9fa0-37cc11750277

📥 Commits

Reviewing files that changed from the base of the PR and between 91d2d45 and 44b367b.

📒 Files selected for processing (10)
  • packages/1-framework/1-core/framework-components/src/control/control-stack.ts
  • packages/1-framework/1-core/framework-components/src/shared/codec-descriptor.ts
  • packages/1-framework/1-core/framework-components/src/shared/codec-types.ts
  • packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts
  • packages/1-framework/3-tooling/emitter/test/domain-type-generation.test.ts
  • packages/2-sql/2-authoring/contract-psl/test/interpreter.defaults.test.ts
  • packages/2-sql/9-family/src/core/psl-contract-infer/relation-inference.ts
  • packages/2-sql/9-family/test/psl-contract-infer/relation-inference.test.ts
  • packages/2-sql/9-family/test/psl-contract-infer/sql-schema-ir-to-psl-ast.test.ts
  • packages/3-targets/3-targets/postgres/src/core/codecs.ts

📝 Walkthrough

Walkthrough

This PR extends the codec system to support custom input type rendering for TypeScript type generation, integrates that capability into domain type emission, implements it for PostgreSQL enum codecs, and includes fixes for relation inference with missing tables and boolean literal defaults in PSL contracts.

Changes

Input type rendering and PSL contract fixes

Layer / File(s) Summary
Codec input type rendering contracts
packages/1-framework/1-core/framework-components/src/shared/codec-descriptor.ts, packages/1-framework/1-core/framework-components/src/shared/codec-types.ts
CodecDescriptor and CodecDescriptorImpl gain optional renderInputType properties to define custom input type expressions; CodecLookup interface adds optional renderInputTypeFor(id, params) method to retrieve codec-keyed input type renderers.
Codec lookup input renderer collection
packages/1-framework/1-core/framework-components/src/control/control-stack.ts
extractCodecLookup collects per-codec renderInputType functions into an inputRenderersById map and exposes them via the renderInputTypeFor method on the returned CodecLookup object.
Domain type generation with input type resolution
packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts, packages/1-framework/3-tooling/emitter/test/domain-type-generation.test.ts
resolveFieldType now optionally calls codecLookup.renderInputTypeFor?.() to resolve custom scalar input types, validates the result, and falls back to the codec's default input type when no custom renderer is provided; two new test cases verify custom input rendering and fallback behavior.
PostgreSQL enum input type rendering
packages/3-targets/3-targets/postgres/src/core/codecs.ts
PgEnumDescriptor overrides renderInputType to return the same literal member union as renderOutputType, ensuring enum input types match output types and improve exhaustiveness checking.
Relation inference for missing referenced tables
packages/2-sql/9-family/src/core/psl-contract-infer/relation-inference.ts, packages/2-sql/9-family/test/psl-contract-infer/relation-inference.test.ts, packages/2-sql/9-family/test/psl-contract-infer/sql-schema-ir-to-psl-ast.test.ts
inferRelations skips emitting relation navigation fields when a foreign key references a table not in the inferred tables set, preventing dangling relations; test cases verify correct omission of relation fields while preserving scalar foreign-key columns.
Boolean literal defaults in PSL contracts
packages/2-sql/2-authoring/contract-psl/test/interpreter.defaults.test.ts
New test case verifies boolean literal defaults (@default(true) / @default(false)) are correctly lowered into the SQL storage contract as kind: 'literal' with the appropriate boolean value.

🎯 3 (Moderate) | ⏱️ ~25 minutes

🐰 With contracts in place and codecs aligned,
Input types now render, input paths refined.
Relations stay tidy when targets go missing,
And booleans default with defaults not slipping!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main changes: fixing dangling cross-schema relations and emitting enum input literals, with specific issue references.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tml-infer-emit-roundtrip

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.

@pkg-pr-new

pkg-pr-new Bot commented Jun 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

@prisma-next/extension-author-tools

npm i https://pkg.pr.new/@prisma-next/extension-author-tools@797

@prisma-next/mongo-runtime

npm i https://pkg.pr.new/@prisma-next/mongo-runtime@797

@prisma-next/family-mongo

npm i https://pkg.pr.new/@prisma-next/family-mongo@797

@prisma-next/sql-runtime

npm i https://pkg.pr.new/@prisma-next/sql-runtime@797

@prisma-next/family-sql

npm i https://pkg.pr.new/@prisma-next/family-sql@797

@prisma-next/extension-arktype-json

npm i https://pkg.pr.new/@prisma-next/extension-arktype-json@797

@prisma-next/middleware-cache

npm i https://pkg.pr.new/@prisma-next/middleware-cache@797

@prisma-next/mongo

npm i https://pkg.pr.new/@prisma-next/mongo@797

@prisma-next/extension-paradedb

npm i https://pkg.pr.new/@prisma-next/extension-paradedb@797

@prisma-next/extension-pgvector

npm i https://pkg.pr.new/@prisma-next/extension-pgvector@797

@prisma-next/extension-postgis

npm i https://pkg.pr.new/@prisma-next/extension-postgis@797

@prisma-next/postgres

npm i https://pkg.pr.new/@prisma-next/postgres@797

@prisma-next/sql-orm-client

npm i https://pkg.pr.new/@prisma-next/sql-orm-client@797

@prisma-next/sqlite

npm i https://pkg.pr.new/@prisma-next/sqlite@797

@prisma-next/extension-supabase

npm i https://pkg.pr.new/@prisma-next/extension-supabase@797

@prisma-next/target-mongo

npm i https://pkg.pr.new/@prisma-next/target-mongo@797

@prisma-next/adapter-mongo

npm i https://pkg.pr.new/@prisma-next/adapter-mongo@797

@prisma-next/driver-mongo

npm i https://pkg.pr.new/@prisma-next/driver-mongo@797

@prisma-next/contract

npm i https://pkg.pr.new/@prisma-next/contract@797

@prisma-next/utils

npm i https://pkg.pr.new/@prisma-next/utils@797

@prisma-next/config

npm i https://pkg.pr.new/@prisma-next/config@797

@prisma-next/errors

npm i https://pkg.pr.new/@prisma-next/errors@797

@prisma-next/framework-components

npm i https://pkg.pr.new/@prisma-next/framework-components@797

@prisma-next/operations

npm i https://pkg.pr.new/@prisma-next/operations@797

@prisma-next/ts-render

npm i https://pkg.pr.new/@prisma-next/ts-render@797

@prisma-next/contract-authoring

npm i https://pkg.pr.new/@prisma-next/contract-authoring@797

@prisma-next/ids

npm i https://pkg.pr.new/@prisma-next/ids@797

@prisma-next/psl-parser

npm i https://pkg.pr.new/@prisma-next/psl-parser@797

@prisma-next/psl-printer

npm i https://pkg.pr.new/@prisma-next/psl-printer@797

@prisma-next/cli

npm i https://pkg.pr.new/@prisma-next/cli@797

@prisma-next/cli-telemetry

npm i https://pkg.pr.new/@prisma-next/cli-telemetry@797

@prisma-next/emitter

npm i https://pkg.pr.new/@prisma-next/emitter@797

@prisma-next/migration-tools

npm i https://pkg.pr.new/@prisma-next/migration-tools@797

prisma-next

npm i https://pkg.pr.new/prisma-next@797

@prisma-next/vite-plugin-contract-emit

npm i https://pkg.pr.new/@prisma-next/vite-plugin-contract-emit@797

@prisma-next/mongo-codec

npm i https://pkg.pr.new/@prisma-next/mongo-codec@797

@prisma-next/mongo-contract

npm i https://pkg.pr.new/@prisma-next/mongo-contract@797

@prisma-next/mongo-value

npm i https://pkg.pr.new/@prisma-next/mongo-value@797

@prisma-next/mongo-contract-psl

npm i https://pkg.pr.new/@prisma-next/mongo-contract-psl@797

@prisma-next/mongo-contract-ts

npm i https://pkg.pr.new/@prisma-next/mongo-contract-ts@797

@prisma-next/mongo-emitter

npm i https://pkg.pr.new/@prisma-next/mongo-emitter@797

@prisma-next/mongo-schema-ir

npm i https://pkg.pr.new/@prisma-next/mongo-schema-ir@797

@prisma-next/mongo-query-ast

npm i https://pkg.pr.new/@prisma-next/mongo-query-ast@797

@prisma-next/mongo-orm

npm i https://pkg.pr.new/@prisma-next/mongo-orm@797

@prisma-next/mongo-query-builder

npm i https://pkg.pr.new/@prisma-next/mongo-query-builder@797

@prisma-next/mongo-lowering

npm i https://pkg.pr.new/@prisma-next/mongo-lowering@797

@prisma-next/mongo-wire

npm i https://pkg.pr.new/@prisma-next/mongo-wire@797

@prisma-next/sql-contract

npm i https://pkg.pr.new/@prisma-next/sql-contract@797

@prisma-next/sql-errors

npm i https://pkg.pr.new/@prisma-next/sql-errors@797

@prisma-next/sql-operations

npm i https://pkg.pr.new/@prisma-next/sql-operations@797

@prisma-next/sql-schema-ir

npm i https://pkg.pr.new/@prisma-next/sql-schema-ir@797

@prisma-next/sql-contract-psl

npm i https://pkg.pr.new/@prisma-next/sql-contract-psl@797

@prisma-next/sql-contract-ts

npm i https://pkg.pr.new/@prisma-next/sql-contract-ts@797

@prisma-next/sql-contract-emitter

npm i https://pkg.pr.new/@prisma-next/sql-contract-emitter@797

@prisma-next/sql-lane-query-builder

npm i https://pkg.pr.new/@prisma-next/sql-lane-query-builder@797

@prisma-next/sql-relational-core

npm i https://pkg.pr.new/@prisma-next/sql-relational-core@797

@prisma-next/sql-builder

npm i https://pkg.pr.new/@prisma-next/sql-builder@797

@prisma-next/target-postgres

npm i https://pkg.pr.new/@prisma-next/target-postgres@797

@prisma-next/target-sqlite

npm i https://pkg.pr.new/@prisma-next/target-sqlite@797

@prisma-next/adapter-postgres

npm i https://pkg.pr.new/@prisma-next/adapter-postgres@797

@prisma-next/adapter-sqlite

npm i https://pkg.pr.new/@prisma-next/adapter-sqlite@797

@prisma-next/driver-postgres

npm i https://pkg.pr.new/@prisma-next/driver-postgres@797

@prisma-next/driver-sqlite

npm i https://pkg.pr.new/@prisma-next/driver-sqlite@797

commit: 44b367b

@github-actions

Copy link
Copy Markdown

size-limit report 📦

Path Size
postgres / no-emit 150.58 KB (+0.05% 🔺)
postgres / emit 119.35 KB (+0.02% 🔺)
mongo / no-emit 76.67 KB (0%)
mongo / emit 70.96 KB (0%)
cf-worker / no-emit 179.99 KB (+0.02% 🔺)
cf-worker / emit 145.52 KB (+0.03% 🔺)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment