The Check Contract¶
This book is for the minority of sites that need more than the built-in 175 checks — a business rule specific to your own application, something the built-in catalog simply doesn't cover, or your own variant of a check that's already there. If you haven't already, read the Catalog book first — most sites never need anything in this book at all.
The collection engine never hardcodes a list of checks. It loops over whatever a suite's membership says belongs to it, and dynamically calls each member's registered function. This means registering your own check and adding it to a suite is enough to make it run — no part of the framework itself needs to change.
Before writing a new check, it's worth checking whether something close already exists: SELECT * FROM pghf.list_checks(p_namespace_code => 'PGHF'); browses the built-in catalog without needing a raw query against its underlying table (or use pghf.get_check('PGHF01-003') if you already have the check ID). See Reading the Catalog Without Table Access in the Catalog book for the full set of these lookup functions, across every catalog entity.
The contract¶
A check is an ordinary PostgreSQL function that:
- Has exactly one parameter — the ID of the current run — and returns
pghf.check_result. - Returns a result meaning "collected", with your data attached and no note, on success; or a result meaning "skipped", with no data but a required explanatory note, if it simply doesn't apply here.
- If its result type isn't
jsonb, includes a key literally named"value"in what it collected — the single primary number or answer the check is about. This is enforced centrally by the collection engine, not left to every check body to remember on its own. - Handles its own data prerequisites — for example, "needs the Spock extension," or "needs a target PostgreSQL version" — inside its own body. The dispatch engine is generic, with no per-category logic of its own. Two things are the exception: a required PostgreSQL version range, and a required primary/standby topology. Those are declared as catalog metadata instead, and the engine excludes a non-qualifying check before your function is ever called — see Declaring a Version or Topology Requirement.
- Never writes to the raw-results table itself — and can't, since only the collection engine has access to the internal helper that does.
- Doesn't need its own exception handling — if it raises an error, the collection engine catches it centrally and logs an error result with the underlying database message attached.
- Schema-qualifies any object it reads outside
pg_catalogandpublic— good practice in any function, and required if the check will ever run in a suite dispatched through pg_relay, where checks execute in a worker whosesearch_pathis pinned to exactlypg_catalog, public.
pghf.check_result is a PostgreSQL composite type with three fields:
CREATE TYPE pghf.check_result AS (
status text, -- 'COLLECTED' or 'SKIPPED' — never set 'ERROR' yourself
observed jsonb, -- your data; empty for SKIPPED
note text -- required for SKIPPED, optional for COLLECTED
);
Because this is a real PostgreSQL type, a function that doesn't match its shape fails to even compile — you'll find out immediately, not later at runtime.
A security note before you register your first check¶
Your check function runs as whoever calls the collection engine — often a scheduled job with broad privileges — and PostgreSQL grants permission to run a newly created function to everyone by default, the moment you create it. If that's not what you want, revoke that permission explicitly right after defining your function, and grant it back only to roles you trust with that level of access. See The Access Model in the User Guide for the full trust-model explanation — it applies to custom evaluators and events in exactly the same way.
Continue to Worked Example: A Custom Check.