Skip to content

features/sql: report integrity constraint violations as their own exception - #600

Merged
azahnen merged 3 commits into
masterfrom
constraint-violation-exception
Aug 19, 2026
Merged

features/sql: report integrity constraint violations as their own exception#600
azahnen merged 3 commits into
masterfrom
constraint-violation-exception

Conversation

@cportele

Copy link
Copy Markdown
Contributor

A mutation statement rejected by the database in SQLSTATE class 23 — a CHECK or foreign-key constraint, a unique index, or a trigger raising one — is caused by the data the client sent, not by a bug or an infrastructure problem. Every SQLException was wrapped in an IllegalStateException, so callers could not tell the two apart, report the rejection as a client error, or log it without a stack trace.

  • new FeatureMutationConstraintException, carrying the SQLSTATE
  • JdbcSqlSession.mutationFailed() picks the exception type from the SQLSTATE and replaces the four wrap sites; anything else stays an IllegalStateException and keeps its stack trace
  • the SQLSTATE is found by iterating the SQLException itself, which walks the next-exception chain as well as the causal one: executeBatch() reports a BatchUpdateException whose actual error is a next-exception, not a cause

…eption

A mutation statement rejected by the database in SQLSTATE class 23 — a CHECK or
foreign-key constraint, a unique index, or a trigger raising one — is caused by
the data the client sent, not by a bug or an infrastructure problem. Every
SQLException was wrapped in an IllegalStateException, so callers could not tell
the two apart, report the rejection as a client error, or log it without a
stack trace.

- new FeatureMutationConstraintException, carrying the SQLSTATE
- JdbcSqlSession.mutationFailed() picks the exception type from the SQLSTATE and
  replaces the four wrap sites; anything else stays an IllegalStateException and
  keeps its stack trace
- the SQLSTATE is found by iterating the SQLException itself, which walks the
  next-exception chain as well as the causal one: executeBatch() reports a
  BatchUpdateException whose actual error is a next-exception, not a cause
cportele and others added 2 commits August 18, 2026 10:00
…ntext

A rejection in SQLSTATE class 23 is caused by the data the client sent and is
reported back to it, so the exception now carries only the primary message of the
database error. The driver appends its call context to getMessage(), and the
failing statement was appended on top of that: for a single trigger rejection
that was 2.6 kB of PL/pgSQL frames and an INSERT with every attribute value,
where the first line is the part a client can act on. Internal schema, function
and column names are no longer disclosed either; the statement is still written
to the SQL debug log.

The primary message is separated by cutting at the first line break, which does
not depend on the server's message locale — PostgreSQL primary messages are
single-line and the context label is localised. System errors keep the full
message and the statement, so genuine bugs stay debuggable.
@azahnen
azahnen enabled auto-merge (squash) August 19, 2026 10:15
@azahnen
azahnen merged commit 6aae575 into master Aug 19, 2026
3 checks passed
@azahnen
azahnen deleted the constraint-violation-exception branch August 19, 2026 10:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants