Skip to content

Require PDO exception mode and classify database failures by their causes - #678

Merged
binaryfire merged 5 commits into
0.4from
require-pdo-exception-mode
Oct 19, 2026
Merged

binaryfire merged 5 commits into
0.4from
require-pdo-exception-mode

Conversation

@binaryfire

@binaryfire binaryfire commented Oct 19, 2026 •

Copy link
Copy Markdown
Member

PDO connections already default to exception mode, but applications can override that setting. In silent mode, or warning mode with warnings suppressed, a failed statement can return false and let a transaction commit incomplete work. This PR requires exception mode when the framework creates a PDO connection.

Exception mode has been PDO's default since PHP 8.0. Modern framework database layers already build around it: Symfony's standard Doctrine integration uses DBAL, which explicitly sets exception mode, while Tempest's PDO connection relies on PHP's default and exposes no error-mode option in its connection setup. Requiring the same contract in Hypervel removes legacy support for warning and silent modes, simplifies failure handling, and eliminates fallback logic for statements that fail without throwing.

It also fixes recovery decisions that match database error text in unrelated exceptions. A filesystem warning from a statement listener can cause a reconnect, an HTTP response containing “database is locked” can repeat a transaction callback, and a wrapped transport failure can stop a queue worker. Recovery should follow the underlying database failure, while preserving Laravel-style query exceptions for application error handling.

Connection requirements

  • Reject warning and silent error-mode options at physical connection creation, including reconnects and read/write connections. Other PDO options remain configurable.
  • Require custom connectors and directly supplied PDO handles to use exception mode too. There is no repeated mode check on queries or pooled connection borrows.
  • Remove the obsolete false-result fallback for internal session statements. Failed statements retain their original exception and still invalidate uncertain session state.
  • Keep public statement return types and the distinct false-result handling needed by quoting, last-insert IDs, ping and driver-neutral schema operations.

Failure classification and retries

  • Unwrap query exceptions before default lost-connection and concurrency classification. SQL and bindings must not influence whether a failure triggers recovery.
  • Restrict the default policies to PDO failures, while preserving the existing lost-connection signal and the native mysqlnd protocol warnings that can occur even in exception mode. Existing PDO codes and message fallbacks remain supported.
  • Preserve detector bindings and protected connection hooks for custom drivers. Non-PDO drivers provide an explicit policy instead of relying on arbitrary message matching. Queue workers retain their filter against unrelated job failures.
  • Preserve nested custom-driver retries. Once an inner transaction classifies a failure and wraps it in DeadlockException, the outer transaction accepts that signal without requiring the custom policy to recognize the framework wrapper. An application-thrown DeadlockException follows the same rule. Retries still require successful cleanup, no remaining transaction levels and an available attempt.
  • Require an underlying PDO failure before MySQL or MariaDB classifies an invalid value. An HTTP response or nested query context containing MySQL error text no longer becomes InvalidValueException.

QueryException wrapping, original causes, public method signatures and cancellation propagation remain intact. The mode check runs once per physical connection attempt; the other changes run on failure paths. Successful queries gain no extra checks, round trips or retained state.

Documentation and tests

The database guide documents the mode requirement and shows how a custom driver extends the default detectors. The porting guide and package README describe the configuration and recovery differences, and the completed warning-mode TODO is removed.

Regression coverage includes rejected modes, unrelated HTTP and filesystem failures, nested exception classification, custom-driver transaction retries and worker detector filtering. Schema restoration fixtures now throw native PDO failures while preserving their cleanup, ordering and exception-identity assertions.

Validation

  • Formatting and static analysis passed.
  • Targeted tests, Testbench and dogfood checks passed.
  • The complete database integration runners passed against MySQL, MariaDB, PostgreSQL and SQLite, including their package-specific suites.
  • Full parallel reruns stalled near completion and were stopped. They are incomplete, not passing full-suite results. No workaround, new skip or weakened assertion was added for the stall.

View guided diff Turn on auto-fix

Note

