Skip to content

TML-2754: point stale migration tests at the post-#751 adapter API#760

Merged
wmadden merged 4 commits into
mainfrom
tml-2754-fix-migration-test-adapter-api
Jun 8, 2026
Merged

TML-2754: point stale migration tests at the post-#751 adapter API#760
wmadden merged 4 commits into
mainfrom
tml-2754-fix-migration-test-adapter-api

Conversation

@wmadden-electric

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

Copy link
Copy Markdown
Contributor

What

PR #751 reworked migration APIs and updated the integration-test variants of the affected tests — but left several adjacent surfaces on the old API. That turned multiple CI checks red on main. This PR fixes them.

The first wave of fixes (commits 9b34bb2b0 and 0431eee74) pointed plain .test.ts / .cli.test.ts files, the SQLite e2e harness, and the example/telemetry db-setup helpers at the new API — pnpm typecheck went red across four packages and is now green.

The second wave (commit 03ff89fb8) addresses two more failures that surfaced once CI ran with the typecheck fixes in place: an emitted-import bug in target-postgres that broke init-journey integration tests at runtime, and a snapshot mismatch in the e2e ddl.test.ts.

The three changes

  1. createPlanner takes the control adapter, not the family instance. The planner now lowers DDL through the adapter, so createPlanner(familyInstance) becomes createPlanner(controlAdapter). createRunner(familyInstance) is unchanged. (Test-only, first wave.)

  2. executeDbInit / executeDbUpdate options now require adapter alongside familyInstance. The adapter is built from the same control stack as the family instance (<target>AdapterDescriptor.create(controlStack)), so the two stay consistent. (Test-only, first wave.)

  3. Emit user migration.ts imports from @prisma-next/postgres/migration, not @prisma-next/sql-relational-core/contract-free. op-factory-call.ts was hardcoding the latter as the module specifier for col / primaryKey / lit / fn / foreignKey / unique emitted into user migration files. User projects depend on @prisma-next/postgres (a runtime dep of every init-scaffolded project), not on the internal sql-relational-core package — so the emitted import … from '@prisma-next/sql-relational-core/contract-free' failed ESM resolution at runtime even though pnpm had the transitive package on disk. The facade already re-exports those symbols, so the fix is a one-constant change in op-factory-call.ts and an alphabetical update to one assertion test that pins the dedupe-merged import line. One production file changes; three test files update. (Second wave; surfaced by init-journey.e2e.test.ts Cannot find package '@prisma-next/sql-relational-core'.)

Snapshot updates

test/e2e/framework/test/ddl.test.ts > creates tables on db initialization is updated to match the adapter renderer's output. Two semantic shifts:

  • Cosmetic — column-clause ordering. New: "col" type NOT NULL DEFAULT (value). Old: "col" type DEFAULT (value) NOT NULL. Both are valid Postgres; the new order is what the adapter's renderColumn produces.
  • Substrate gap, filed as TML-2861 — the PG adapter renderer's DdlColumnDefaultVisitor.literal has no column-type context, so it can't emit the ::jsonb / ::json cast that the old planner's renderDefaultLiteral did for jsonb/json columns. Tables still work (Postgres applies the implicit text → jsonb cast at default-evaluation time), but the explicit cast is gone from emitted DDL. Tracked; not fixed here.

Why it slipped through

#751 was green on its own branch; these failures appeared only after the merge, in files #751's diff didn't touch — logical (non-textual) conflicts. Git merged cleanly; the types / the snapshot / the runtime ESM resolution did not.

Scope

11 files, +113 / −70. One production file (op-factory-call.ts); ten test / test-support / e2e files; one upgrade-instructions doc.

Verification

  • Full-repo pnpm typecheck: green across all 138 packages (was 4 failing on main).
  • pnpm lint: 79/79.
  • @prisma-next/target-postgres test: 272/272.
  • @prisma-next/adapter-postgres test: 567/571 (4 expected-fail).
  • @prisma-next/adapter-sqlite test: 178/178.
  • @prisma-next/postgres test: 92/92.
  • @prisma-next/e2e-tests framework test: 105/105 (with the updated snapshot).
  • @prisma-next/integration-tests typecheck: clean.
  • The example/telemetry integration tests need a live Postgres; typecheck covers the change there.

The Code Scanning / semgrep CI check is unrelated — the actual semgrep scan completes with 0 findings; the failing exit is in the downstream reviewdog / SARIF-upload step, not in code quality.

