fix(target-postgres): brand Timestamp(tz)<P> on Date#429
fix(target-postgres): brand Timestamp(tz)<P> on Date#429
Conversation
|
Warning Rate limit exceeded
To continue reviewing without waiting, purchase usage credits in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe PR updates PostgreSQL codec types to brand ChangesType Definitions and Validation
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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/mongo-runtime
@prisma-next/family-mongo
@prisma-next/sql-runtime
@prisma-next/family-sql
@prisma-next/extension-arktype-json
@prisma-next/middleware-telemetry
@prisma-next/mongo
@prisma-next/extension-paradedb
@prisma-next/extension-pgvector
@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/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: |
The pg/timestamp@1 and pg/timestamptz@1 codecs are declared Codec<…, Date, Date, …> and pass the wire value through unchanged, but the emitter-facing Timestamp<P> / Timestamptz<P> aliases were branded on string. For columns with a precision specifier the emitter wrote those branded-string aliases into contract.d.ts, so consumers calling Date methods on a projected timestamp(P) / timestamptz(P) field tripped a TS error even though the runtime value was a Date. Re-base both aliases on a new BrandedDate helper so the type matches the codec declaration. Other parameterized aliases (Char/Varchar/Numeric /Bit/VarBit/Time/Timetz/Interval) correctly stay branded on string — their codecs decode to string. Add a .test-d.ts that pins both directions plus the alias-vs-CodecInput agreement, so future drift between the codec generic and the emitter alias gets caught at typecheck time. Closes TML-2391
6780555 to
f33c9dd
Compare
closes TML-2391
Intent
Resolve a self-contradiction inside
@prisma-next/target-postgres: the package'spg/timestamp@1/pg/timestamptz@1codecs declareCodec<…, Date, Date, …>and decode toDate, but the package's emitter-facing aliasesTimestamp<P>/Timestamptz<P>were branded onstring. For columns declared with a precision (timestamp(3)etc.), the contract emitter wrote those branded-string aliases intocontract.d.ts, so consumers calling.toISOString()orinstanceof Dateon a projected column tripped a TS error even though the runtime value was aDate. The fix re-bases the two branded aliases onDateso the type matches the codec's declared input/output.Before / After (packages/3-targets/3-targets/postgres/src/exports/codec-types.ts)
The other parameterized aliases (
Char<N>,Varchar<N>,Numeric<P,S>,Bit<N>,VarBit<N>,Time<P>,Timetz<P>,Interval<P>) stay branded onstring— their codecs decode tostring, so the brand base was already correct.Change map
BrandedDatehelper; switchedTimestamp/TimestamptzfromBrandedStringtoBrandedDate.Timestamp(tz)<P>extendsDate, (2) does not extendstring, (3) the codec's declaredCodecInput<…>isDateand the alias is assignable to it, (4) the other parameterized aliases (Char,Varchar,Numeric,Time) remain string-shaped.The story
pgTimestampCodecandpgTimestamptzCodecinpackages/3-targets/3-targets/postgres/src/core/codecs.tsare explicitly parametrized asCodec<…, Date, Date, …>withdecode: (wire) => wire— i.e. the package's own statement is "input isDate, output isDate."exports/codec-types.ts,Timestamp<P>andTimestamptz<P>were branded onstring. For a column with a precision specifier, the emitter callscodec.renderOutputType({ precision }), gets the literal name'Timestamp<3>', and writes that alias intocontract.d.ts— so the consumer's projected field was typedstring & { __timestampPrecision: 3 }while the runtime value was aDate.Date. No emitter logic, no codec, no contract serialization, no descriptor metadata changes — the contradiction was localized to two type aliases in one file..test-d.tsregression locks the relationship in place, both the positive direction (Timestamp(tz)<P> extends Date) and the negative one (not extends string), plus a check that the alias matchesCodecInput<…>of the codec, so future drift between the codec generic and the emitter alias gets caught at typecheck time.Behavior changes & evidence
Branded aliases for parameterized timestamps now match the codec's declared output type. Reading a
timestamp(P)/timestamptz(P)column through the runtime returns a value that TypeScript sees asDate & { brand }..toISOString(),instanceof Date, and the rest of theDatesurface typecheck without casts. The previousas unknown as Dateworkaround consumers used (e.g.pdp-control-plane's management-api handlers) is no longer required.Date, by node-postgres default). The package's own codec generic confirmed that contract. The branded-string alias was the only place advertising a different shape.BrandedDateand re-pointedTimestamp/Timestamptzat it.Timestamp<P> brand is Date-shaped,Timestamptz<P> brand is Date-shaped,Timestamp/Timestamptz brand agrees with codec input/output type.Other parameterized aliases unchanged.
Char<N>,Varchar<N>,Numeric<P,S>,Bit<N>,VarBit<N>,Time<P>,Timetz<P>,Interval<P>correctly stay branded onstring— their codecs decode tostring. A regression test pins this so a future "consistency" pass doesn't accidentally Date-brand them too.string-shaped parameterized aliases are unchanged.Compatibility / migration / risk
test/e2e/framework/test/fixtures/generated/contract.d.ts:86(readonly createdAt: Timestamptz<3> | null;), and no test exercisesDatemethods on it. All other generatedcontract.d.tsfiles importTimestamp/Timestamptzbut use the unparameterizedCodecTypes['pg/timestamptz@1']['output'](=Date) accessor at field sites — those imports are dead. Zero references to the brand keys__timestampPrecision/__timestamptzPrecisionexist outside the type definition itself.pnpm typecheck(workspace-wide, packages),pnpm test:packages,pnpm test:integration,pnpm test:e2e, and thetarget-postgrespnpm lintall pass on the branch.sql/timestamp@1(packages/2-sql/4-lanes/relational-core/src/ast/sql-codecs.ts) has the sameCodec<…, Date, Date, …>shape and renders the same'Timestamp<P>'name. Under the postgres adapter it imports the same alias from@prisma-next/target-postgres/codec-types, so it inherits the fix without any change.Follow-ups / open questions
mongo/date@1is declared directly as{ input: Date; output: Date }with no parameterized branded alias intermediary). The FL-03 entry inprojects/mongo-example-apps/framework-limitations.mdreferences a non-existentmongo/dateTime@1codec and looks stale — separate cleanup, not part of this PR.Timestampbrand from a separate codec-types module would need the analogous fix in that module. None exist today.Non-goals / intentionally out of scope
renderOutputTypefor these codecs (Option B in the ticket). Option A is the smaller, more conservative fix; Option B was considered and rejected for losing the precision-in-hover affordance.Summary by CodeRabbit
Release Notes
New Features
Tests