Skip to content

CHECK (FALSE) NO INHERIT constraints silently dropped during plan/apply #386

@taichi

Description

@taichi

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions