select open change scope Open full search

PG.CENTER connects PostgreSQL documentation, reference, and ecosystem knowledge. Maintained by Pigsty.

SQLSTATE / CLASS 23 · INTEGRITY CONSTRAINT VIOLATION

restrict_violation

SQLSTATE
23001
Condition name
restrict_violation
Class
Integrity Constraint Violation
Source macro
ERRCODE_RESTRICT_VIOLATION
Evidence
Observed at runtime in the source evidence
Analysis & operational context

English SQLSTATE atlas: authored explanations and source/runtime evidence are separate from the manual definitions. View source ↗

<h1>23001 — restrict_violation</h1>

At a glance

23001 is the specific RESTRICT violation condition. In the selected PostgreSQL 18.6 case, deleting a referenced parent row raised it with the child constraint and key in the diagnostic. PostgreSQL 10 reported the same tested operation through 23503, so version and server diagnostics must be recorded. A separate PostgreSQL 17.11 comparison also returned 23503 for this RESTRICT operation; it is not 23001 coverage.

Meaning

An ON DELETE RESTRICT or ON UPDATE RESTRICT action checks the referencing row immediately and refuses the parent change. This differs from NO ACTION: a DEFERRABLE NO ACTION constraint can postpone its check until the relevant commit, while RESTRICT is not deferrable. The source builds a dynamic primary/detail message from the parent table, foreign-key constraint, child table, and key visibility.

Diagnosis

Record the exact SQLSTATE and constraint_name, table_name, schema_name, and detail. Check the child rows before changing the parent. In an explicit transaction the failed delete leaves the connection INERROR; ROLLBACK is required before the repair transaction can delete the child and then the parent.

Response

Choose the action deliberately: remove or reassign dependent rows, change the parent operation, or redesign the FK action after reviewing its integrity meaning. Do not turn RESTRICT into a deferred NO ACTION merely to make a migration pass. The selected repair deletes the dependent row in a new BEGIN/COMMIT sequence and then deletes the parent.

Observed diagnostics

18.6 (Homebrew) / latest:SQLSTATE 23001; primary update or delete on table "parents" violates RESTRICT setting of foreign key constraint "children_parent_id_fkey" on table "children"; DETAIL Key (id)=(1) is referenced from table "children".; after_error INERROR; after_rollback IDLE. 10.21 (Debian 10.21-1.pgdg90+1) / pg10:SQLSTATE 23503; primary update or delete on table "parents" violates foreign key constraint "children_parent_id_fkey" on table "children"; DETAIL Key (id)=(1) is still referenced from table "children".; after_error INERROR; after_rollback IDLE. 17.11 (pg17) / boundary:SQLSTATE 23503; primary update or delete on table "parents" violates foreign key constraint "children_parent_id_fkey" on table "children"; DETAIL Key (id)=(1) is still referenced from table "children".; after_error INERROR; after_rollback IDLE; final counts [0, 0]. This boundary case is not 23001 coverage.

Representative case

The runner reads these statements from verify/cases/23001/snippets.json (SHA-256 6086c1ce982afa5438bbcd29b86ec4cd0cbe0c76fa6152c1eb4a330a7706d5c8) and qualifies the temporary table names; the complete setup, assertions, and cleanup are in the case export.

-- create_parent
CREATE TABLE parents(id integer PRIMARY KEY);
-- create_child
CREATE TABLE children(id integer PRIMARY KEY, parent_id integer NOT NULL REFERENCES parents(id) ON DELETE RESTRICT);
-- seed_parent
INSERT INTO parents VALUES (1);
-- seed_child
INSERT INTO children VALUES (10, 1);
-- begin
BEGIN;
-- trigger
DELETE FROM parents WHERE id = 1;
-- rollback
ROLLBACK;
-- repair_begin
BEGIN;
-- repair_child
DELETE FROM children WHERE id = 10;
-- repair_parent
DELETE FROM parents WHERE id = 1;
-- commit
COMMIT;
-- verify
SELECT (SELECT count(*) FROM parents), (SELECT count(*) FROM children);

The SQLSTATE, diagnostic, transaction-state, and repair assertions for this excerpt are produced from the shared registry; structured evidence · case export.

Authored evidence IDs: identity, restrict-path, no-action-boundary, runtime, runtime.pg17-boundary. Selected base runtime records: runtime.23001-batch1-latest2-20260909.latest, runtime.23001-batch1-pg10b-20260909.pg10. Separate boundary record: runtime.23001-boundary-pg17-final-20260909.pg17.

Versions

The locked catalogue observes the condition by 7.4 and in all listed formal snapshots. The selected base cases observe 23001 on 18.6 and 23503 on 10.21 for the same RESTRICT delete. A separate PG17.11 boundary comparison also returned 23503, with the same INERROR to IDLE recovery; it is retained as a comparison and does not establish a universal result for every PostgreSQL 11–17 minor release.

