Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

CREATE TABLE without PRIMARY KEY

Operation: CREATE TABLE without PRIMARY KEY

Why this is dangerous

Tables without a primary key cannot participate in logical replication. PostgreSQL replication slots require each replicated table to have either a primary key or an explicit REPLICA IDENTITY setting (FULL or USING INDEX). Without one, any attempt to replicate the table will fail or require manual intervention.

Beyond replication, a primary key provides:

  • A guaranteed unique row identifier for updates, deletes, and ON CONFLICT clauses
  • A natural target for foreign key references from other tables
  • An implicit index that speeds up single-row lookups

What diesel-guard checks

Any CREATE TABLE statement that defines no primary key — neither inline on a column nor as a separate table-level constraint — is flagged.

Exceptions (not flagged):

  • CREATE TEMP TABLE — temporary tables are session-scoped and never replicated
  • CREATE TABLE (LIKE other) — PK inheritance depends on INCLUDING CONSTRAINTS, which cannot be determined at parse time

Bad

CREATE TABLE events (
  name    TEXT,
  payload JSONB
);

Good

-- Option 1: identity column (recommended)
CREATE TABLE events (
  id      BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  name    TEXT,
  payload JSONB
);

-- Option 2: UUID
CREATE TABLE events (
  id      UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name    TEXT,
  payload JSONB
);

-- Option 3: separate constraint
CREATE TABLE events (
  id      BIGINT GENERATED ALWAYS AS IDENTITY,
  name    TEXT,
  payload JSONB,
  PRIMARY KEY (id)
);

Escape hatch

If the table is intentionally without a primary key (for example, a log table where you plan to set REPLICA IDENTITY FULL), wrap the statement in a safety-assured block:

-- safety-assured:start
CREATE TABLE audit_log (
  recorded_at TIMESTAMPTZ,
  message     TEXT
);
-- safety-assured:end