Require PDO exception mode and classify database failures by their cause

  • Connector.createConnection now throws InvalidArgumentException when the configured PDO error mode is warning or silent; an unset mode defaults to PDO::ERRMODE_EXCEPTION (Connector.php)
  • ConcurrencyErrorDetector, LostConnectionDetector, and MySqlConnection.isDataTypeError now unwrap QueryException and classify only PDOException (or LostConnectionException) causes. Generic exceptions or HTTP failures with database-like text are no longer treated as database failures
  • LostConnectionDetector adds explicit mysqlnd packet-warning prefixes as recognized lost-connection signals
  • ManagesTransactions.handleTransactionException retries cleaned-up DeadlockException failures even when the detector does not recognize the wrapper; PdoConnection.executeSessionStatement no longer throws when PDO::exec returns false
  • Behavioral Change: non-PDO drivers that relied on generic exception-message matching must now bind custom lost-connection and concurrency detectors, per updated guidance in database.md and porting-from-laravel.md; connections with warning/silent PDO modes are rejected at creation

Macroscope summarized 58f739f.

Summary by CodeRabbit

  • Bug Fixes
    • Database connections now reject warning and silent PDO error modes, helping ensure database failures are reported consistently.
    • Improved detection of lost connections and concurrency errors to avoid treating unrelated failures as database issues.
    • Outer transactions can retry after eligible nested deadlocks when cleanup succeeds and retry attempts remain.
    • Database failure classification now requires a PDO cause in cases where applicable; session statements no longer turn a false result into a synthetic exception.
  • Documentation
    • Added guidance on PDO error modes and database failure detection, including requirements for custom connectors and non-PDO drivers.

Reject warning and silent error modes at the shared physical connection boundary, including reconnects and effective read/write options. Failed statements must throw rather than allow a transaction to continue after a false result. Other PDO options and public statement return types remain unchanged.

Remove the obsolete false-result fallback for internal session statements while preserving invalidation and propagation of the original failure. Update schema restoration fixtures to throw native PDO failures and retain cleanup, committed-schema and exception-identity assertions. Add rejection coverage through the real SQLite connector.
Unwrap query exceptions before classification and reject arbitrary application or transport exception text. This prevents filesystem warnings and HTTP failures from reconnecting database sessions or stopping queue workers merely because their messages resemble database errors.

Preserve PDO message fallbacks, the existing lost-session signal and native mysqlnd protocol warnings that can still occur in exception mode. Keep custom detector bindings authoritative and retain the worker filter that excludes unrelated job failures even when a bound detector would accept their messages.
Require an underlying PDO failure in the default concurrency detector while retaining SQLSTATE, native driver codes and message fallbacks. HTTP response bodies and custom exception codes no longer cause transaction callbacks to be replayed accidentally; custom drivers keep the existing policy extension points.

Treat DeadlockException as a retryable signal at the outer transaction boundary. Nested custom-driver failures have already been classified before the framework wraps them, and matching that wrapper again can lose the original decision. Retries still require successful cleanup, transaction level zero and remaining attempts. Explicit application-thrown DeadlockException instances follow the same rule.

Cover the nested custom-driver regression, retain cancellation and cleanup assertions, and update simulated PDO failures without weakening their retry premises. Verify that an HTTP failure containing database lock text executes its transaction callback only once.
Unwrap nested query exceptions and require a PDO cause before applying MySQL invalid-value codes and message matching. An external-service response or SQL binding containing MySQL error text must remain an ordinary QueryException rather than become an InvalidValueException. MariaDB inherits the same correction.

Cover genuine nested invalid values alongside HTTP-body and query-context false positives, preserving the original exception chain. Add a statement-listener warning regression that proves the existing PDO is retained and the reconnect callback is never invoked.
Explain the connection-time requirement, supplied PDO and custom connector responsibilities, and the existing detector bindings for non-PDO drivers. Preserve Laravel-style query exception wrapping and show how custom policies inspect the underlying cause without matching SQL or bindings.

Record the deliberate configuration and default-recovery differences in the package README and porting guide. Remove the fulfilled warning-mode follow-up and retain the implementation plan with its rationale, removal audit and verification scope.

Formatting, static analysis, targeted tests, Testbench, dogfood and all four database integration runners passed. Full parallel reruns stalled near completion and remain incomplete; no timing workaround or test exclusion was added.
@binaryfire

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: hypervel/components/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b90b2e46-2fd6-44b8-a11e-54f3a2f9292d
📥 Commits

Reviewing files that changed from the base of the PR and between b7230cb and 58f739f.