Follow-ups filed

🤖 Generated with Claude Code

…r API (TML-2754)

PR #751 reworked two migration APIs but only updated the *.integration.test.ts variants, leaving the plain .test.ts / .cli.test.ts files, the sqlite e2e harness, and the example/telemetry db-setup helpers on the old API. That turned pnpm typecheck red on four packages: adapter-sqlite, e2e-tests, example-multi-extension-monorepo, and telemetry-backend.

Two test-only changes: createPlanner(familyInstance) becomes createPlanner(controlAdapter), since the planner now lowers DDL through the adapter (createRunner(familyInstance) is unchanged); and executeDbInit / executeDbUpdate options now require adapter alongside familyInstance, built from the same control stack as the family instance.

No production code changes. Full-repo typecheck (138/138) and lint (79/79) are green.

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 8, 2026 06:17
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR refactors test code to use a controlAdapter SPI when constructing migration planners and consolidates migration factory imports to use the @prisma-next/postgres/migration public facade. Test fixtures build and export shared control stacks, planners are created via adapter instead of family instance, and DDL snapshots are updated to reflect schema formatting changes.

Changes

Migration Facade and Planner Routing

Layer / File(s) Summary
Postgres migration facade imports
packages/3-targets/3-targets/postgres/src/core/migrations/op-factory-call.ts
PostgresOpFactoryCallNode, CreateTableCall, and DataTransformCall use @prisma-next/postgres/migration as the unified source for user migration factory helpers (col, lit, primaryKey, foreignKey, unique, fn, placeholder).
Test expectations for facade imports
packages/3-targets/6-adapters/postgres/test/migrations/op-factory-call.rendering.test.ts
Import assertions in CreateTableCall and aggregated call-list tests are updated to expect symbols from @prisma-next/postgres/migration instead of internal contract-free modules.
Shared control stack construction
apps/telemetry-backend/test/db-setup.ts, packages/3-targets/6-adapters/sqlite/test/migrations/fixtures/runner-fixtures.ts, test/e2e/framework/test/sqlite/migrations/harness.ts, examples/multi-extension-monorepo/test/e2e.integration.test.ts
Test fixtures and entry points construct reusable controlStack instances and derive both familyInstance and exported controlAdapter from them, replacing inline patterns.
Control adapter wiring in init and planning
apps/telemetry-backend/test/db-setup.ts, examples/multi-extension-monorepo/test/e2e.integration.test.ts, packages/3-targets/6-adapters/sqlite/test/migrations/db-init-update.cli.test.ts
executeDbInit and executeDbUpdate calls now pass adapter: controlAdapter alongside driver, parameterizing extension-pack ordering in multi-extension tests.
SQLite planner creation via adapter
packages/3-targets/6-adapters/sqlite/test/migrations/runner.basic.test.ts, packages/3-targets/6-adapters/sqlite/test/migrations/runner.ledger.test.ts, test/e2e/framework/test/sqlite/migrations/harness.ts
Migration planners are constructed by passing controlAdapter to sqliteTargetDescriptor.createPlanner() instead of familyInstance.
DDL snapshots and upgrade docs
test/e2e/framework/test/ddl.test.ts, skills/upgrade/prisma-next-upgrade/upgrades/0.12-to-0.13/instructions.md
E2E DDL snapshots reflect ordering changes (NOT NULL DEFAULT (now())) and explicit NOT NULL markers for defaulted columns; upgrade documentation notes test-only changes related to control adapter behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • prisma/prisma-next#712: Updates telemetry and migration tests to construct and pass controlAdapter into init/planning flows, aligning with the control adapter SPI routing changes.

Suggested reviewers

  • wmadden

Poem

🐰 Control flows through adapters now,
No longer just through family's bough,
Migrations import from the public facade,
Testing snapshots rearranged, not odd,
The planner's built on firmer sod! ✨

🚥 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
Title check ✅ Passed The PR title accurately reflects the main change: updating test code to use the post-#751 adapter API. It is specific, concise, and directly related to the changeset content.
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.
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
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tml-2754-fix-migration-test-adapter-api

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 8, 2026

Copy link
Copy Markdown

Open in StackBlitz

@prisma-next/extension-author-tools

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

@prisma-next/mongo-runtime

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

@prisma-next/family-mongo

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

