Bug description
PostgreSQL 18 represents column NOT NULL constraints as first-class pg_constraint rows with contype = 'n' (PG 18 docs). The schema engine's constraint introspection query (schema-engine/sql-schema-describer/src/postgres/constraints_query.sql) filters with a denylist:
AND contype NOT IN ('p', 'u', 'f')
so on PostgreSQL 18 the new 'n' rows pass through to get_constraints, where row.get_expect_char(\"constraint_type\") panics:
panicked at schema-engine/sql-schema-describer/src/postgres.rs:1193:39:
called `Result::unwrap()` on an `Err` value: "Getting constraint_type from ResultRow ResultRow { columns: [\"namespace\", \"table_name\", \"constraint_name\", \"constraint_type\", \"constraint_definition\", \"is_deferrable\", \"is_deferred\"], ... values: [... Text(Some(\"Widget_color_not_null\")) ..., Text(Some(\"n\")) ..., Text(Some(\"NOT NULL color\")) ...] } as char failed"
Observed through the WASM schema engine (@prisma/schema-engine-wasm) with a driver adapter, where contype arrives as a text value; with no panic handler registered the schemaPush promise never settles, so callers hang indefinitely. Any PostgreSQL 18 database whose schema has at least one NOT NULL column (i.e. effectively all of them) triggers this as soon as introspection runs against a non-empty schema — db push diffing, db pull, migrate dev/deploy. On PG ≤ 17 the query returns no rows for a typical Prisma-managed schema (no check/exclusion constraints), which is why this path went unnoticed.
Note: PostgreSQL 18 is listed as supported in the supported databases matrix.
Severity
⚠️ Major: Breaks core functionality (e.g., migrations fail)
Reproduction
Discovered via in-process PGlite 0.5.x (PostgreSQL 18.3) behind @prisma/adapter-pg, no Docker needed:
-
schemaPush a schema containing a model with a NOT NULL column (any non-optional field), e.g.:
model Widget {
id Int @id @default(autoincrement())
name String @unique
color Color @default(RED)
}
-
Run a second schemaPush (or any introspection) against the now non-empty schema.
-
The constraints query returns rows like (\"public\", \"Widget\", \"Widget_color_not_null\", \"n\", \"NOT NULL color\", f, f) and the engine panics as above.
The same applies to a plain PostgreSQL 18 server via driver adapters; step-by-step: create any table with a NOT NULL column on PG 18, then run introspection through the WASM engine + driver adapter. (Whether the native-engine value path converts the \"char\" column type without panicking was not verified — if it does, the rows still reach the match and are discarded by the _ => () arm, which is the correct outcome.)
Expected vs. Actual Behavior
Expected: introspection on PostgreSQL 18 ignores contype = 'n' rows — column nullability is already read from pg_attribute.attnotnull, and NOT-NULL-as-constraint is PG 18 catalog bookkeeping, not schema information Prisma models.
Actual: the engine panics (as char failed), and via the WASM engine the returned promise never settles (indefinite hang).
Suggested fix
Add 'n' to the denylist in constraints_query.sql:
AND contype NOT IN ('p', 'u', 'f', 'n')
or invert to an allowlist (contype IN ('c', 'x')), which is robust against future contype additions (e.g. 't' trigger constraints are also currently unfiltered).
Prisma information
prisma / @prisma/client: 7.8.0
@prisma/schema-engine-wasm: 7.8.0-6.3c6e192761c0362d496ed980de936e2f3cebcd3a (latest at time of filing; engine hash 3c6e192761c0362d496ed980de936e2f3cebcd3a)
- Driver adapter:
@prisma/adapter-pg 7.8.0
Environment & setup
- OS: macOS 15 (also reproduces on Linux)
- Database: PostgreSQL 18.3 (via PGlite 0.5.x; applies to any PostgreSQL 18)
- Node.js: 24.x
Bug description
PostgreSQL 18 represents column
NOT NULLconstraints as first-classpg_constraintrows withcontype = 'n'(PG 18 docs). The schema engine's constraint introspection query (schema-engine/sql-schema-describer/src/postgres/constraints_query.sql) filters with a denylist:so on PostgreSQL 18 the new
'n'rows pass through toget_constraints, whererow.get_expect_char(\"constraint_type\")panics:Observed through the WASM schema engine (
@prisma/schema-engine-wasm) with a driver adapter, wherecontypearrives as a text value; with no panic handler registered theschemaPushpromise never settles, so callers hang indefinitely. Any PostgreSQL 18 database whose schema has at least oneNOT NULLcolumn (i.e. effectively all of them) triggers this as soon as introspection runs against a non-empty schema —db pushdiffing,db pull,migrate dev/deploy. On PG ≤ 17 the query returns no rows for a typical Prisma-managed schema (no check/exclusion constraints), which is why this path went unnoticed.Note: PostgreSQL 18 is listed as supported in the supported databases matrix.
Severity
Reproduction
Discovered via in-process PGlite 0.5.x (PostgreSQL 18.3) behind
@prisma/adapter-pg, no Docker needed:schemaPusha schema containing a model with aNOT NULLcolumn (any non-optional field), e.g.:Run a second
schemaPush(or any introspection) against the now non-empty schema.The constraints query returns rows like
(\"public\", \"Widget\", \"Widget_color_not_null\", \"n\", \"NOT NULL color\", f, f)and the engine panics as above.The same applies to a plain PostgreSQL 18 server via driver adapters; step-by-step: create any table with a
NOT NULLcolumn on PG 18, then run introspection through the WASM engine + driver adapter. (Whether the native-engine value path converts the\"char\"column type without panicking was not verified — if it does, the rows still reach thematchand are discarded by the_ => ()arm, which is the correct outcome.)Expected vs. Actual Behavior
Expected: introspection on PostgreSQL 18 ignores
contype = 'n'rows — column nullability is already read frompg_attribute.attnotnull, and NOT-NULL-as-constraint is PG 18 catalog bookkeeping, not schema information Prisma models.Actual: the engine panics (
as char failed), and via the WASM engine the returned promise never settles (indefinite hang).Suggested fix
Add
'n'to the denylist inconstraints_query.sql:or invert to an allowlist (
contype IN ('c', 'x')), which is robust against future contype additions (e.g.'t'trigger constraints are also currently unfiltered).Prisma information
prisma/@prisma/client: 7.8.0@prisma/schema-engine-wasm: 7.8.0-6.3c6e192761c0362d496ed980de936e2f3cebcd3a (latest at time of filing; engine hash3c6e192761c0362d496ed980de936e2f3cebcd3a)@prisma/adapter-pg7.8.0Environment & setup