📒 Files selected for processing: 21
  • docs/plans/2026-10-19-1400-pdo-exception-mode.md
  • docs/todo.md
  • src/database/README.md
  • src/database/src/Concerns/ManagesTransactions.php
  • src/database/src/ConcurrencyErrorDetector.php
  • src/database/src/Connectors/Connector.php
  • src/database/src/LostConnectionDetector.php
  • src/database/src/MySqlConnection.php
  • src/database/src/PdoConnection.php
  • src/docs/database.md
  • src/docs/porting-from-laravel.md
  • tests/Database/ConcurrencyErrorDetectorTest.php
  • tests/Database/DatabaseConnectionLostTest.php
  • tests/Database/DatabaseConnectionTest.php
  • tests/Database/DatabaseConnectorTest.php
  • tests/Database/DatabasePdoConnectionTest.php
  • tests/Database/DatabaseSQLiteBuilderTest.php
  • tests/Database/DatabaseSchemaBuilderTest.php
  • tests/Database/DatabaseServiceProviderTest.php
  • tests/Database/DatabaseSessionConfiguratorTest.php
  • tests/Queue/QueueWorkerTest.php
💤 Files with no reviewable changes: 1
  • docs/todo.md

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📝 Walkthrough

PDO connections now require exception mode. Default failure detectors distinguish PDO failures from unrelated exceptions, with selected mysqlnd warnings retained. Transaction retry and session-statement behavior are updated, with related documentation and tests.

Changes: Database failure handling

Layer / File(s) Summary
Require PDO exception mode
src/database/src/Connectors/Connector.php, tests/Database/DatabaseConnectorTest.php, src/database/README.md, src/docs/database.md, src/docs/porting-from-laravel.md, docs/plans/2026-10-19-1400-pdo-exception-mode.md, docs/todo.md
Connection creation rejects warning or silent PDO modes. Documentation describes the requirement for built-in and custom connectors, supplied PDO handles, and non-PDO driver policies. The deferred database TODO section was removed.
Restrict database-failure classification
src/database/src/ConcurrencyErrorDetector.php, src/database/src/LostConnectionDetector.php, src/database/src/MySqlConnection.php, src/docs/database.md, tests/Database/ConcurrencyErrorDetectorTest.php, tests/Database/DatabaseConnectionLostTest.php, tests/Database/DatabasePdoConnectionTest.php, tests/Database/DatabaseServiceProviderTest.php, tests/Database/DatabaseSessionConfiguratorTest.php, tests/Queue/QueueWorkerTest.php, docs/plans/2026-10-19-1400-pdo-exception-mode.md
Default detectors now require PDO causes, except for the specified lost-connection exception and mysqlnd warning cases. MySQL invalid-value classification requires a PDO cause. Tests cover nested causes, non-database failures, and queue-worker behavior.
Adjust transaction retries and session statements
src/database/src/Concerns/ManagesTransactions.php, src/database/src/PdoConnection.php, tests/Database/DatabaseConnectionTest.php, tests/Database/DatabasePdoConnectionTest.php, tests/Database/DatabaseSQLiteBuilderTest.php, tests/Database/DatabaseSchemaBuilderTest.php, tests/Database/DatabaseSessionConfiguratorTest.php, docs/plans/2026-10-19-1400-pdo-exception-mode.md
Outer transactions can retry a DeadlockException when cleanup succeeds, the transaction level is zero, and attempts remain. Session statements no longer raise a synthetic exception when PDO::exec() returns false; tests cover transaction retries and propagation of thrown PDO exceptions.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to 58f73

