Skip to content

Commit 2309310

Browse files
committed
refactor(transaction): inherit postgres startTransaction
1 parent acc0c1c commit 2309310

3 files changed

Lines changed: 38 additions & 41 deletions

File tree

src/Database/Adapter/Postgres.php

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -31,44 +31,6 @@
3131
class Postgres extends SQL
3232
{
3333
public const MAX_IDENTIFIER_NAME = 63;
34-
/**
35-
* @inheritDoc
36-
*/
37-
public function startTransaction(): bool
38-
{
39-
try {
40-
if ($this->inTransaction === 0) {
41-
try {
42-
if ($this->getPDO()->inTransaction()) {
43-
$this->getPDO()->rollBack();
44-
} else {
45-
// If no active transaction, this has no effect.
46-
$this->getPDO()->prepare('ROLLBACK')->execute();
47-
}
48-
} catch (PDOException) {
49-
// A pooled connection can report a transaction it no longer
50-
// holds after a reconnect (e.g. Swoole PDOProxy keeps its own
51-
// counter), making this cleanup rollback throw. It is best
52-
// effort; swallow it and begin a fresh transaction below.
53-
}
54-
55-
$result = $this->getPDO()->beginTransaction();
56-
} else {
57-
$this->getPDO()->exec('SAVEPOINT transaction' . $this->inTransaction);
58-
$result = true;
59-
}
60-
} catch (PDOException $e) {
61-
throw new TransactionException('Failed to start transaction: ' . $e->getMessage(), $e->getCode(), $e);
62-
}
63-
64-
if (!$result) {
65-
throw new TransactionException('Failed to start transaction');
66-
}
67-
68-
$this->inTransaction++;
69-
70-
return $result;
71-
}
7234

7335
/**
7436
* @inheritDoc

src/Database/Adapter/SQL.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,18 +94,23 @@ public function startTransaction(): bool
9494
// effort; swallow it and begin a fresh transaction below.
9595
}
9696

97-
$this->getPDO()->beginTransaction();
97+
$result = $this->getPDO()->beginTransaction();
9898

9999
} else {
100100
$this->getPDO()->exec('SAVEPOINT transaction' . $this->inTransaction);
101+
$result = true;
101102
}
102103
} catch (PDOException $e) {
103104
throw new TransactionException('Failed to start transaction: ' . $e->getMessage(), $e->getCode(), $e);
104105
}
105106

107+
if (!$result) {
108+
throw new TransactionException('Failed to start transaction');
109+
}
110+
106111
$this->inTransaction++;
107112

108-
return true;
113+
return $result;
109114
}
110115

111116
/**

tests/unit/SQLTransactionTest.php

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44

55
use PDOException;
66
use PHPUnit\Framework\TestCase;
7+
use ReflectionMethod;
78
use Utopia\Database\Adapter\MySQL;
89
use Utopia\Database\Adapter\Postgres;
10+
use Utopia\Database\Adapter\SQL;
911
use Utopia\Database\Exception\Transaction as TransactionException;
1012

11-
class SQLTransactionTest extends TestCase
13+
final class SQLTransactionTest extends TestCase
1214
{
1315
/**
1416
* A pooled connection (e.g. Swoole PDOProxy) can keep its own transaction
@@ -59,8 +61,36 @@ public function testStartTransactionDoesNotMaskBeginFailureAfterDesyncedRollback
5961
$adapter->startTransaction();
6062
}
6163

64+
public function testStartTransactionDoesNotMaskFalseBeginResult(): void
65+
{
66+
$pdo = $this->getMockBuilder(\PDO::class)
67+
->disableOriginalConstructor()
68+
->getMock();
69+
70+
$pdo->method('inTransaction')->willReturn(false);
71+
$pdo->expects($this->once())
72+
->method('beginTransaction')
73+
->willReturn(false);
74+
75+
$cleanup = $this->getMockBuilder(\PDOStatement::class)
76+
->disableOriginalConstructor()
77+
->getMock();
78+
$cleanup->method('execute')->willReturn(true);
79+
$pdo->method('prepare')->with('ROLLBACK')->willReturn($cleanup);
80+
81+
$adapter = new MySQL($pdo);
82+
83+
$this->expectException(TransactionException::class);
84+
$this->expectExceptionMessage('Failed to start transaction');
85+
86+
$adapter->startTransaction();
87+
}
88+
6289
public function testPostgresStartTransactionRecoversFromDesyncedRollback(): void
6390
{
91+
$method = new ReflectionMethod(Postgres::class, 'startTransaction');
92+
$this->assertSame(SQL::class, $method->getDeclaringClass()->getName());
93+
6494
$pdo = $this->getMockBuilder(\PDO::class)
6595
->disableOriginalConstructor()
6696
->getMock();

0 commit comments

Comments
 (0)