Skip to content

Treat PostgreSQL RESTRICT violations (SQLSTATE 23001) as ReferenceConstraintException - #103

Merged
Giorgi merged 1 commit into
Giorgi:mainfrom
zerox12311:fix/postgresql-restrict-violation
Sep 14, 2026
Merged

Giorgi merged 1 commit into
Giorgi:mainfrom
zerox12311:fix/postgresql-restrict-violation

Conversation

@zerox12311

Copy link
Copy Markdown
Contributor

Problem

PostgreSQL 18 reports a violation of an ON DELETE/UPDATE RESTRICT foreign key with SQLSTATE 23001 (restrict_violation) instead of 23503 (foreign_key_violation). PostgreSQL 17 and earlier used 23503 for both RESTRICT and NO ACTION; NO ACTION still reports 23503 on 18.

The change is in src/backend/utils/adt/ri_triggers.c (ri_ReportViolation, new is_restrict branch using ERRCODE_RESTRICT_VIOLATION). It is not mentioned in the PostgreSQL 18 release notes.

PostgreSQLExceptionClassifier.IsReferenceConstraintError only matches PostgresErrorCodes.ForeignKeyViolation, so on PostgreSQL 18 an EF Core relationship configured with DeleteBehavior.Restrict surfaces as a plain DbUpdateException instead of ReferenceConstraintException:

Npgsql.PostgresException : 23001: update or delete on table "Products" violates RESTRICT setting of foreign key constraint "FK_ProductReviews_Products_ProductId" on table "ProductReviews"

Fix

Also match PostgresErrorCodes.RestrictViolation (23001) in IsReferenceConstraintError. PostgresErrorCodes.RestrictViolation is a const, and PostgreSQL 17 and earlier never emit 23001, so behaviour on older servers is unchanged.

Tests

  • Added a ProductReview entity to DemoContext with an OnDelete(DeleteBehavior.Restrict) relationship to Product.
  • Added DeleteParentItemWithRestrictThrowsReferenceConstraintException and ...ThroughExecuteDelete to DatabaseTests, mirroring the existing DeleteParentItem... tests, asserting ReferenceConstraintException is thrown.
  • Switched the PostgreSQL Testcontainer to postgres:18 (the Testcontainers default is 15.x, where 23001 is never emitted). Without the classifier change both new tests fail on PostgreSQL 18 with the 23001 error above; with it, the full PostgreSQL suite passes.
  • The two new tests are skipped for SQLite: SQLite reports RESTRICT violations with SQLITE_CONSTRAINT_TRIGGER (1811) rather than SQLITE_CONSTRAINT_FOREIGNKEY (787), so SqliteExceptionClassifier does not classify them either. That is a separate issue and is left out of this PR.

…straintException

PostgreSQL 18 reports violations of ON DELETE/UPDATE RESTRICT foreign keys
with SQLSTATE 23001 (restrict_violation) instead of 23503.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The classifier change is focused and covered by appropriate PostgreSQL 18 integration tests.

Pull request overview

Adds PostgreSQL 18 support for classifying SQLSTATE 23001 RESTRICT violations as reference constraint errors.

Changes:

  • Recognizes RestrictViolation alongside foreign-key violations.
  • Adds RESTRICT relationship integration tests using PostgreSQL 18.
  • Skips unsupported SQLite cases.
File summaries
File Description
PostgreSQLExceptionClassifier.cs Classifies SQLSTATE 23001.
DemoContext.cs Adds the RESTRICT test relationship.
DatabaseTests.cs Tests tracked and bulk deletions.
PostgreSQLTests.cs Runs tests against PostgreSQL 18.
SqliteTests.cs Skips SQLite-specific unsupported behavior.
Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 0
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@Giorgi

Giorgi commented Sep 14, 2026

Copy link
Copy Markdown
Owner

@zerox12311 How do other databases (SQL Server, Oracle, MySQL) treat DeleteBehavior.Restrict ?

@zerox12311

Copy link
Copy Markdown
Contributor Author

As far as I know, PostgreSQL 18 is the only one affected. The new tests pass on SQL Server, MySQL and Oracle in this PR's CI run without any change to their classifiers, since those servers report a RESTRICT violation with the same error code as NO ACTION (SQL Server and Oracle don't even emit a RESTRICT clause; EF Core generates NO ACTION / no clause for them).

That said, I only verified against the versions used in the test containers, and I can't rule out that other versions behave differently. So I kept this PR focused on the PostgreSQL 18 change.

The one other case I ran into is SQLite: it reports RESTRICT violations with SQLITE_CONSTRAINT_TRIGGER instead of SQLITE_CONSTRAINT_FOREIGNKEY, so the two new tests are skipped there. Happy to handle that here or in a separate PR if you'd like.

@Giorgi

Giorgi commented Sep 14, 2026

Copy link
Copy Markdown
Owner

I'm not sure that SQLite needs any additional handling. If we report every SQLITE_CONSTRAINT_TRIGGER we will have false positives, i.e. errors thrown from triggers will appear as ReferenceConstraintException

@zerox12311

Copy link
Copy Markdown
Contributor Author

Agreed, that would cause false positives. I'll leave SQLite as is and keep this PR to the PostgreSQL 18 change only.

@Giorgi Giorgi changed the title PostgreSQL: treat RESTRICT violations (SQLSTATE 23001) as ReferenceConstraintException Treat PostgreSQL RESTRICT violations (SQLSTATE 23001) as ReferenceConstraintException Sep 14, 2026
@Giorgi
Giorgi merged commit ced5ca8 into Giorgi:main Sep 14, 2026
3 of 4 checks passed
@Giorgi

Giorgi commented Sep 14, 2026

Copy link
Copy Markdown
Owner

Thanks for the PR!

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.

3 participants