Skip to content

Diagnostics

Every diagnostic carries a code: the one to name in [rules] of pgzest.toml to change its severity, or in a -- pgzest-disable-next-line <code> comment to silence it at one place.

Code Meaning
parse-error SQL that does not parse: the statement is skipped, with the parser’s message.
column-not-found Column reference that resolves to no column in scope (PostgreSQL 42703).
ambiguous-column Column reference that several relations in scope provide (42702): qualify it.
table-not-found Relation that does not exist at this point (42P01).
function-not-found Function or operator with no overload matching the argument types (42883).
ambiguous-function Call that several overloads match equally (42725): cast an argument.
type-mismatch Types PostgreSQL would refuse: a value assigned to a column of another type, CASE/UNION/ARRAY branches without a common type, an impossible cast, a non-boolean condition, a procedure called as a function… (a comparison without a matching operator is a function-not-found, as PostgreSQL reports it).
parameter-not-found $n beyond the parameters of the routine (42P02).
default-not-allowed DEFAULT outside an INSERT … VALUES or an UPDATE … SET.
generated-column Value written to a generated column, or a generated column used in a generation expression.
subquery-columns Subquery returning more columns than its context takes (42601).
values-columns VALUES rows of different lengths.
set-operation-columns Branches of a UNION, INTERSECT or EXCEPT with different numbers of columns.
not-analyzed What the analysis does not cover, stated (information): no conclusion is drawn from it, and nothing is guessed.
Code Meaning
empty-function-body Routine whose body is empty (PL/pgSQL refuses it).
trigger-no-context Trigger function no CREATE TRIGGER of the analyzed files attaches: NEW and OLD have no table to take their type from.
undeclared-variable Variable used without being declared.
raise-arguments RAISE whose % placeholders and arguments do not match in number.
Code Meaning
unparsable-statement Statement the parser could not read: either valid PostgreSQL outside the supported scope, or a syntax error. Level controlled by [parse] unparsable.
unsupported-statement Valid statement deliberately not modeled in the AST (GRANT, …). Level controlled by [parse] unsupported.
unparsed-function-body PL/pgSQL body the parser could not read: the routine is replayed, its body is not analyzed. Level controlled by [parse] unparsable.
Code Meaning
query-header-invalid Malformed -- name: header (name, cardinality, position).
query-without-name SQL placed outside a named query in a query file.
query-empty Header without SQL.
query-multiple-statements More than one statement under the same header.
query-duplicate-name Two queries with the same name in a file.
query-mixed-parameters $name and $1 in the same query.
query-parameter-gap Positional parameters with a gap ($1, $3 without $2).
query-file-extension File in [queries] directory that is not a .sql.
query-parameter-invalid Malformed parameter ($0, $pageId).
query-not-describable Query the describer does not cover (other than SELECT, INSERT, UPDATE, DELETE, CALL).
query-parameter-untyped Parameter whose type no usage determines.
query-cardinality-mismatch Declared cardinality incompatible with the query’s.
Code Meaning
type-not-found Referenced type not found in the catalog (neither built-in, nor from an active extension, nor declared by the project).
object-not-found Altered or dropped object that does not exist at this point of the replay.
object-already-exists Object created while it already exists (without IF NOT EXISTS / OR REPLACE).
schema-not-found Nonexistent schema.
extension-unavailable Extension whose catalog pgzest does not ship for this version.
ambiguous-routine Routine name without a signature that designates several overloads.
dependent-objects Drop refused by PostgreSQL for lack of CASCADE (dependent objects).
ddl-not-replayed DDL in a DO block that the replay could not apply: condition not evaluable on the catalog, dynamic SQL, loop, unanalyzed body. The catalog may then differ from the database’s.
migration-header-invalid Migration header -- pgzest: postgres=… extensions=… malformed, duplicated or misplaced.
migration-header-mismatch Migration header inconsistent with the replayed state: an extension announced but absent or active but not announced, routines generated at another log= level.
migration-without-header Migrations without a -- pgzest: header: analyzed with the configured version (reported once).
schema-drift Declarative schema the migrations do not reach: a migration remains to be generated (pgzest migrate create), or a change cannot be expressed automatically.
query-row-mismatch Query declared to return the rows of another one (-> other) whose columns do not match (name, type, nullability).
view-not-typed View columns whose types the query analysis did not establish.
Code Meaning
macro-error Macro invocation that cannot be expanded: unknown macro or fragment, header not matching the macro’s pattern, missing }, a directive pgzest cannot rewrite.
not-isolated Dynamic SQL (EXECUTE) in a body with isolated temp tables: what it runs is not rewritten, and may see the rows of other runs.
not-logged What a logged build cannot log: a routine whose body pgzest cannot instrument (it runs unlogged), or the routines a RETURN of a polymorphic result calls (logged as called by the caller).

Reported on the statements of a generated migration (pgzest migrate create), and listed in its !! TO ADAPT header when the migration may lose data or fail on a populated database.

Code Meaning
M001 NOT NULL column added without a default: fails if the table has rows.
M002 Dropped column, with its data.
M003 Dropped table, with its data.
M004 Column type changed: table rewritten, conversion possibly impossible.
M005 SET NOT NULL: full scan, fails on a null value.
M006 Index built without CONCURRENTLY on an existing table: writes blocked during the build.
M007 Constraint added to an existing table, checked on every row under lock (NOT VALID then VALIDATE avoids it).

Style and practice rules (pgzest lint).

Code Meaning
L001 SELECT * used outside a CTE definition. SELECT * makes schema changes silently break downstream consumers. Prefer explicitly listing columns.
L002 CREATE TABLE without a primary key constraint. Every table should have a primary key so rows can be uniquely identified and to support replication, foreign keys, and ORM ergonomics.
L003 Table or column name is not snake_case. PostgreSQL folds unquoted identifiers to lower-case. Quoted mixed-case names are error-prone and break convention. Enforce snake_case throughout.
L004 NOT IN (subquery) is used. NOT IN (SELECT ...) returns no rows if the subquery contains any NULL, which is almost never the intended behaviour. Use NOT EXISTS (...) instead.
L005 Foreign key column without a covering index. Unindexed FK columns cause full table scans on every FK lookup and CASCADE operation. Always index the referencing column(s).
L006 LIKE/ILIKE pattern with a leading wildcard. LIKE ‘%foo’ cannot use a standard B-tree index and causes a full table scan. Use a full-text index (GIN + tsvector) or a trigram index (pg_trgm) instead.
L007 Aggregate function mixed with bare column references without GROUP BY. SELECT count(*), user_id FROM tasks lacks GROUP BY user_id: PostgreSQL refuses it.
L008 ALTER TABLE ADD COLUMN NOT NULL without a DEFAULT value. On PostgreSQL < 11, this rewrites the entire table. Even on modern PG, it acquires an ACCESS EXCLUSIVE lock while the NOT NULL constraint is validated. Always provide a DEFAULT when adding NOT NULL columns to existing tables.
L009 JOIN table that is never referenced in SELECT, WHERE, or ORDER BY. An unused JOIN wastes I/O and can produce unexpected row multiplication. Remove the JOIN or verify that it was intended.

Enabled by [plugins] builtin = { rls-enforcer = {} }.

Code Meaning
rls/table-without-rls Table created without row-level security enabled (warning: RLS may be enabled separately, by ALTER TABLE … ENABLE ROW LEVEL SECURITY).
rls/select-star SELECT * in an application query: list the columns.

pgzest is made by Creatiwity, free and open source under the MIT or Apache-2.0 license.

GitHubLegal notice