diff --git a/src/Connection.php b/src/Connection.php index c936b4a302..5a66e1ad21 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -926,15 +926,20 @@ public function lastInsertId(): int|string public function transactional(Closure $func): mixed { $this->beginTransaction(); + + $successful = false; + try { $res = $func($this); $this->commit(); - return $res; - } catch (Throwable $e) { - $this->rollBack(); + $successful = true; - throw $e; + return $res; + } finally { + if (! $successful) { + $this->rollBack(); + } } } diff --git a/tests/ConnectionTest.php b/tests/ConnectionTest.php index 29922683d4..a71bc958d9 100644 --- a/tests/ConnectionTest.php +++ b/tests/ConnectionTest.php @@ -623,4 +623,25 @@ public function testDefaultSchemaManagerFactory(): void $connection = DriverManager::getConnection(['driver' => 'sqlite3', 'memory' => true]); self::assertInstanceOf(SQLiteSchemaManager::class, $connection->createSchemaManager()); } + + public function testItPreservesTheOriginalExceptionOnRollbackFailure(): void + { + $connection = new class (['memory' => true], new Driver\SQLite3\Driver()) extends Connection { + public function rollBack(): void + { + throw new Exception('Rollback exception'); + } + }; + + try { + $connection->transactional(static function (): void { + throw new Exception('Original exception'); + }); + self::fail('Exception expected'); + } catch (Exception $e) { + self::assertSame('Rollback exception', $e->getMessage()); + self::assertNotNull($e->getPrevious()); + self::assertSame('Original exception', $e->getPrevious()->getMessage()); + } + } }