@prisma-next/sql-runtime

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

@prisma-next/family-sql

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

@prisma-next/extension-arktype-json

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

@prisma-next/middleware-cache

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

@prisma-next/mongo

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

@prisma-next/extension-paradedb

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

@prisma-next/extension-pgvector

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

@prisma-next/extension-postgis

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

@prisma-next/postgres

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

@prisma-next/sql-orm-client

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

@prisma-next/sqlite

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

@prisma-next/extension-supabase

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

@prisma-next/target-mongo

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

@prisma-next/adapter-mongo

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

@prisma-next/driver-mongo

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

@prisma-next/contract

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

@prisma-next/utils

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

@prisma-next/config

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

@prisma-next/errors

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

@prisma-next/framework-components

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

@prisma-next/operations

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

@prisma-next/ts-render

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

@prisma-next/contract-authoring

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

@prisma-next/ids

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

@prisma-next/psl-parser

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

@prisma-next/psl-printer

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

@prisma-next/cli

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

@prisma-next/cli-telemetry

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

@prisma-next/emitter

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

@prisma-next/migration-tools

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

prisma-next

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

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

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

@prisma-next/mongo-codec

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

@prisma-next/mongo-contract

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

@prisma-next/mongo-value

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

@prisma-next/mongo-contract-psl

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

@prisma-next/mongo-contract-ts

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

@prisma-next/mongo-emitter

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

@prisma-next/mongo-schema-ir

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

@prisma-next/mongo-query-ast

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

@prisma-next/mongo-orm

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

@prisma-next/mongo-query-builder

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

@prisma-next/mongo-lowering

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

@prisma-next/mongo-wire

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

@prisma-next/sql-contract

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

@prisma-next/sql-errors

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

@prisma-next/sql-operations

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

@prisma-next/sql-schema-ir

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

@prisma-next/sql-contract-psl

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

@prisma-next/sql-contract-ts

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

@prisma-next/sql-contract-emitter

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

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

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

@prisma-next/sql-relational-core

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

@prisma-next/sql-builder

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

@prisma-next/target-postgres

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

@prisma-next/target-sqlite

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

@prisma-next/adapter-postgres

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

@prisma-next/adapter-sqlite

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

@prisma-next/driver-postgres

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

@prisma-next/driver-sqlite

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

commit: 03ff89f

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown

size-limit report 📦

Path Size
postgres / no-emit 147.85 KB (0%)
postgres / emit 117.65 KB (0%)
mongo / no-emit 76.67 KB (0%)
mongo / emit 70.96 KB (0%)
cf-worker / no-emit 177.77 KB (0%)
cf-worker / emit 144.31 KB (0%)

wmadden and others added 3 commits June 8, 2026 08:50
…754)

The migration-test adapter-API fix touches examples/multi-extension-monorepo/test/, which trips the upgrade-coverage check. Record it as an incidental, test-only diff (no user-side action) so the check passes.

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

PR #751 left `op-factory-call.ts` hardcoding `'@prisma-next/sql-relational-core/contract-free'`
as the module specifier emitted in user `migration.ts` imports for `col`/`primaryKey`/`lit`/`fn`/
`foreignKey`/`unique`. User projects only depend on `@prisma-next/postgres` (not the internal
`sql-relational-core` package), so the emitted `import { col, ... } from '@prisma-next/sql-
relational-core/contract-free'` fails ESM resolution at runtime in user migrations even though
pnpm has the transitive package on disk. The `postgres/migration` → `target-postgres/migration`
facade already re-exports those symbols from sql-relational-core/contract-free (lines 12-19),
so the fix is to emit imports against the facade.

- Collapse the two import-module constants in `op-factory-call.ts` (`TARGET_MIGRATION_MODULE`
  and `RELATIONAL_CORE_CONTRACT_FREE`) into a single `POSTGRES_MIGRATION_FACADE` =
  `'@prisma-next/postgres/migration'`, with a doc comment explaining why.
- Update the three `op-factory-call.rendering.test.ts` assertions that pinned the old import
  path. The dedupe test now expects `col` merged into the single postgres/migration import line
  rather than a separate sql-relational-core import — a small UX win (one import line, one
  runtime dep, not two).
