Unverified Commit 045fbe80 authored by Marco Pivetta's avatar Marco Pivetta Committed by Sergei Morozov

Merge pull request #3480 from morozov/transaction-void

Transaction-related Statement methods return void
parents bcb17e0b 21905ad7
# Upgrade to 3.0 # Upgrade to 3.0
## BC BREAK Transaction-related `Statement` methods return `void`.
`Statement::beginTransaction()`, `::commit()` and `::rollBack()` no longer return a boolean value. They will throw a `DriverException` in case of failure.
## MINOR BC BREAK `Statement::fetchColumn()` with an invalid index. ## MINOR BC BREAK `Statement::fetchColumn()` with an invalid index.
Similarly to `PDOStatement::fetchColumn()`, DBAL statements throw an exception in case of an invalid column index. Similarly to `PDOStatement::fetchColumn()`, DBAL statements throw an exception in case of an invalid column index.
......
...@@ -9,6 +9,7 @@ use Doctrine\DBAL\Cache\CacheException; ...@@ -9,6 +9,7 @@ use Doctrine\DBAL\Cache\CacheException;
use Doctrine\DBAL\Cache\QueryCacheProfile; use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\DBAL\Cache\ResultCacheStatement; use Doctrine\DBAL\Cache\ResultCacheStatement;
use Doctrine\DBAL\Driver\Connection as DriverConnection; use Doctrine\DBAL\Driver\Connection as DriverConnection;
use Doctrine\DBAL\Driver\DriverException;
use Doctrine\DBAL\Driver\PingableConnection; use Doctrine\DBAL\Driver\PingableConnection;
use Doctrine\DBAL\Driver\ResultStatement; use Doctrine\DBAL\Driver\ResultStatement;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection; use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
...@@ -502,12 +503,11 @@ class Connection implements DriverConnection ...@@ -502,12 +503,11 @@ class Connection implements DriverConnection
* *
* @see isAutoCommit * @see isAutoCommit
* *
* @param bool $autoCommit True to enable auto-commit mode; false to disable it. * @throws ConnectionException
* @throws DriverException
*/ */
public function setAutoCommit($autoCommit) public function setAutoCommit(bool $autoCommit) : void
{ {
$autoCommit = (bool) $autoCommit;
// Mode not changed, no-op. // Mode not changed, no-op.
if ($autoCommit === $this->autoCommit) { if ($autoCommit === $this->autoCommit) {
return; return;
...@@ -1191,7 +1191,7 @@ class Connection implements DriverConnection ...@@ -1191,7 +1191,7 @@ class Connection implements DriverConnection
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function beginTransaction() public function beginTransaction() : void
{ {
$connection = $this->getWrappedConnection(); $connection = $this->getWrappedConnection();
...@@ -1201,15 +1201,17 @@ class Connection implements DriverConnection ...@@ -1201,15 +1201,17 @@ class Connection implements DriverConnection
if ($this->transactionNestingLevel === 1) { if ($this->transactionNestingLevel === 1) {
$logger->startQuery('"START TRANSACTION"'); $logger->startQuery('"START TRANSACTION"');
try {
$connection->beginTransaction(); $connection->beginTransaction();
} finally {
$logger->stopQuery(); $logger->stopQuery();
}
} elseif ($this->nestTransactionsWithSavepoints) { } elseif ($this->nestTransactionsWithSavepoints) {
$logger->startQuery('"SAVEPOINT"'); $logger->startQuery('"SAVEPOINT"');
$this->createSavepoint($this->_getNestedTransactionSavePointName()); $this->createSavepoint($this->_getNestedTransactionSavePointName());
$logger->stopQuery(); $logger->stopQuery();
} }
return true;
} }
/** /**
...@@ -1218,11 +1220,12 @@ class Connection implements DriverConnection ...@@ -1218,11 +1220,12 @@ class Connection implements DriverConnection
* @throws ConnectionException If the commit failed due to no active transaction or * @throws ConnectionException If the commit failed due to no active transaction or
* because the transaction was marked for rollback only. * because the transaction was marked for rollback only.
*/ */
public function commit() public function commit() : void
{ {
if ($this->transactionNestingLevel === 0) { if ($this->transactionNestingLevel === 0) {
throw ConnectionException::noActiveTransaction(); throw ConnectionException::noActiveTransaction();
} }
if ($this->isRollbackOnly) { if ($this->isRollbackOnly) {
throw ConnectionException::commitFailedRollbackOnly(); throw ConnectionException::commitFailedRollbackOnly();
} }
...@@ -1235,8 +1238,12 @@ class Connection implements DriverConnection ...@@ -1235,8 +1238,12 @@ class Connection implements DriverConnection
if ($this->transactionNestingLevel === 1) { if ($this->transactionNestingLevel === 1) {
$logger->startQuery('"COMMIT"'); $logger->startQuery('"COMMIT"');
$result = $connection->commit();
try {
$connection->commit();
} finally {
$logger->stopQuery(); $logger->stopQuery();
}
} elseif ($this->nestTransactionsWithSavepoints) { } elseif ($this->nestTransactionsWithSavepoints) {
$logger->startQuery('"RELEASE SAVEPOINT"'); $logger->startQuery('"RELEASE SAVEPOINT"');
$this->releaseSavepoint($this->_getNestedTransactionSavePointName()); $this->releaseSavepoint($this->_getNestedTransactionSavePointName());
...@@ -1246,18 +1253,19 @@ class Connection implements DriverConnection ...@@ -1246,18 +1253,19 @@ class Connection implements DriverConnection
--$this->transactionNestingLevel; --$this->transactionNestingLevel;
if ($this->autoCommit !== false || $this->transactionNestingLevel !== 0) { if ($this->autoCommit !== false || $this->transactionNestingLevel !== 0) {
return $result; return;
} }
$this->beginTransaction(); $this->beginTransaction();
return $result;
} }
/** /**
* Commits all current nesting transactions. * Commits all current nesting transactions.
*
* @throws ConnectionException
* @throws DriverException
*/ */
private function commitAll() private function commitAll() : void
{ {
while ($this->transactionNestingLevel !== 0) { while ($this->transactionNestingLevel !== 0) {
if ($this->autoCommit === false && $this->transactionNestingLevel === 1) { if ($this->autoCommit === false && $this->transactionNestingLevel === 1) {
...@@ -1273,11 +1281,11 @@ class Connection implements DriverConnection ...@@ -1273,11 +1281,11 @@ class Connection implements DriverConnection
} }
/** /**
* Cancels any database changes done during the current transaction. * {@inheritDoc}
* *
* @throws ConnectionException If the rollback operation failed. * @throws ConnectionException If the rollback operation failed.
*/ */
public function rollBack() public function rollBack() : void
{ {
if ($this->transactionNestingLevel === 0) { if ($this->transactionNestingLevel === 0) {
throw ConnectionException::noActiveTransaction(); throw ConnectionException::noActiveTransaction();
...@@ -1290,13 +1298,17 @@ class Connection implements DriverConnection ...@@ -1290,13 +1298,17 @@ class Connection implements DriverConnection
if ($this->transactionNestingLevel === 1) { if ($this->transactionNestingLevel === 1) {
$logger->startQuery('"ROLLBACK"'); $logger->startQuery('"ROLLBACK"');
$this->transactionNestingLevel = 0; $this->transactionNestingLevel = 0;
try {
$connection->rollBack(); $connection->rollBack();
} finally {
$this->isRollbackOnly = false; $this->isRollbackOnly = false;
$logger->stopQuery(); $logger->stopQuery();
if ($this->autoCommit === false) { if ($this->autoCommit === false) {
$this->beginTransaction(); $this->beginTransaction();
} }
}
} elseif ($this->nestTransactionsWithSavepoints) { } elseif ($this->nestTransactionsWithSavepoints) {
$logger->startQuery('"ROLLBACK TO SAVEPOINT"'); $logger->startQuery('"ROLLBACK TO SAVEPOINT"');
$this->rollbackSavepoint($this->_getNestedTransactionSavePointName()); $this->rollbackSavepoint($this->_getNestedTransactionSavePointName());
......
...@@ -230,31 +230,31 @@ class MasterSlaveConnection extends Connection ...@@ -230,31 +230,31 @@ class MasterSlaveConnection extends Connection
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function beginTransaction() public function beginTransaction() : void
{ {
$this->connect('master'); $this->connect('master');
return parent::beginTransaction(); parent::beginTransaction();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function commit() public function commit() : void
{ {
$this->connect('master'); $this->connect('master');
return parent::commit(); parent::commit();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function rollBack() public function rollBack() : void
{ {
$this->connect('master'); $this->connect('master');
return parent::rollBack(); parent::rollBack();
} }
/** /**
......
...@@ -54,23 +54,23 @@ interface Connection ...@@ -54,23 +54,23 @@ interface Connection
/** /**
* Initiates a transaction. * Initiates a transaction.
* *
* @return bool TRUE on success or FALSE on failure. * @throws DriverException
*/ */
public function beginTransaction(); public function beginTransaction() : void;
/** /**
* Commits a transaction. * Commits a transaction.
* *
* @return bool TRUE on success or FALSE on failure. * @throws DriverException
*/ */
public function commit(); public function commit() : void;
/** /**
* Rolls back the current transaction, as initiated by beginTransaction(). * Rolls back the current transaction, as initiated by beginTransaction().
* *
* @return bool TRUE on success or FALSE on failure. * @throws DriverException
*/ */
public function rollBack(); public function rollBack() : void;
/** /**
* Returns the error code associated with the last operation on the database handle. * Returns the error code associated with the last operation on the database handle.
......
...@@ -137,31 +137,39 @@ class DB2Connection implements Connection, ServerInfoAwareConnection ...@@ -137,31 +137,39 @@ class DB2Connection implements Connection, ServerInfoAwareConnection
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function beginTransaction() public function beginTransaction() : void
{ {
db2_autocommit($this->conn, DB2_AUTOCOMMIT_OFF); if (! db2_autocommit($this->conn, DB2_AUTOCOMMIT_OFF)) {
throw new DB2Exception(db2_conn_errormsg($this->conn));
}
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function commit() public function commit() : void
{ {
if (! db2_commit($this->conn)) { if (! db2_commit($this->conn)) {
throw new DB2Exception(db2_conn_errormsg($this->conn)); throw new DB2Exception(db2_conn_errormsg($this->conn));
} }
db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON);
if (! db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON)) {
throw new DB2Exception(db2_conn_errormsg($this->conn));
}
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function rollBack() public function rollBack() : void
{ {
if (! db2_rollback($this->conn)) { if (! db2_rollback($this->conn)) {
throw new DB2Exception(db2_conn_errormsg($this->conn)); throw new DB2Exception(db2_conn_errormsg($this->conn));
} }
db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON);
if (! db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON)) {
throw new DB2Exception(db2_conn_errormsg($this->conn));
}
} }
/** /**
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
namespace Doctrine\DBAL\Driver\IBMDB2; namespace Doctrine\DBAL\Driver\IBMDB2;
use Exception; use Doctrine\DBAL\Driver\AbstractDriverException;
class DB2Exception extends Exception class DB2Exception extends AbstractDriverException
{ {
} }
...@@ -174,27 +174,29 @@ class MysqliConnection implements Connection, PingableConnection, ServerInfoAwar ...@@ -174,27 +174,29 @@ class MysqliConnection implements Connection, PingableConnection, ServerInfoAwar
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function beginTransaction() public function beginTransaction() : void
{ {
$this->conn->query('START TRANSACTION'); $this->conn->query('START TRANSACTION');
return true;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function commit() public function commit() : void
{ {
return $this->conn->commit(); if (! $this->conn->commit()) {
throw new MysqliException($this->conn->error, $this->conn->sqlstate, $this->conn->errno);
}
} }
/** /**
* {@inheritdoc}non-PHPdoc) * {@inheritdoc}
*/ */
public function rollBack() public function rollBack() : void
{ {
return $this->conn->rollback(); if (! $this->conn->rollback()) {
throw new MysqliException($this->conn->error, $this->conn->sqlstate, $this->conn->errno);
}
} }
/** /**
......
...@@ -177,37 +177,33 @@ class OCI8Connection implements Connection, ServerInfoAwareConnection ...@@ -177,37 +177,33 @@ class OCI8Connection implements Connection, ServerInfoAwareConnection
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function beginTransaction() public function beginTransaction() : void
{ {
$this->executeMode = OCI_NO_AUTO_COMMIT; $this->executeMode = OCI_NO_AUTO_COMMIT;
return true;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function commit() public function commit() : void
{ {
if (! oci_commit($this->dbh)) { if (! oci_commit($this->dbh)) {
throw OCI8Exception::fromErrorInfo($this->errorInfo()); throw OCI8Exception::fromErrorInfo($this->errorInfo());
} }
$this->executeMode = OCI_COMMIT_ON_SUCCESS;
return true; $this->executeMode = OCI_COMMIT_ON_SUCCESS;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function rollBack() public function rollBack() : void
{ {
if (! oci_rollback($this->dbh)) { if (! oci_rollback($this->dbh)) {
throw OCI8Exception::fromErrorInfo($this->errorInfo()); throw OCI8Exception::fromErrorInfo($this->errorInfo());
} }
$this->executeMode = OCI_COMMIT_ON_SUCCESS;
return true; $this->executeMode = OCI_COMMIT_ON_SUCCESS;
} }
/** /**
......
...@@ -126,25 +126,25 @@ class PDOConnection implements Connection, ServerInfoAwareConnection ...@@ -126,25 +126,25 @@ class PDOConnection implements Connection, ServerInfoAwareConnection
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function beginTransaction() public function beginTransaction() : void
{ {
return $this->connection->beginTransaction(); $this->connection->beginTransaction();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function commit() public function commit() : void
{ {
return $this->connection->commit(); $this->connection->commit();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function rollBack() public function rollBack() : void
{ {
return $this->connection->rollBack(); $this->connection->rollBack();
} }
/** /**
......
...@@ -64,13 +64,11 @@ class SQLAnywhereConnection implements Connection, ServerInfoAwareConnection ...@@ -64,13 +64,11 @@ class SQLAnywhereConnection implements Connection, ServerInfoAwareConnection
* *
* @throws SQLAnywhereException * @throws SQLAnywhereException
*/ */
public function beginTransaction() public function beginTransaction() : void
{ {
if (! sasql_set_option($this->connection, 'auto_commit', 'off')) { if (! sasql_set_option($this->connection, 'auto_commit', 'off')) {
throw SQLAnywhereException::fromSQLAnywhereError($this->connection); throw SQLAnywhereException::fromSQLAnywhereError($this->connection);
} }
return true;
} }
/** /**
...@@ -78,15 +76,13 @@ class SQLAnywhereConnection implements Connection, ServerInfoAwareConnection ...@@ -78,15 +76,13 @@ class SQLAnywhereConnection implements Connection, ServerInfoAwareConnection
* *
* @throws SQLAnywhereException * @throws SQLAnywhereException
*/ */
public function commit() public function commit() : void
{ {
if (! sasql_commit($this->connection)) { if (! sasql_commit($this->connection)) {
throw SQLAnywhereException::fromSQLAnywhereError($this->connection); throw SQLAnywhereException::fromSQLAnywhereError($this->connection);
} }
$this->endTransaction(); $this->endTransaction();
return true;
} }
/** /**
...@@ -185,30 +181,24 @@ class SQLAnywhereConnection implements Connection, ServerInfoAwareConnection ...@@ -185,30 +181,24 @@ class SQLAnywhereConnection implements Connection, ServerInfoAwareConnection
* *
* @throws SQLAnywhereException * @throws SQLAnywhereException
*/ */
public function rollBack() public function rollBack() : void
{ {
if (! sasql_rollback($this->connection)) { if (! sasql_rollback($this->connection)) {
throw SQLAnywhereException::fromSQLAnywhereError($this->connection); throw SQLAnywhereException::fromSQLAnywhereError($this->connection);
} }
$this->endTransaction(); $this->endTransaction();
return true;
} }
/** /**
* Ends transactional mode and enables auto commit again. * Ends transactional mode and enables auto commit again.
* *
* @return bool Whether or not ending transactional mode succeeded.
*
* @throws SQLAnywhereException * @throws SQLAnywhereException
*/ */
private function endTransaction() private function endTransaction() : void
{ {
if (! sasql_set_option($this->connection, 'auto_commit', 'on')) { if (! sasql_set_option($this->connection, 'auto_commit', 'on')) {
throw SQLAnywhereException::fromSQLAnywhereError($this->connection); throw SQLAnywhereException::fromSQLAnywhereError($this->connection);
} }
return true;
} }
} }
...@@ -146,7 +146,7 @@ class SQLSrvConnection implements Connection, ServerInfoAwareConnection ...@@ -146,7 +146,7 @@ class SQLSrvConnection implements Connection, ServerInfoAwareConnection
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function beginTransaction() public function beginTransaction() : void
{ {
if (! sqlsrv_begin_transaction($this->conn)) { if (! sqlsrv_begin_transaction($this->conn)) {
throw SQLSrvException::fromSqlSrvErrors(); throw SQLSrvException::fromSqlSrvErrors();
...@@ -156,7 +156,7 @@ class SQLSrvConnection implements Connection, ServerInfoAwareConnection ...@@ -156,7 +156,7 @@ class SQLSrvConnection implements Connection, ServerInfoAwareConnection
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function commit() public function commit() : void
{ {
if (! sqlsrv_commit($this->conn)) { if (! sqlsrv_commit($this->conn)) {
throw SQLSrvException::fromSqlSrvErrors(); throw SQLSrvException::fromSqlSrvErrors();
...@@ -166,7 +166,7 @@ class SQLSrvConnection implements Connection, ServerInfoAwareConnection ...@@ -166,7 +166,7 @@ class SQLSrvConnection implements Connection, ServerInfoAwareConnection
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function rollBack() public function rollBack() : void
{ {
if (! sqlsrv_rollback($this->conn)) { if (! sqlsrv_rollback($this->conn)) {
throw SQLSrvException::fromSqlSrvErrors(); throw SQLSrvException::fromSqlSrvErrors();
......
...@@ -251,8 +251,6 @@ class ConnectionTest extends DbalTestCase ...@@ -251,8 +251,6 @@ class ConnectionTest extends DbalTestCase
{ {
$this->connection->setAutoCommit(false); $this->connection->setAutoCommit(false);
self::assertFalse($this->connection->isAutoCommit()); self::assertFalse($this->connection->isAutoCommit());
$this->connection->setAutoCommit(0);
self::assertFalse($this->connection->isAutoCommit());
} }
/** /**
...@@ -297,28 +295,6 @@ class ConnectionTest extends DbalTestCase ...@@ -297,28 +295,6 @@ class ConnectionTest extends DbalTestCase
self::assertTrue($conn->isTransactionActive()); self::assertTrue($conn->isTransactionActive());
} }
/**
* @dataProvider resultProvider
*/
public function testCommitReturn(bool $expectedResult) : void
{
$driverConnection = $this->createMock(DriverConnection::class);
$driverConnection->expects($this->once())
->method('commit')->willReturn($expectedResult);
$driverMock = $this->createMock(Driver::class);
$driverMock->expects($this->any())
->method('connect')
->will($this->returnValue($driverConnection));
$conn = new Connection([], $driverMock);
$conn->connect();
$conn->beginTransaction();
self::assertSame($expectedResult, $conn->commit());
}
/** /**
* @return bool[][] * @return bool[][]
*/ */
......
...@@ -122,7 +122,7 @@ class ConnectionTest extends DbalFunctionalTestCase ...@@ -122,7 +122,7 @@ class ConnectionTest extends DbalFunctionalTestCase
self::assertEquals(2, $this->connection->getTransactionNestingLevel()); self::assertEquals(2, $this->connection->getTransactionNestingLevel());
$this->connection->beginTransaction(); $this->connection->beginTransaction();
self::assertEquals(3, $this->connection->getTransactionNestingLevel()); self::assertEquals(3, $this->connection->getTransactionNestingLevel());
self::assertTrue($this->connection->commit()); $this->connection->commit();
self::assertEquals(2, $this->connection->getTransactionNestingLevel()); self::assertEquals(2, $this->connection->getTransactionNestingLevel());
throw new Exception(); throw new Exception();
$this->connection->commit(); // never reached $this->connection->commit(); // never reached
......
<?php
namespace Doctrine\Tests\DBAL\Functional;
use Doctrine\DBAL\Platforms\MySqlPlatform;
use Doctrine\Tests\DbalFunctionalTestCase;
use function sleep;
class TransactionTest extends DbalFunctionalTestCase
{
protected function setUp() : void
{
parent::setUp();
if ($this->connection->getDatabasePlatform() instanceof MySqlPlatform) {
return;
}
$this->markTestSkipped('Restricted to MySQL.');
}
protected function tearDown() : void
{
$this->resetSharedConn();
parent::tearDown();
}
public function testCommitFalse() : void
{
$this->connection->query('SET SESSION wait_timeout=1');
$this->assertTrue($this->connection->beginTransaction());
sleep(2); // during the sleep mysql will close the connection
$this->assertFalse(@$this->connection->commit()); // we will ignore `MySQL server has gone away` warnings
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment