TML-2605: namespace-qualify runtime SQL via per-family default namespace#670
TML-2605: namespace-qualify runtime SQL via per-family default namespace#670wmadden-electric wants to merge 20 commits into
Conversation
Signed-off-by: Will Madden <madden@prisma.io>
…lane Centralise POSTGRES_DEFAULT_DOMAIN_NAMESPACE_ID and per-target helpers, add resolveDomainModel with default-namespace-first ordering, and retarget resolveSingleDomainNamespaceId to infer public then unbound when multiple namespaces are declared. Signed-off-by: Will Madden <madden@prisma.io>
Export POSTGRES_DEFAULT_STORAGE_NAMESPACE_ID, defaultStorageNamespaceIdForSqlTarget, and resolveStorageTable with default-namespace-first ordering. Re-export from family-sql/runtime and add Mongo-family default namespace identifiers. Signed-off-by: Will Madden <madden@prisma.io>
Thread resolved storage namespace id from resolveStorageTable (default-first) through the flat db.sql table proxy into TableSource, and preserve it across select/insert/update/delete plan construction. Signed-off-by: Will Madden <madden@prisma.io>
Postgres and SQLite renderers call namespace concretion qualifyTable() using the namespaceId stamped on TableSource (no render-time bare-name resolution). SqliteContractSerializer hydrates table-bearing unbound namespaces as SqliteDatabase so runtime contracts expose qualifyTable. Signed-off-by: Will Madden <madden@prisma.io>
After sql-contract dist changes, rolldown-plugin-dts could not name the inferred return type across the package boundary (TS2742). Use an explicit ReturnType<typeof buildSqlNamespace> so sql-runtime builds cleanly. Signed-off-by: Will Madden <madden@prisma.io>
… nodes Route model and table resolution through D1 default-namespace-first APIs (resolveDomainModel, resolveStorageTable) and build every ORM TableSource via tableSourceForContract so adapter renderers qualify SQL like the DSL path. Signed-off-by: Will Madden <madden@prisma.io>
Reuse sql-builder cross-namespace table types for UnboundTables so Postgres public contracts type-check on root.from and the flat ref surface; add vitest type tests against the sql-builder postgres fixture. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Signed-off-by: Will Madden <madden@prisma.io>
Regenerate showcase migration end-contract.d.ts with explicit namespaced domain emission (__unbound__ slot matches JSON). Restore telemetry-backend migration end-contract artefacts accidentally dropped from the branch. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Signed-off-by: Will Madden <madden@prisma.io>
Remove contractModels, contractValueObjects, resolveSingleDomainNamespaceId, ContractModelsMap, and ContractValueObjectsMap from foundation contract. Runtime consumers use domainModelsAtDefaultNamespace and default-namespace resolvers; the emitter asserts a single domain namespace and throws on multi-namespace contracts. Regenerated contract.d.ts fixtures use infer-based Models export aligned with the framework emitter template. Signed-off-by: Will Madden <madden@prisma.io>
…ering (D5b) Declare qualifyTable on the family SqlNamespace type and implement it on SqlBoundNamespace and SqlUnboundNamespace so adapters call namespace.qualifyTable without bare casts. Renderers guard when a namespace entry is not materialised. Fix adapter-postgres qualification tests to use package imports and the renderLoweredSql codecLookup argument. Signed-off-by: Will Madden <madden@prisma.io>
…pace SQL (D7) Postgres integration paths deserialize contracts with PostgresContractSerializer and re-hydrate clones instead of structuredClone so qualifyTable survives. CLI journey migrations hydrate end-contract.json before sql-builder lowering. Add PGlite e2e proof for auth + public namespace-qualified SELECT. Reconcile emitted contract.d.ts fixtures via canonical CLI emit. Signed-off-by: Will Madden <madden@prisma.io>
…ck (D7) Signed-off-by: Will Madden <madden@prisma.io>
Record the default-namespace family façade convention, retire transitional contract projection helpers in upgrade guidance, and document Postgres namespace-qualified runtime SQL for users and extension authors. Signed-off-by: Will Madden <madden@prisma.io>
ADRs are durable docs and must not link to transient project artefacts under projects/; the slice-spec path would dangle at project close-out. The ADR body is self-contained, so the reference is removed rather than re-pointed. Signed-off-by: Will Madden <madden@prisma.io>
…ade snippet The hydration example used a static deserialize/serialize API from the /contract subpath; the real serializer is instance-based (serializeContract/deserializeContract) on /runtime. Extension authors copy this snippet verbatim, so the wrong API would mislead. Signed-off-by: Will Madden <madden@prisma.io>
…ace test PDoD7 is a transient project done-condition id; source/test comments and describe names must not reference transient project-planning artefacts. Describe the property the test pins instead. Signed-off-by: Will Madden <madden@prisma.io>
|
Important Review skippedToo many files! This PR contains 156 files, which is 6 over the limit of 150. To get a review, narrow the scope: ⚙️ Run configurationConfiguration used: Path: .coderabbit.yml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (12)
📒 Files selected for processing (156)
You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
@prisma-next/extension-author-tools
@prisma-next/mongo-runtime
@prisma-next/family-mongo
@prisma-next/sql-runtime
@prisma-next/family-sql
@prisma-next/extension-arktype-json
@prisma-next/middleware-cache
@prisma-next/mongo
@prisma-next/extension-paradedb
@prisma-next/extension-pgvector
@prisma-next/extension-postgis
@prisma-next/postgres
@prisma-next/sql-orm-client
@prisma-next/sqlite
@prisma-next/target-mongo
@prisma-next/adapter-mongo
@prisma-next/driver-mongo
@prisma-next/contract
@prisma-next/utils
@prisma-next/config
@prisma-next/errors
@prisma-next/framework-components
@prisma-next/operations
@prisma-next/ts-render
@prisma-next/contract-authoring
@prisma-next/ids
@prisma-next/psl-parser
@prisma-next/psl-printer
@prisma-next/cli
@prisma-next/cli-telemetry
@prisma-next/emitter
@prisma-next/migration-tools
prisma-next
@prisma-next/vite-plugin-contract-emit
@prisma-next/mongo-codec
@prisma-next/mongo-contract
@prisma-next/mongo-value
@prisma-next/mongo-contract-psl
@prisma-next/mongo-contract-ts
@prisma-next/mongo-emitter
@prisma-next/mongo-schema-ir
@prisma-next/mongo-query-ast
@prisma-next/mongo-orm
@prisma-next/mongo-query-builder
@prisma-next/mongo-lowering
@prisma-next/mongo-wire
@prisma-next/sql-contract
@prisma-next/sql-errors
@prisma-next/sql-operations
@prisma-next/sql-schema-ir
@prisma-next/sql-contract-psl
@prisma-next/sql-contract-ts
@prisma-next/sql-contract-emitter
@prisma-next/sql-lane-query-builder
@prisma-next/sql-relational-core
@prisma-next/sql-builder
@prisma-next/target-postgres
@prisma-next/target-sqlite
@prisma-next/adapter-postgres
@prisma-next/adapter-sqlite
@prisma-next/driver-postgres
@prisma-next/driver-sqlite
commit: |
size-limit report 📦
|
Refactor assertSingleDomainNamespaceForEmission to destructure namespace keys so every branch is reachable and unit-tested. Add direct tests for zero, one, and many namespaces; extend emitter tests for missing namespace payloads and value object emission. Drop an unreachable resolveFieldTypeParams guard. Also fix sql-contract lint: prefix unused tableNamed parameter with underscore. Signed-off-by: Will Madden <madden@prisma.io>
Refresh committed contract.d.ts fixtures after a full build so emit matches the current CLI: add StorageBase to contract type imports and export Models above Contract. Signed-off-by: Will Madden <madden@prisma.io>
Pass the iterated ContractModel into FieldTypeParamsResolver so generate-contract-dts does not re-index modelsRecord under noUncheckedIndexedAccess. Extend TestContractOverrides with valueObjects and cast the missing-namespace negative test contract via unknown. Signed-off-by: Will Madden <madden@prisma.io>
wmadden
left a comment
There was a problem hiding this comment.
Unacceptable. You moved target-specific logic into the framework domain
| domainModelsAtDefaultNamespace( | ||
| contract.domain, | ||
| defaultDomainNamespaceIdForSqlTarget(contract.target), | ||
| ) as Models, |
There was a problem hiding this comment.
This isn't good enough. This example app exists to prove that walking the Contract type is ergonomic. This cast, and the domainModelsAtDefaultNamespace() and defaultDomainNamespaceIdForSqlTarget() helpers fail the test
| readonly profileHash: ProfileHash; | ||
| }; | ||
|
|
||
| export type Models = Contract extends ContractType<StorageBase, infer TModels> ? TModels : never; |
There was a problem hiding this comment.
Don't love this, for the same reason. Why are we generating types which need to be interpolated?
| defaultDomainNamespaceIdForMongo, | ||
| defaultDomainNamespaceIdForSqlTarget, | ||
| inferDefaultDomainNamespaceId, | ||
| POSTGRES_DEFAULT_DOMAIN_NAMESPACE_ID, |
There was a problem hiding this comment.
WOW YOU'RE KIDDING? A postgres constant in the framework domain?
There was a problem hiding this comment.
Completely unacceptable. SQL and Postgres constants in the framework domain
Linked issue
Refs TML-2605. Final slice of the Target-Extensible IR + Namespaces project — the runtime query path was the last consumer still namespace-blind after the IR became fully namespaced. Builds on the
symmetric-domain-planeslice (which deliberately left transitional projection helpers for this slice to retire). The explicit namespace-aware surface (db.sql.auth.user,db.auth.User) is the follow-up, TML-2550.At a glance
Application code stays flat — no query-code changes for existing single-namespace consumers:
What changed is the SQL that comes out the other side. The contract IR has been fully namespaced for a while (
storage.namespaces.public.tables.user), but the runtime still rendered the bare name. It now renders the qualified identifier its coordinate already implies:SQLite renders unqualified (
"user", single-namespace no-op); Mongo addresses the collection in the right namespace's database.Summary
Make the runtime query path namespace-aware: a bare query name resolves through a per-family default namespace and renders through its namespace's qualification, so emitted SQL matches the schema the IR already describes. This is the migration that takes the runtime from namespace-blind to namespace-aware, and it retires the throw-on-multi-namespace projection helpers the previous slice left as a bridge.
The decision
Four coupled pieces, all facets of "a bare query name now resolves and renders through its namespace" — see ADR 223:
TableSource.namespaceId), set where resolution already happens, and the family adapter renders qualification via the namespace concretion'squalifyTable(). It is never re-derived by bare name at render time.publicfor Postgres,__unbound__for SQLite/Mongo). Authoring already centralised this rule; the runtime now has an importable equivalent, and every flat-name lookup resolves default-namespace-first instead of scanning in insertion order.assertSingleDomainNamespaceForEmission) because per-namespacecontract.d.tsis co-designed with the explicit surface in TML-2550. The two intentionally diverge until then.contractModels/contractValueObjects/resolveSingleDomainNamespaceIdand theContractModelsMap/ContractValueObjectsMaptype maps are removed from the foundationcontractpackage, replaced by default-namespace access helpers — see ADR 221 for the IR shape these sit on.How it fits together
@prisma-next/contract, re-exported through the family runtime facades.relational-core'sTableSourcegains an optionalnamespaceId; thesql-builderproxy stamps it at construction.qualifyTable; SQLite does the same through its no-op. Column refs stay alias-qualified (unchanged).sql-orm-clientquery plans resolve models/tables default-first and stamp the same coordinate, so DSL and ORM render identically.query-builder's flat table types are brought to parity withsql-builder's cross-namespace + default-namespace resolution (a Postgrespubliccontract no longer types its tables asnever).qualifyTable. The transitional helpers are removed; runtime consumers use default-first access helpers, the emitter uses an explicit single-namespace assertion, andqualifyTablebecomes a typed member of the SQL namespace concretions.Behavior changes & evidence
db.sql.user/db.Userrender"public"."user"for DML —packages/3-targets/6-adapters/postgres/src/core/sql-renderer.ts. Evidence:sql-renderer-namespace-qualification.test.tsand the end-to-endmulti-namespace-runtime.test.ts(PGlite, assertsFROM "auth"."user"/FROM "public"."note"through build → lower → execute).packages/2-sql/4-lanes/sql-builder/src/runtime/resolve-table.tsandpackages/3-extensions/sql-orm-client/src/storage-resolution.ts. Evidence:cross-namespace-tables.types.test-d.ts.packages/1-framework/0-foundation/contract/src/domain-envelope.ts→domain-namespace-access.ts; the emitter's fail-loud guard moves toassert-single-domain-namespace-for-emission.ts. Evidence: the emitter guardrail test stays green.Notes for the reviewer
2482d2db7(helper retirement + runtime/emitter split) ande8a054684/98bcba9a6(renderer + AST coordinate). The renderer/AST pair is the load-bearing change.structuredClone(which strips thequalifyTablemethod off a hydrated contract) onto aPostgresContractSerializerJSON round-trip. The root cause of the post-refactor integration reds was clone-induced method loss, not missing qualification logic.test:integrationpasses 1015/1015 but exits 1 on PGlite teardown noise;test:packagesfails only@prisma-next/cli-telemetrywithprisma-next: command not found(the slice touches zerocli-telemetryfiles; the local worktree lacked the.bin/prisma-nextsymlink and runs Node 22 against anengines.node >= 24);fixtures:checkneeds the CLI on PATH locally —contract.*fixtures were confirmed clean via canonical emit.Compatibility / migration / risk
Breaking for extension authors and SPI consumers: the removed
contracthelpers and the newqualifyTableexpectation on SQL namespace concretions require code changes. Upgrade instructions are recorded for both the user surface (skills/upgrade/.../0.11-to-0.12/instructions.md) and the extension-author surface (skills/extension-author/.../0.11-to-0.12/instructions.md), with an old→new mapping table. Single-namespace Postgres application code needs no changes.Testing performed
pnpm typecheck— pass (135/135).pnpm test:integration— 1015/1015 tests pass (process exit 1 on PGlite teardown noise, ruled pre-existing).pnpm test:e2e— pass (105 tests, incl. the new multi-namespace proof).pnpm lint:deps— pass.pnpm fixtures:check—contract.*confirmed clean via canonical CLI emit (root script needs CLI on PATH locally).pnpm test:packages— 695/699 files pass; only the pre-existing@prisma-next/cli-telemetrycommand not foundfailure remains (environmental, not from this slice).Skill update
Upgrade-instruction skills updated (user + extension-author 0.11→0.12 transitions) for the removed helpers, the
qualifyTableexpectation, and namespace-qualified SQL. No CLI/config surface changed.Follow-ups
db.sql.<ns>.<table>,db.<ns>.<Model>), cross-namespace collision ergonomics, and per-namespacecontract.d.tsemission. Builds directly on the AST coordinate this slice introduces.Alternatives considered
contract.storagedefault-first). Smaller diff, but it re-derives what the proxy already knew and would diverge from the proxy's choice for colliding names —db.sql.auth.userwould render"public"."user". TML-2550 would have to rip it out. Carrying the coordinate on the AST is the extension seam, not a throwaway.Checklist
git commit -s) per the DCO.TML-NNNN: <sentence-case title>form.