Skip to content

[CASE-4454] Unique violation without key detail is handled on PostgreSQL - #82

Merged
aitorlb merged 2 commits into
masterfrom
unique-violation-without-detail-is-handled
Oct 22, 2026
Merged

aitorlb merged 2 commits into
masterfrom
unique-violation-without-detail-is-handled

Conversation

@aitorlb

@aitorlb aitorlb commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Ticket: CASE-4454

Problem

On PostgreSQL, a unique violation on a table that the current role is subject to row-level security on carries no DETAIL: Key (...)=(...) already exists. line. PostgreSQL omits it because the values could belong to a row the role cannot see.

PostgresqlAdapter.unique_error_columns does error.message[/Key \((.+)\)=/, 1].split(', '), so the missing line makes it call split on nil. validates_db_uniqueness_of then raises NoMethodError: undefined method 'split' for nil where it should add the validation error. It only happens on the path this gem exists for: two writers pass validation and the index decides.

Fix

error.message[/Key \((.+)\)=/, 1]&.split(', ')

Rescuer builds two keys for a unique violation, one from the columns and one from the index name, and UniquenessKeyExtractor.attribute_by_key registers every validator under both. The index name is still in the message (duplicate key value violates unique constraint "..."), so returning nil is enough. The MySQL adapter already returns nil from unique_error_columns and relies on the same thing.

Tests

  • spec/validations/validates_db_uniqueness_of_row_level_security_spec.rb: a table with row-level security enabled, run as a role that is not a superuser (a superuser bypasses RLS and keeps the detail line). It covers an attribute-only validator, a scoped validator and an index_name: validator, each through create and create!. A first example asserts the error really has no Key ( line, so it fails if PostgreSQL changes.
  • spec/adapters/postgresql_unique_error_columns_spec.rb: the adapter method with and without the detail line.

Without the change, 7 of the 9 new examples fail with undefined method 'split' for nil; with it all 9 pass. The existing PostgreSQL examples still pass (166 in validates_db_uniqueness_of_spec.rb, 25 in the presence and belongs-to specs), and RuboCop is clean with the repo config.

The new RLS spec creates and drops a role, so it needs a database user that can create roles (CI's root can). It skips otherwise.

Not run

MySQL and SQLite: the gems were not available where I ran this. The change touches only the PostgreSQL adapter.

Found while testing PostgreSQL row-level security on toptal/career-services-backend, where 7 specs hit this.

🤖 Generated with Claude Code

PostgreSQL leaves the `DETAIL:  Key (...)=(...) already exists.` line out of a
unique violation on a table the current role is subject to row-level security
on. `PostgresqlAdapter.unique_error_columns` called `split` on the nil match, so
`validates_db_uniqueness_of` raised NoMethodError instead of adding the error.

Validators are also registered under the index name, which the error still
carries, so returning nil (as the MySQL adapter already does) is enough.

Co-authored-by: Claude <noreply@anthropic.com>
@aitorlb aitorlb self-assigned this Oct 21, 2026
@aitorlb
aitorlb requested a review from AlfonsoUceda October 21, 2026 17:07
@aitorlb aitorlb changed the title Unique violation without key detail is handled on PostgreSQL [CASE-4454] Unique violation without key detail is handled on PostgreSQL Oct 22, 2026
…s it

The reason `unique_error_columns` can return nil was written twice, once in each spec, and not on the
method. Keep one explanation there and shorten the specs to point at it.
@aitorlb
aitorlb merged commit e4f9895 into master Oct 22, 2026
3 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants