Require PDO exception mode and classify database failures by their causes

Merged
BI
4d ago
+538-77

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.

[!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.