From f1357a7e209b4ec676d959649d686478d81ae8df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20R=C3=A9?= Date: Tue, 19 May 2026 11:03:37 +0400 Subject: [PATCH] Check constraint name in PgError unique violations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Checking only pgErr.Code == "23505" is insufficient when a table has multiple unique constraints. Always verify pgErr.ConstraintName as well to avoid mapping unrelated violations to the wrong sentinel error. Signed-off-by: Émile Ré --- contrib/claude/coredata.md | 31 +++++++++++++++++++++++ pkg/coredata/common_third_party_domain.go | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/contrib/claude/coredata.md b/contrib/claude/coredata.md index f74d87e31..7e5b28184 100644 --- a/contrib/claude/coredata.md +++ b/contrib/claude/coredata.md @@ -149,6 +149,37 @@ var ( Map `pgx.ErrNoRows` to `ErrResourceNotFound`. Check unique constraint violations for `ErrResourceAlreadyExists`, foreign key violations for `ErrResourceInUse`. +**Always check both `pgErr.Code` and `pgErr.ConstraintName`** when mapping PostgreSQL errors to sentinel errors. Checking only the error code (e.g. `"23505"`) is not enough — a table may have multiple unique constraints, and a blind code-only check maps unrelated constraint violations to the wrong sentinel error. + +```go +// Good — checks both code and constraint name +if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok { + if pgErr.Code == "23505" && pgErr.ConstraintName == "controls_framework_ref_unique" { + return ErrResourceAlreadyExists + } +} + +// Good — multiple constraints on the same table +if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok { + if pgErr.Code == "23505" { + switch pgErr.ConstraintName { + case "document_versions_document_id_major_minor_key", + "document_one_active_version_idx": + return ErrResourceAlreadyExists + } + } +} + +// Bad — code-only check; any unique violation silently becomes ErrResourceAlreadyExists +if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok { + if pgErr.Code == "23505" { + return ErrResourceAlreadyExists + } +} +``` + +The same applies to foreign key violations (`"23503"`) mapped to `ErrResourceInUse` — always verify the constraint name. + ## Filters Filters implement `SQLFragment() string` and `SQLArguments() pgx.NamedArgs`. Use double pointers for three-state filtering: `nil` = no filter, `*nil` = IS NULL, `*val` = equals. diff --git a/pkg/coredata/common_third_party_domain.go b/pkg/coredata/common_third_party_domain.go index e9406144a..207027591 100644 --- a/pkg/coredata/common_third_party_domain.go +++ b/pkg/coredata/common_third_party_domain.go @@ -108,7 +108,7 @@ INSERT INTO common_third_party_domains ( _, err := conn.Exec(ctx, q, args) if err != nil { if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok { - if pgErr.Code == "23505" { + if pgErr.Code == "23505" && pgErr.ConstraintName == "common_third_party_domains_party_domain_key" { return ErrResourceAlreadyExists } }