diff --git a/lib/Doctrine/Migrations/Tools/TransactionHelper.php b/lib/Doctrine/Migrations/Tools/TransactionHelper.php index 866c591ec4..acfceeabea 100644 --- a/lib/Doctrine/Migrations/Tools/TransactionHelper.php +++ b/lib/Doctrine/Migrations/Tools/TransactionHelper.php @@ -5,7 +5,8 @@ namespace Doctrine\Migrations\Tools; use Doctrine\DBAL\Connection; -use PDO; + +use function method_exists; /** * @internal @@ -36,6 +37,10 @@ private static function inTransaction(Connection $connection): bool /* Attempt to commit or rollback while no transaction is running results in an exception since PHP 8 + pdo_mysql combination */ - return ! $wrappedConnection instanceof PDO || $wrappedConnection->inTransaction(); + if (method_exists($wrappedConnection, 'inTransaction')) { + return $wrappedConnection->inTransaction(); + } + + return true; } } diff --git a/tests/Doctrine/Migrations/Tests/Event/Listeners/AutoCommitListenerTest.php b/tests/Doctrine/Migrations/Tests/Event/Listeners/AutoCommitListenerTest.php index 8167315aee..7ce41503c0 100644 --- a/tests/Doctrine/Migrations/Tests/Event/Listeners/AutoCommitListenerTest.php +++ b/tests/Doctrine/Migrations/Tests/Event/Listeners/AutoCommitListenerTest.php @@ -5,6 +5,7 @@ namespace Doctrine\Migrations\Tests\Event\Listeners; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\Connection as DriverConnection; use Doctrine\Migrations\Configuration\Configuration; use Doctrine\Migrations\Event\Listeners\AutoCommitListener; use Doctrine\Migrations\Event\MigrationsEventArgs; @@ -38,9 +39,14 @@ public function testListenerDoesNothingWhenConnecitonAutoCommitIsOn(): void public function testListenerDoesFinalCommitWhenAutoCommitIsOff(): void { + $this->conn->expects(self::once()) + ->method('getWrappedConnection') + ->willReturn($this->createMock(DriverConnection::class)); + $this->conn->expects(self::once()) ->method('isAutoCommit') ->willReturn(false); + $this->conn->expects(self::once()) ->method('commit'); diff --git a/tests/Doctrine/Migrations/Tests/Tools/TransactionHelperTest.php b/tests/Doctrine/Migrations/Tests/Tools/TransactionHelperTest.php new file mode 100644 index 0000000000..716ca8e7d2 --- /dev/null +++ b/tests/Doctrine/Migrations/Tests/Tools/TransactionHelperTest.php @@ -0,0 +1,97 @@ +createMock($driverConnectionFqcn); + $wrappedConnection->expects(self::once()) + ->method('inTransaction') + ->willReturn($commitExpected); + + $connection = $this->createMock(Connection::class); + $connection->expects(self::once()) + ->method('getWrappedConnection') + ->willReturn($wrappedConnection); + + $connection->expects($commitExpected ? self::once() : self::never()) + ->method('commit'); + + TransactionHelper::commitIfInTransaction($connection); + } + + public function testCommitIfInTransactionWithConnectionNotImplementingInTransactionMethod(): void + { + $connection = $this->createMock(Connection::class); + $connection->expects(self::once()) + ->method('getWrappedConnection') + ->willReturn($this->createMock(DriverConnection::class)); + + $connection->expects(self::once()) + ->method('commit'); + + TransactionHelper::commitIfInTransaction($connection); + } + + /** + * @param class-string $driverConnectionFqcn + * + * @dataProvider getDriverConnectionClassesImplementingInTransactionMethod + */ + public function testRollbackIfInTransactionWithConnectionImplementingInTransactionMethod(string $driverConnectionFqcn, bool $commitExpected): void + { + $wrappedConnection = $this->createMock($driverConnectionFqcn); + $wrappedConnection->expects(self::once()) + ->method('inTransaction') + ->willReturn($commitExpected); + + $connection = $this->createMock(Connection::class); + $connection->expects(self::once()) + ->method('getWrappedConnection') + ->willReturn($wrappedConnection); + + $connection->expects($commitExpected ? self::once() : self::never()) + ->method('rollback'); + + TransactionHelper::rollbackIfInTransaction($connection); + } + + public function testRollbackIfInTransactionWithConnectionNotImplementingInTransactionMethod(): void + { + $connection = $this->createMock(Connection::class); + $connection->expects(self::once()) + ->method('getWrappedConnection') + ->willReturn($this->createMock(DriverConnection::class)); + + $connection->expects(self::once()) + ->method('rollback'); + + TransactionHelper::rollbackIfInTransaction($connection); + } + + /** + * @return list, bool}> + */ + public function getDriverConnectionClassesImplementingInTransactionMethod(): array + { + return [ + [PDOConnection::class, true], + [PDOConnection::class, false], + ]; + } +} diff --git a/tests/Doctrine/Migrations/Tests/Tracking/TableUpdaterTest.php b/tests/Doctrine/Migrations/Tests/Tracking/TableUpdaterTest.php index 1035be0e99..71c213bc01 100644 --- a/tests/Doctrine/Migrations/Tests/Tracking/TableUpdaterTest.php +++ b/tests/Doctrine/Migrations/Tests/Tracking/TableUpdaterTest.php @@ -5,6 +5,7 @@ namespace Doctrine\Migrations\Tests\Tracking; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\Connection as DriverConnection; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\Column; @@ -96,6 +97,10 @@ public function testUpdateMigrationTable(): void ->with($toSchema, $this->platform) ->willReturn(['ALTER TABLE table_name ADD COLUMN executed_at DATETIME DEFAULT NULL']); + $this->connection->expects(self::once()) + ->method('getWrappedConnection') + ->willReturn($this->createMock(DriverConnection::class)); + $this->connection->expects(self::once()) ->method('beginTransaction'); @@ -173,6 +178,10 @@ public function testUpdateMigrationTableRollback(): void ->with($toSchema, $this->platform) ->willReturn(['ALTER TABLE table_name ADD COLUMN executed_at DATETIME DEFAULT NULL']); + $this->connection->expects(self::once()) + ->method('getWrappedConnection') + ->willReturn($this->createMock(DriverConnection::class)); + $this->connection->expects(self::once()) ->method('beginTransaction'); diff --git a/tests/Doctrine/Migrations/Tests/Version/ExecutorTest.php b/tests/Doctrine/Migrations/Tests/Version/ExecutorTest.php index b8d11b6e13..2768240909 100644 --- a/tests/Doctrine/Migrations/Tests/Version/ExecutorTest.php +++ b/tests/Doctrine/Migrations/Tests/Version/ExecutorTest.php @@ -5,6 +5,7 @@ namespace Doctrine\Migrations\Tests\Version; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\Connection as DriverConnection; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\AbstractMigration; @@ -63,6 +64,10 @@ public function testAddSql(): void public function testExecuteUp(): void { + $this->connection->expects(self::once()) + ->method('getWrappedConnection') + ->willReturn($this->createMock(DriverConnection::class)); + $this->outputWriter->expects(self::at(0)) ->method('write') ->with("\n ++ migrating 001\n"); @@ -113,6 +118,10 @@ public function testExecuteUp(): void */ public function executeUpShouldAppendDescriptionWhenItIsNotEmpty(): void { + $this->connection->expects(self::once()) + ->method('getWrappedConnection') + ->willReturn($this->createMock(DriverConnection::class)); + $this->outputWriter->expects(self::at(0)) ->method('write') ->with("\n ++ migrating 001 (testing)\n"); @@ -130,6 +139,10 @@ public function executeUpShouldAppendDescriptionWhenItIsNotEmpty(): void public function testExecuteDown(): void { + $this->connection->expects(self::once()) + ->method('getWrappedConnection') + ->willReturn($this->createMock(DriverConnection::class)); + $this->outputWriter->expects(self::at(0)) ->method('write') ->with("\n -- reverting 001\n"); @@ -180,6 +193,10 @@ public function testExecuteDown(): void */ public function executeDownShouldAppendDescriptionWhenItIsNotEmpty(): void { + $this->connection->expects(self::once()) + ->method('getWrappedConnection') + ->willReturn($this->createMock(DriverConnection::class)); + $this->outputWriter->expects(self::at(0)) ->method('write') ->with("\n -- reverting 001 (testing)\n");