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. |