Sources

Source evidence

Evidence belongs to the frozen source and runtime versions listed here. It is not a runtime verification of the selected manual version.

23001 is restrict_violation, a specific Class 23 condition.

Method: Read the condition definition and macro.

Limits: The code is narrower than the general foreign-key violation code 23503.

src.errcodes.18.6

The RI trigger reports 23001 when an immediate ON DELETE or ON UPDATE RESTRICT action finds a referencing row.

Method: Trace the fixed ri_ReportViolation call and compare the documented action timing.

Limits: The exact DETAIL depends on privileges and key values; do not treat one rendered DETAIL as a static format string.

src.ri_triggers.18.6 src.calls.REL_18_6 doc.ddl-constraints.18.6

RESTRICT is checked immediately and is not deferrable; NO ACTION can be deferred when the constraint is declared DEFERRABLE, and PostgreSQL 10 uses the general 23503 path for the tested RESTRICT delete.

Method: Read action semantics and the two selected runs.

Limits: The PG10 result is a version/path boundary, not evidence that the user operation succeeded.

src.ri_triggers.18.6 doc.ddl-constraints.18.6 runtime.23001-batch1-pg10b-20260909.pg10

The selected PG18 run returned 23001, entered INERROR, recovered after ROLLBACK, and committed deletion after removing the dependent row; PG10 returned 23503 for the same mechanism.

Method: Compare summaries and raw diagnostics with the shared registry.

Limits: Only the PG18 result is an observed runtime for code 23001; the PG10 case is explicitly not_applicable for that code.

runtime.23001-batch1-latest2-20260909.latest runtime.23001-batch1-pg10b-20260909.pg10

On the fixed PG17.11 boundary case, the same referenced-parent ON DELETE RESTRICT operation returned the general foreign_key_violation 23503 rather than dedicated 23001; the failed transaction moved from INERROR to IDLE after ROLLBACK and cleanup assertions passed.

Method: Read the PG17 boundary summary for the exact primary/detail, SQLSTATE, transaction statuses, and final counts.

Limits: This is one PG17.11 comparison point and is not a universal claim for every PostgreSQL 11–17 minor release. It is not an observed 23001 runtime.

restrict-path runtime.23001-boundary-pg17-final-20260909.pg17

Message templates

ERROR · restrict

Primary

update or delete on table "%s" violates RESTRICT setting of foreign key constraint "%s" on table "%s"

Detail

Key (%s)=(%s) is referenced from table "%s".

The privilege-aware branch can instead emit the source detail template without key values.

Reproduction & repair cases

restrict_delete_referenced_parent · PG 18

Preconditions

  • A child row references a parent through ON DELETE RESTRICT
  • The parent delete is attempted while the child remains

Trigger: Delete the referenced parent row.

Expected assertions

  • SQLSTATE is 23001
  • The RESTRICT constraint and referencing table are identified
  • Deleting the child first permits an explicit parent repair

Repair: Resolve the dependent row according to business rules, then delete or retain the parent; changing RESTRICT to another action is a schema decision, not an error fix.

Cleanup: Drop the case schema with an owner connection.

Recorded runtime evidence

18.6 (Homebrew) · passed

Run: 23001-batch1-latest2-20260909

{
  "detail": "Key (id)=(1) is referenced from table \"children\".",
  "primary": "update or delete on table \"parents\" violates RESTRICT setting of foreign key constraint \"children_parent_id_fkey\" on table \"children\"",
  "sqlstate": "23001",
  "after_error": "INERROR",
  "final_counts": [
    0,
    0
  ],
  "after_rollback": "IDLE"
}
10.21 (Debian 10.21-1.pgdg90+1) · not_applicable

Run: 23001-batch1-pg10b-20260909

{
  "detail": "Key (id)=(1) is still referenced from table \"children\".",
  "primary": "update or delete on table \"parents\" violates foreign key constraint \"children_parent_id_fkey\" on table \"children\"",
  "sqlstate": "23503",
  "after_error": "INERROR",
  "final_counts": [
    0,
    0
  ],
  "after_rollback": "IDLE"
}
17.11 (Debian 17.11-1.pgdg13+2) · not_applicable

Run: 23001-boundary-pg17-final-20260909

{
  "detail": "Key (id)=(1) is still referenced from table \"children\".",
  "primary": "update or delete on table \"parents\" violates foreign key constraint \"children_parent_id_fkey\" on table \"children\"",
  "severity": "ERROR",
  "sqlstate": "23503",
  "after_error": "INERROR",
  "final_counts": [
    0,
    0
  ],
  "after_rollback": "IDLE",
  "constraint_name": "children_parent_id_fkey",
  "expected_sqlstate": "23503",
  "natural_23001_observed": false
}

Definition snapshot: english-manuals:cb4be5aedc601c28f8e59d76c82… · English manual source