Skip to content

The Check Contract

This book is for the minority of sites that need more than the built-in 165 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 Using the Seed Catalog in the User Guide 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 User Guide for the full set of these lookup functions, across every catalog entity.

The contract

A check is an ordinary PostgreSQL function that:

  1. Has exactly one parameter — the ID of the current run — and returns pghf.check_result.
  2. 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.
  3. 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.
  4. 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.
  5. Never writes to the raw-results table itself — and can't, since only the collection engine has access to the internal helper that does.
  6. 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.

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.