diff --git a/src/Internal/PostgresHandleConnection.php b/src/Internal/PostgresHandleConnection.php index 9a6cc57..1fa5f9f 100644 --- a/src/Internal/PostgresHandleConnection.php +++ b/src/Internal/PostgresHandleConnection.php @@ -135,6 +135,7 @@ final public function listen(string $channel): PostgresListener #[\Override] final public function beginTransaction(): PostgresTransaction { + $this->awaitPending(); $this->reserve(); try { diff --git a/test/TransactionReleaseRaceTest.php b/test/TransactionReleaseRaceTest.php new file mode 100644 index 0000000..acf12e1 --- /dev/null +++ b/test/TransactionReleaseRaceTest.php @@ -0,0 +1,175 @@ +beginTransaction(); + $transaction->commit(); + + $transaction = $connection->beginTransaction(); + $transaction->commit(); + + delay(0); + + $this->expectNotToPerformAssertions(); + } + + private static function createConnection(): PostgresConnection + { + $reflection = new \ReflectionClass(RaceTestConnection::class); + $connection = $reflection->newInstanceWithoutConstructor(); + $reflection->getConstructor()->invoke($connection, new RaceTestHandle()); + + return $connection; + } +} + +final class RaceTestConnection extends PostgresHandleConnection +{ + public static function connect(PostgresConfig $config, ?Cancellation $cancellation = null): self + { + throw new SqlConnectionException('Not used in this test'); + } +} + +final class RaceTestHandle implements PostgresHandle +{ + public function close(): void + { + } + + public function commit(): void + { + } + + public function createSavepoint(string $identifier): void + { + } + + public function escapeByteA(string $data): string + { + return $data; + } + + public function execute(string $sql, array $params = []): PostgresResult + { + return new RaceTestResult(); + } + + public function getConfig(): PostgresConfig + { + return new PostgresConfig(host: 'localhost'); + } + + public function getLastUsedAt(): int + { + return 0; + } + + public function isClosed(): bool + { + return false; + } + + public function listen(string $channel): PostgresListener + { + throw new \BadMethodCallException('Not used in this test'); + } + + public function notify(string $channel, string $payload = ""): PostgresResult + { + return new RaceTestResult(); + } + + public function onClose(\Closure $onClose): void + { + EventLoop::queue($onClose); + } + + public function prepare(string $sql): PostgresStatement + { + throw new \BadMethodCallException('Not used in this test'); + } + + public function query(string $sql): PostgresResult + { + return new RaceTestResult(); + } + + public function quoteIdentifier(string $name): string + { + return $name; + } + + public function quoteLiteral(string $data): string + { + return $data; + } + + public function releaseSavepoint(string $identifier): void + { + } + + public function rollback(): void + { + } + + public function rollbackTo(string $identifier): void + { + } + + public function statementDeallocate(string $name): void + { + } + + public function statementExecute(string $name, array $params): PostgresResult + { + return new RaceTestResult(); + } +} + +final class RaceTestResult implements PostgresResult, \IteratorAggregate +{ + public function fetchRow(): ?array + { + return null; + } + + public function getColumnCount(): ?int + { + return null; + } + + public function getIterator(): \Traversable + { + return new \EmptyIterator(); + } + + public function getNextResult(): ?PostgresResult + { + return null; + } + + public function getRowCount(): ?int + { + return null; + } +}