- Update the `ddl.test.ts` inline snapshot to match the new (correct) column-clause output from
  the adapter renderer: `"col" type NOT NULL DEFAULT (value)` instead of the old planner's
  `"col" type DEFAULT (value) NOT NULL`. Two semantic shifts to note: column-clause ordering is
  cosmetic; the `::jsonb` / `::json` cast on jsonb/json defaults is dropped (Postgres applies
  the implicit text → jsonb cast at default-evaluation time, so the tables still work, but the
  explicit cast is gone from emitted DDL — tracked as TML-2861).

Verification (worktree-local):
- `@prisma-next/target-postgres` test: 272/272.
- `@prisma-next/adapter-postgres` test: 567/571 (4 expected-fail).
- `@prisma-next/postgres` test: 92/92.
- `@prisma-next/e2e-tests` framework test: 105/105 (with the updated snapshot).
- `@prisma-next/integration-tests` typecheck: clean.

The CI semgrep "fail" check is unrelated — the actual semgrep scan completes with 0 findings;
the failure is in the downstream reviewdog/SARIF-upload step, not in code quality.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Will Madden <madden@prisma.io>
@wmadden wmadden enabled auto-merge (squash) June 8, 2026 07:42
@wmadden wmadden merged commit 04d39e5 into main Jun 8, 2026
21 checks passed
@wmadden wmadden deleted the tml-2754-fix-migration-test-adapter-api branch June 8, 2026 07:43
medz pushed a commit to medz/prisma-next that referenced this pull request Jun 10, 2026
…ults (prisma#763)

## At a glance

A Postgres `jsonb` column with a JSON-object default. **Before** this
PR, the migration planner emits:

```sql
CREATE TABLE "user" (
  "metadata" jsonb NOT NULL DEFAULT '{"key":"default"}'
);
```

**After:**

```sql
CREATE TABLE "user" (
  "metadata" jsonb NOT NULL DEFAULT '{"key":"default"}'::jsonb
);
```

The difference is the `::jsonb` cast on the default literal. Same change
applies to `json` columns (emits `::json`); non-JSON column types are
unaffected.

## Why it matters

Without the cast, Postgres infers the literal's type as `text` at parse
time, then applies an implicit `text → jsonb` coercion at
default-evaluation time (every time a row is inserted without a
`metadata` value). Two consequences:

1. **The DDL no longer states its own intent.** Reading the table
definition, you can't tell whether the author meant the default to be
JSON or whether they happened to write a string that looks JSON-ish. The
`::jsonb` cast pins the intent.
2. **Implicit coercion is fragile.** It works today for `jsonb`/`json`
(because Postgres special-cases them), but the same pattern breaks for
types like `xml` or user-defined types where no implicit cast exists.
Emitting the explicit cast is the form that generalises.

This is a one-line shift in emitted DDL, not a behaviour change at the
table level — existing tables continue to work, the implicit coercion
still fires for them. The fix is about the form of newly-emitted
migration files.

## The decision

The renderer that turns the DDL AST into SQL strings runs
per-column-default, dispatched through a visitor. Today the visitor
signature is:

```ts
interface DdlColumnDefaultVisitor<R> {
  literal(node: LiteralColumnDefault): R;
  function(node: FunctionColumnDefault): R;
}
```

`literal` receives the default value but **not the column it belongs
to** — so it can't see that the parent column's type is `jsonb` and
therefore can't decide to emit the cast.

The chosen fix: **widen the visitor with a render-context second
argument** that carries the parent column's native type.

```ts
export interface DdlColumnRenderContext {
  readonly nativeType: string;
}

export interface DdlColumnDefaultVisitor<R> {
  literal(node: LiteralColumnDefault, ctx: DdlColumnRenderContext): R;
  function(node: FunctionColumnDefault, ctx: DdlColumnRenderContext): R;
}
```

`DdlColumnDefault.accept(visitor, ctx)` and both concrete subclasses
(`LiteralColumnDefault`, `FunctionColumnDefault`) forward the context.

The Postgres adapter's `renderColumn` already has `column.type` in scope
at the call site, so passing it through is a one-line change:

```ts
const defaultClause = column.default
  ? column.default.accept(defaultVisitor, { nativeType: column.type })
  : '';
```

The Postgres `defaultVisitor.literal` then keys off `ctx.nativeType` for
the cast decision; the SQLite visitor accepts the context and ignores it
(SQLite has no `jsonb` / `json` types).

## What changed

- **`relational-core/src/ast/ddl-types.ts`** — introduces
`DdlColumnRenderContext`, widens the visitor interface and
`DdlColumnDefault.accept` signature, forwards the context from both
concrete `*ColumnDefault.accept` overrides.
- **Postgres adapter renderer**
(`packages/3-targets/6-adapters/postgres/src/core/ddl-renderer.ts`) —
`defaultVisitor.literal` uses `ctx.nativeType` to decide: `jsonb` →
append `::jsonb`; `json` → append `::json`; everything else stays
cast-free. `renderColumn` passes the context to `accept`.
- **SQLite adapter renderer**
(`packages/3-targets/6-adapters/sqlite/src/core/ddl-renderer.ts`) —
visitor signatures take and ignore the new context; `renderColumn`
passes `{ nativeType: column.type }` for symmetry.
- **E2E `ddl.test.ts` snapshot** — `metadata` and `tags` columns now
include the `::jsonb` cast.

The user-facing `lit(value)` builder is unchanged. The pairing of value
+ column happens at render time, where the column type lives, rather
than upstream where users hand-write `lit(...)` calls.

## Test coverage

Four new regression-pin tests in `ddl-create-table-lowering.test.ts`:

1. `jsonb`-column literal default emits `::jsonb`.
2. `json`-column literal default emits `::json`.
3. Non-JSON column types stay cast-free — covers `text` / `int` / `bool`
/ `null`, **and** an array-shaped value on a `text` column (the renderer
keys off the column's nativeType, not the value shape).
4. `jsonb`-column **function** defaults stay cast-free — a `DEFAULT
(jsonb_build_object(...))` already returns `jsonb`; the cast is only
relevant for string-shaped JSON literals.

One existing test was strengthened: "escapes single quotes in
JSON-object literal defaults" used `toContain` against a substring that
was a prefix of the new output, so it passed silently without the cast.
Now it asserts the full form with cast.

## Verification

| Gate | Result |
|---|---|
| `@prisma-next/sql-relational-core` test | 331 / 331 |
| `@prisma-next/target-postgres` test | 272 / 272 |
| `@prisma-next/adapter-postgres` test | 571 / 575 (4 pre-existing
expected-fail; the 4 new tests count toward 571) |
| `@prisma-next/target-sqlite` test | 111 / 111 |
| `@prisma-next/adapter-sqlite` test | 178 / 178 |
| `@prisma-next/e2e-tests` framework test | 105 / 105 (with the
cast-restored snapshot) |
| `pnpm lint:deps` | clean |
| `pnpm fixtures:check` | clean (no extension-migration drift) |

## Scope

5 files, `+127 / −28`. One framework substrate file (the visitor
interface); two adapter renderers; one new-tests file; one e2e snapshot.

## Alternatives considered

- **Distinct AST node kinds for JSON defaults** (`JsonbColumnDefault`,
`JsonColumnDefault`). The visitor would gain `jsonbLiteral` /
`jsonLiteral` methods. Rejected because the choice of node kind would
have to be made upstream — but the natural choice point is `lit(value)`
in user code, which has no column-type context. Pushing the decision
into the planner's `*Spec → DdlColumn` mapping would work, but at the
cost of fragmenting the literal AST surface for a dispatch the renderer
is the natural home for.
- **Append the cast in `renderColumn` after the visitor returns**, by
inspecting the rendered string. Rejected because the visitor already
encapsulates the "how to render a default" logic; reaching into its
output from outside breaks that encapsulation, and parsing rendered SQL
to decide whether to append a suffix is the kind of thing that bites you
later.
- **Defer / accept the form change.** The implicit coercion does fire
today, so tables still work. Rejected because the cost of the fix is
small, the affected surface (the visitor interface) is internal, and
"the DDL states its own intent" is the kind of small correctness win
that's worth taking when it's cheap. The contextless visitor would also
be a recurring papercut for any future dialect feature that depends on
column-type context (e.g. PostGIS, vector, enum types) — fixing it now
is groundwork as much as a bug fix.

## Linear

Closes TML-2861. Surfaced during PR prisma#760's e2e snapshot review.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* PostgreSQL column defaults now emit explicit type casts (e.g.,
`::jsonb`, `::timestamptz`) for non-text-shaped column types in
generated schemas. Type casts are omitted for text-shaped types and for
numeric/boolean/null literal defaults.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Will Madden <madden@prisma.io>
Co-authored-by: Will Madden <madden@prisma.io>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants