TML-2754: point stale migration tests at the post-#751 adapter API#760
Conversation
…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>
📝 WalkthroughWalkthroughThis PR refactors test code to use a ChangesMigration Facade and Planner Routing
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 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/extension-supabase
@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 📦
|
…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>
…n-test-adapter-api
… 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>
…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>
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
9b34bb2b0and0431eee74) pointed plain.test.ts/.cli.test.tsfiles, the SQLite e2e harness, and the example/telemetrydb-setuphelpers at the new API —pnpm typecheckwent 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 intarget-postgresthat brokeinit-journeyintegration tests at runtime, and a snapshot mismatch in the e2eddl.test.ts.The three changes
createPlannertakes the control adapter, not the family instance. The planner now lowers DDL through the adapter, socreatePlanner(familyInstance)becomescreatePlanner(controlAdapter).createRunner(familyInstance)is unchanged. (Test-only, first wave.)executeDbInit/executeDbUpdateoptions now requireadapteralongsidefamilyInstance. 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.)Emit user
migration.tsimports from@prisma-next/postgres/migration, not@prisma-next/sql-relational-core/contract-free.op-factory-call.tswas hardcoding the latter as the module specifier forcol/primaryKey/lit/fn/foreignKey/uniqueemitted into user migration files. User projects depend on@prisma-next/postgres(a runtime dep of every init-scaffolded project), not on the internalsql-relational-corepackage — so the emittedimport … 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 inop-factory-call.tsand 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 byinit-journey.e2e.test.tsCannot find package '@prisma-next/sql-relational-core'.)Snapshot updates
test/e2e/framework/test/ddl.test.ts > creates tables on db initializationis updated to match the adapter renderer's output. Two semantic shifts:"col" type NOT NULL DEFAULT (value). Old:"col" type DEFAULT (value) NOT NULL. Both are valid Postgres; the new order is what the adapter'srenderColumnproduces.DdlColumnDefaultVisitor.literalhas no column-type context, so it can't emit the::jsonb/::jsoncast that the old planner'srenderDefaultLiteraldid 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
pnpm typecheck: green across all 138 packages (was 4 failing onmain).pnpm lint: 79/79.@prisma-next/target-postgrestest: 272/272.@prisma-next/adapter-postgrestest: 567/571 (4 expected-fail).@prisma-next/adapter-sqlitetest: 178/178.@prisma-next/postgrestest: 92/92.@prisma-next/e2e-testsframework test: 105/105 (with the updated snapshot).@prisma-next/integration-teststypecheck: clean.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
::jsonb/::jsoncast on PG jsonb/json column defaults emitted by the adapter renderer.executeDbInit/UpdateSPI change (filed earlier while doing slice 5 work — surfaced by the same kind of typecheck gate).🤖 Generated with Claude Code