Skip to content

Commit 71e5b43

Browse files
committed
Ensure nested transactions are not wrongly rolled back
1 parent 086213b commit 71e5b43

File tree

2 files changed

+34
-9
lines changed

2 files changed

+34
-9
lines changed

src/Connection.php

+19-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Doctrine\DBAL\Cache\QueryCacheProfile;
1010
use Doctrine\DBAL\Driver\API\ExceptionConverter;
1111
use Doctrine\DBAL\Driver\Connection as DriverConnection;
12+
use Doctrine\DBAL\Driver\Exception as TheDriverException;
1213
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
1314
use Doctrine\DBAL\Driver\Statement as DriverStatement;
1415
use Doctrine\DBAL\Event\TransactionBeginEventArgs;
@@ -1279,12 +1280,29 @@ public function transactional(Closure $func)
12791280
{
12801281
$this->beginTransaction();
12811282

1283+
$successful = false;
1284+
12821285
try {
12831286
$res = $func($this);
12841287

1288+
$successful = true;
1289+
} finally {
1290+
if (! $successful) {
1291+
$this->rollBack();
1292+
}
1293+
}
1294+
1295+
$shouldRollback = true;
1296+
try {
12851297
$this->commit();
1298+
1299+
$shouldRollback = false;
1300+
} catch (TheDriverException $t) {
1301+
$shouldRollback = false;
1302+
1303+
throw $t;
12861304
} finally {
1287-
if ($this->isTransactionActive()) {
1305+
if ($shouldRollback) {
12881306
$this->rollBack();
12891307
}
12901308
}

tests/Functional/TransactionTest.php

+15-8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Doctrine\DBAL\Tests\Functional;
44

5+
use Doctrine\DBAL\Connection;
56
use Doctrine\DBAL\Driver\Exception as DriverException;
67
use Doctrine\DBAL\Platforms\AbstractMySQLPlatform;
78
use Doctrine\DBAL\Tests\FunctionalTestCase;
@@ -11,17 +12,12 @@
1112

1213
class TransactionTest extends FunctionalTestCase
1314
{
14-
protected function setUp(): void
15+
public function testCommitFalse(): void
1516
{
16-
if ($this->connection->getDatabasePlatform() instanceof AbstractMySQLPlatform) {
17-
return;
17+
if (! $this->connection->getDatabasePlatform() instanceof AbstractMySQLPlatform) {
18+
$this->markTestSkipped('Restricted to MySQL.');
1819
}
1920

20-
$this->markTestSkipped('Restricted to MySQL.');
21-
}
22-
23-
public function testCommitFalse(): void
24-
{
2521
$this->connection->executeStatement('SET SESSION wait_timeout=1');
2622

2723
self::assertTrue($this->connection->beginTransaction());
@@ -40,4 +36,15 @@ public function testCommitFalse(): void
4036
$this->connection->close();
4137
}
4238
}
39+
40+
public function testNestedTransactionWalkthrough(): void
41+
{
42+
$result = $this->connection->transactional(
43+
static fn (Connection $connection) => $connection->transactional(
44+
static fn (Connection $connection) => $connection->fetchOne('SELECT 1'),
45+
),
46+
);
47+
48+
self::assertSame('1', (string) $result);
49+
}
4350
}

0 commit comments

Comments
 (0)