No merge-blocking issue was established; the change is ready for normal merge checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 32.08% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 53 functions across 16 files. (4 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely states the two main changes: requiring PDO exception mode and classifying database failures by their underlying causes.
Description check Passed The description is detailed and covers the problem, behavior changes, supporting regression coverage, documentation updates, and verification results. It also accurately reports that full parallel rer…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description is detailed and covers the problem, behavior changes, supporting regression coverage, documentation updates, and verification results. It also accurately reports that full parallel reruns were incomplete. The template checkboxes and an explicit issue or discussion link are not completed, but the substantive description is mostly complete.

Full details: Docstring Coverage

Explanation

Docstring coverage is 32.08% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 53 functions across 16 files. (4 skipped: 4 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

  • Fix all pre-merge checks with AI
✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Some tools did not complete. Review the errors below.

🔧 PHPMD (2.15.0)
tests/Database/DatabaseConnectionTest.php

PHPMD could not process this file (exit code 3): Unexpected token: ], line: 793, col: 91, file: /tests/Database/DatabaseConnectionTest.php.
#0 phar:///usr/bin/phpmd/vendor/pdepend/pdepend/src/main/php/PDepend/Source/Language/PHP/AbstractPHPParser.php(8056): PDepend\Source\Parser\UnexpectedTokenException->__construct()
#1 phar:///usr/bin/phpmd/vendor/pdepend/pdepend/src/main/php/PDepend/Source/Language/PHP/AbstractPHPParser.php(8041): PDepend\Source\Language\PHP\AbstractPHPPa...

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Require PDO exception mode and classify database failures by cause

✨ Enhancement 🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Reject silent and warning PDO modes when framework connectors create physical connections.
• Classify underlying database failures so unrelated HTTP, filesystem, and listener errors cannot
 trigger recovery.
• Preserve custom-driver policies and nested retries; document migration requirements and expand
 regression coverage.
Diagram

graph TD
  Options["PDO options"] --> Connector["Shared connector"] --> PDO["PDO connection"] --> Query["Query exception"] --> Cause["Underlying cause"] --> Detectors["Failure detectors"] --> Recovery["Recovery decisions"]
  PDO --> Cause
  Cause --> MySQL["MySQL classification"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Silently force exception mode
  • ➕ Allows existing warning- or silent-mode configurations to connect.
  • ➖ Hides a configuration mismatch and changes the application's requested PDO behavior without notice.
2. Guard each recovery consumer
  • ➕ Allows caller-specific failure rules.
  • ➖ Duplicates origin checks across transactions, connections, workers, and locks; future consumers could omit them.

Recommendation: Keep the PR's shared connector validation and detector-level classification. They establish one clear connection requirement and correct the policy used by multiple recovery paths while leaving explicit custom-driver bindings available.

Files changed (21) +538 / -77

Enhancement (1) +4 / -0
Connector.phpReject non-exception PDO modes when connecting +4/-0

Reject non-exception PDO modes when connecting

• Validates effective PDO options at the shared physical connection boundary and throws for warning or silent mode. Other connector options and the existing connection retry path remain unchanged.

src/database/src/Connectors/Connector.php

Bug fix (4) +44 / -15
ManagesTransactions.phpRetain retries for classified nested deadlocks +2/-1

Retain retries for classified nested deadlocks

• Allows an outer transaction to retry a DeadlockException even when a custom policy does not recognize that framework wrapper. Successful cleanup, zero remaining transaction levels, and an available attempt remain required.

src/database/src/Concerns/ManagesTransactions.php

ConcurrencyErrorDetector.phpLimit default concurrency detection to PDO causes +14/-12

Limit default concurrency detection to PDO causes

• Unwraps query exceptions before checking driver codes or messages and rejects non-PDO causes. Query SQL, bindings, and unrelated HTTP failures can no longer trigger default concurrency retries.

src/database/src/ConcurrencyErrorDetector.php

LostConnectionDetector.phpRestrict default lost-connection classification +14/-0

Restrict default lost-connection classification

• After unwrapping query exceptions, accepts PDO failures, the explicit lost-connection signal, and narrowly identified mysqlnd warnings. Unrelated exceptions no longer qualify through database-like message text.

src/database/src/LostConnectionDetector.php

MySqlConnection.phpClassify invalid values from the underlying PDO failure +14/-2

Classify invalid values from the underlying PDO failure

• Requires a PDO cause before applying MySQL invalid-value codes or message fallbacks. This prevents HTTP text or nested query context from producing InvalidValueException while preserving the original query-exception cause.

src/database/src/MySqlConnection.php

Refactor (1) +1 / -3
PdoConnection.phpPropagate internal session-statement failures directly +1/-3

Propagate internal session-statement failures directly

• Removes the synthetic exception created when an internal PDO exec returned false. Thrown failures still mark the session state unknown and propagate unchanged.

src/database/src/PdoConnection.php

Tests (10) +243 / -50
ConcurrencyErrorDetectorTest.phpCover PDO-only concurrency classification +25/-2

Cover PDO-only concurrency classification

• Retains PDO code and message coverage while rejecting custom-driver errors without a policy and HTTP responses containing lock text, both directly and through query wrappers.

tests/Database/ConcurrencyErrorDetectorTest.php

DatabaseConnectionLostTest.phpCover eligible and unrelated lost-connection causes +44/-3

Cover eligible and unrelated lost-connection causes

• Tests nested PDO failures, the explicit lost-connection signal, and supported mysqlnd warning prefixes. Verifies that filesystem warnings, unsupported PDO warning-mode text, and unconfigured custom failures do not match.

tests/Database/DatabaseConnectionLostTest.php

DatabaseConnectionTest.phpPreserve transaction recovery under explicit driver policies +48/-4

Preserve transaction recovery under explicit driver policies

• Adds a nested custom-driver retry regression and gives neutral test drivers explicit failure classification. Updates PDO deadlock fixtures and the native-warning fixture so retry and cleanup assertions exercise supported failure types.

tests/Database/DatabaseConnectionTest.php

DatabaseConnectorTest.phpTest rejection of warning and silent PDO options +13/-0

Test rejection of warning and silent PDO options

• Uses the real SQLite connector to verify both incompatible error modes fail with the expected configuration message.

tests/Database/DatabaseConnectorTest.php

DatabasePdoConnectionTest.phpTest listener recovery and MySQL cause classification +62/-0

Test listener recovery and MySQL cause classification

• Verifies a filesystem-style statement-listener warning retains its cause without reconnecting. Checks that HTTP and query-context text do not become invalid-value errors, while a nested PDO failure still does.

tests/Database/DatabasePdoConnectionTest.php

DatabaseSQLiteBuilderTest.phpModel native PDO failures in SQLite schema restoration +23/-19

Model native PDO failures in SQLite schema restoration

• Replaces false-return and synthetic-error fixtures with prebuilt PDO exceptions. Keeps assertions for committed schema, unusable sessions, restoration order, and exception identity.

tests/Database/DatabaseSQLiteBuilderTest.php

DatabaseSchemaBuilderTest.phpAssert original PDO restoration failure is retained +5/-6

Assert original PDO restoration failure is retained

• Makes the foreign-key restoration mock throw a PDO exception and asserts that the same failure propagates while the session becomes non-reusable.

tests/Database/DatabaseSchemaBuilderTest.php

DatabaseServiceProviderTest.phpKeep detector fallback coverage grounded in PDO failures +1/-1

Keep detector fallback coverage grounded in PDO failures

• Changes the bare-container lost-connection fixture to a PDO exception so it continues to test fallback detector resolution under the narrower default policy.

tests/Database/DatabaseServiceProviderTest.php

DatabaseSessionConfiguratorTest.phpPrevent retries for HTTP lock-like responses +7/-4

Prevent retries for HTTP lock-like responses

• Verifies an HTTP response containing database lock text does not repeat a transaction callback. Updates a nested deadlock fixture to use a genuine PDO failure.

tests/Database/DatabaseSessionConfiguratorTest.php

QueueWorkerTest.phpVerify worker filtering with default and bound detectors +15/-11

Verify worker filtering with default and bound detectors

• Covers a supported mysqlnd warning and wrapped listener failures that must not stop the worker. Confirms a bound detector handles eligible database failures without being applied to an unrelated direct job failure.

tests/Queue/QueueWorkerTest.php

Documentation (5) +246 / -9
2026-10-19-1400-pdo-exception-mode.mdRecord the exception-mode and failure-classification plan +185/-0

Record the exception-mode and failure-classification plan

• Documents the intended contracts, design evidence, affected paths, test coverage, and verification workflow. It also records the limits of the mode check and the custom-driver extension strategy.

docs/plans/2026-10-19-1400-pdo-exception-mode.md

todo.mdRemove the completed database-mode TODO +0/-9

Remove the completed database-mode TODO

• Removes the deferred work item covering exception mode, recovery classification, and related verification.

docs/todo.md

README.mdSummarize database differences from Laravel +2/-0

Summarize database differences from Laravel

• Notes the exception-mode requirement and the narrower default recovery policy, with links to the database guide.

src/database/README.md

database.mdDocument PDO mode and custom failure detectors +55/-0

Document PDO mode and custom failure detectors

• Explains the connector requirement, supplied-handle obligations, and preserved query-exception behavior. Adds an example of extending and binding a detector for non-PDO failures.

src/docs/database.md

porting-from-laravel.mdAdd migration guidance for database recovery +4/-0

Add migration guidance for database recovery

• Directs applications to remove warning or silent PDO settings and provide explicit detector policies for custom non-PDO drivers.

src/docs/porting-from-laravel.md

@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can import rules and skills only from the repositories your team follows

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@binaryfire
binaryfire merged commit 9f57185 into 0.4 Oct 19, 2026
46 of 50 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.

1 participant