All rules Rule BV018
note
hosted, paid — --remote

Table created without a primary key

Note — this works and blocks nothing, but a performance regression is likely.

What it catches

A table without a primary key (or any unique constraint) works until it doesn't: logical replication rejects UPDATE/DELETE without a replica identity, targeted row operations degrade to full scans, and maintenance tooling that addresses rows by key can't help you. Adding a key later on a populated table is exactly the migration this tool exists to warn about.

Fires on

CREATE TABLE audit_log (entry text, created_at timestamptz);

Do this instead

Give every table a primary key at creation, when it costs nothing — a bigint identity column is the default answer. It provides the replica identity logical replication requires, and spares you the ADD PRIMARY KEY migration on a populated table later.

CREATE TABLE audit_log (
  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  entry text,
  created_at timestamptz NOT NULL DEFAULT now()
);
Catch this before it ships

This rule runs in the hosted service on Startup and above — add --remote with a team token, or use the GitHub Action: npx bolvrk check migration.sql