Summary
CHECK constraints that reference no columns (e.g., CHECK (FALSE) NO INHERIT)
are silently dropped during schema introspection.
They never appear in the generated plan and are absent from the applied schema.
Reproduction
-- schema.sql
CREATE TABLE parent_base (
id UUID PRIMARY KEY,
name TEXT NOT NULL,
CONSTRAINT no_direct_insert CHECK (FALSE) NO INHERIT
);
CREATE TABLE child (
CONSTRAINT child_pkey PRIMARY KEY (id)
) INHERITS (parent_base);
pgschema plan --schema public --file schema.sql --output-human stdout
Expected: plan includes no_direct_insert constraint on parent_base.
Actual: no_direct_insert is absent from the plan output.
After apply, the constraint does not exist in the database:
SELECT conname, pg_get_constraintdef(oid, true)
FROM pg_constraint
WHERE conrelid = 'parent_base'::regclass AND contype = 'c';
-- no_direct_insert is missing
Background
CHECK (FALSE) NO INHERIT is a standard PostgreSQL pattern to prevent
direct INSERTs into a parent table while allowing child tables
(via INHERITS) to accept inserts normally.
The NO INHERIT modifier ensures the always-false check
is not inherited by children.
Root cause
In ir/inspector.go, the constraint introspection loop skips
any constraint where the joined column name is empty:
// ir/inspector.go:455-457
if columnName == "" || columnName == "<nil>" {
continue // Skip constraints without columns
}
The introspection query joins pg_constraint with pg_attribute
via a.attnum = ANY(c.conkey).
For CHECK (FALSE), conkey is an empty array ({}),
so the LEFT JOIN produces a row with NULL for attname.
The guard above then skips the entire constraint.
This means any CHECK constraint that does not reference a column
is silently excluded from the IR
and never appears in plans or applied schemas.
Additional note: NO INHERIT modifier not preserved
Even if the column-less constraint were included in the IR,
there is a secondary issue:
- The
Constraint struct has no field for connoinherit / NO INHERIT.
- The introspection query does not read
connoinherit from pg_constraint.
normalizeCheckClause does not handle the NO INHERIT suffix
returned by pg_get_constraintdef.
As a result, NO INHERIT information would be lost
even if the constraint itself were retained.
Environment
- pgschema v1.7.4
- PostgreSQL 18.1
Summary
CHECK constraints that reference no columns (e.g.,
CHECK (FALSE) NO INHERIT)are silently dropped during schema introspection.
They never appear in the generated plan and are absent from the applied schema.
Reproduction
Expected: plan includes
no_direct_insertconstraint onparent_base.Actual:
no_direct_insertis absent from the plan output.After apply, the constraint does not exist in the database:
Background
CHECK (FALSE) NO INHERITis a standard PostgreSQL pattern to preventdirect INSERTs into a parent table while allowing child tables
(via
INHERITS) to accept inserts normally.The
NO INHERITmodifier ensures the always-false checkis not inherited by children.
Root cause
In
ir/inspector.go, the constraint introspection loop skipsany constraint where the joined column name is empty:
The introspection query joins
pg_constraintwithpg_attributevia
a.attnum = ANY(c.conkey).For
CHECK (FALSE),conkeyis an empty array ({}),so the LEFT JOIN produces a row with
NULLforattname.The guard above then skips the entire constraint.
This means any CHECK constraint that does not reference a column
is silently excluded from the IR
and never appears in plans or applied schemas.
Additional note:
NO INHERITmodifier not preservedEven if the column-less constraint were included in the IR,
there is a secondary issue:
Constraintstruct has no field forconnoinherit/NO INHERIT.connoinheritfrompg_constraint.normalizeCheckClausedoes not handle theNO INHERITsuffixreturned by
pg_get_constraintdef.As a result,
NO INHERITinformation would be losteven if the constraint itself were retained.
Environment