Commit 68596ba3 authored by Steve Müller's avatar Steve Müller

add possibility to set the auto-commit mode for a connection

parent a57e05a9
...@@ -117,6 +117,13 @@ class Connection implements DriverConnection ...@@ -117,6 +117,13 @@ class Connection implements DriverConnection
*/ */
private $_isConnected = false; private $_isConnected = false;
/**
* Whether to automatically commit DML and DDL statements.
*
* @var boolean
*/
private $autoCommit = true;
/** /**
* The transaction nesting level. * The transaction nesting level.
* *
...@@ -356,6 +363,10 @@ class Connection implements DriverConnection ...@@ -356,6 +363,10 @@ class Connection implements DriverConnection
$this->_conn = $this->_driver->connect($this->_params, $user, $password, $driverOptions); $this->_conn = $this->_driver->connect($this->_params, $user, $password, $driverOptions);
$this->_isConnected = true; $this->_isConnected = true;
if (false === $this->autoCommit) {
$this->beginTransaction();
}
if ($this->_eventManager->hasListeners(Events::postConnect)) { if ($this->_eventManager->hasListeners(Events::postConnect)) {
$eventArgs = new Event\ConnectionEventArgs($this); $eventArgs = new Event\ConnectionEventArgs($this);
$this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs);
...@@ -364,6 +375,49 @@ class Connection implements DriverConnection ...@@ -364,6 +375,49 @@ class Connection implements DriverConnection
return true; return true;
} }
/**
* Returns the current auto-commit mode for this connection.
*
* @return boolean True if auto-commit mode is currently enabled for this connection, false otherwise.
*
* @see setAutoCommit
*/
public function getAutoCommit()
{
return $this->autoCommit;
}
/**
* Sets auto-commit mode for this connection.
*
* If a connection is in auto-commit mode, then all its SQL statements will be executed and committed as individual
* transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either
* the method commit or the method rollback. By default, new connections are in auto-commit mode.
*
* NOTE: If this method is called during a transaction and the auto-commit mode is changed, the transaction is
* committed. If this method is called and the auto-commit mode is not changed, the call is a no-op.
*
* @param boolean $autoCommit True to enable auto-commit mode; false to disable it.
*
* @see getAutoCommit
*/
public function setAutoCommit($autoCommit)
{
$autoCommit = (boolean) $autoCommit;
// Mode not changed, no-op.
if ($autoCommit === $this->autoCommit) {
return;
}
$this->autoCommit = $autoCommit;
// Commit all currently active transactions if any when switching auto-commit mode.
if (true === $this->_isConnected && 0 !== $this->_transactionNestingLevel) {
$this->commitAll();
}
}
/** /**
* Sets the fetch mode. * Sets the fetch mode.
* *
...@@ -1095,6 +1149,28 @@ class Connection implements DriverConnection ...@@ -1095,6 +1149,28 @@ class Connection implements DriverConnection
} }
--$this->_transactionNestingLevel; --$this->_transactionNestingLevel;
if (false === $this->autoCommit && 0 === $this->_transactionNestingLevel) {
$this->beginTransaction();
}
}
/**
* Commits all current nesting transactions.
*/
public function commitAll()
{
while (0 !== $this->_transactionNestingLevel) {
if (false === $this->autoCommit && 1 === $this->_transactionNestingLevel) {
// When in no auto-commit mode, the last nesting commit immediately starts a new transaction.
// Therefore we need to do the final commit here and then leave to avoid an infinite loop.
$this->commit();
return;
}
$this->commit();
}
} }
/** /**
...@@ -1125,6 +1201,10 @@ class Connection implements DriverConnection ...@@ -1125,6 +1201,10 @@ class Connection implements DriverConnection
if ($logger) { if ($logger) {
$logger->stopQuery(); $logger->stopQuery();
} }
if (false === $this->autoCommit) {
$this->beginTransaction();
}
} else if ($this->_nestTransactionsWithSavepoints) { } else if ($this->_nestTransactionsWithSavepoints) {
if ($logger) { if ($logger) {
$logger->startQuery('"ROLLBACK TO SAVEPOINT"'); $logger->startQuery('"ROLLBACK TO SAVEPOINT"');
...@@ -1140,6 +1220,24 @@ class Connection implements DriverConnection ...@@ -1140,6 +1220,24 @@ class Connection implements DriverConnection
} }
} }
/**
* Cancel any database changes done during all current nesting transactions.
*/
public function rollBackAll()
{
while (0 !== $this->_transactionNestingLevel) {
if (false === $this->autoCommit && 1 === $this->_transactionNestingLevel) {
// When in no auto-commit mode, the last nesting commit immediately starts a new transaction.
// Therefore we need to do the final commit here and then leave to avoid an infinite loop.
$this->rollBack();
return;
}
$this->rollBack();
}
}
/** /**
* Creates a new savepoint. * Creates a new savepoint.
* *
......
...@@ -8,6 +8,7 @@ use Doctrine\DBAL\Connection; ...@@ -8,6 +8,7 @@ use Doctrine\DBAL\Connection;
use Doctrine\Common\EventManager; use Doctrine\Common\EventManager;
use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Events; use Doctrine\DBAL\Events;
use Doctrine\Tests\Mocks\DriverConnectionMock;
class ConnectionTest extends \Doctrine\Tests\DbalTestCase class ConnectionTest extends \Doctrine\Tests\DbalTestCase
{ {
...@@ -174,4 +175,148 @@ SQLSTATE[HY000]: General error: 1 near \"MUUHAAAAHAAAA\""); ...@@ -174,4 +175,148 @@ SQLSTATE[HY000]: General error: 1 near \"MUUHAAAAHAAAA\"");
$this->_conn->getConfiguration()->setSQLLogger($logger); $this->_conn->getConfiguration()->setSQLLogger($logger);
$this->assertSame($logger, $this->_conn->getConfiguration()->getSQLLogger()); $this->assertSame($logger, $this->_conn->getConfiguration()->getSQLLogger());
} }
/**
* @group DBAL-81
*/
public function testCommitAll()
{
$driverConnectionMock = $this->getMock('Doctrine\DBAL\Driver\Connection');
$driverConnectionMock->expects($this->once())
->method('commit');
$driverMock = $this->getMock('Doctrine\DBAL\Driver');
$driverMock->expects($this->any())
->method('connect')
->will($this->returnValue($driverConnectionMock));
$conn = new Connection(array('platform' => new Mocks\MockPlatform()), $driverMock);
$conn->connect();
$conn->beginTransaction();
$conn->beginTransaction();
$conn->commitAll();
$this->assertFalse($conn->isTransactionActive());
}
/**
* @group DBAL-81
*/
public function testRollBackAll()
{
$driverConnectionMock = $this->getMock('Doctrine\DBAL\Driver\Connection');
$driverConnectionMock->expects($this->once())
->method('rollback');
$driverMock = $this->getMock('Doctrine\DBAL\Driver');
$driverMock->expects($this->any())
->method('connect')
->will($this->returnValue($driverConnectionMock));
$conn = new Connection(array('platform' => new Mocks\MockPlatform()), $driverMock);
$conn->connect();
$conn->beginTransaction();
$conn->beginTransaction();
$conn->rollBackAll();
$this->assertFalse($conn->isTransactionActive());
}
/**
* @group DBAL-81
*/
public function testGetAutoCommit()
{
$this->assertTrue($this->_conn->getAutoCommit());
}
/**
* @group DBAL-81
*/
public function testSetAutoCommit()
{
$this->_conn->setAutoCommit(false);
$this->assertFalse($this->_conn->getAutoCommit());
$this->_conn->setAutoCommit(0);
$this->assertFalse($this->_conn->getAutoCommit());
}
/**
* @group DBAL-81
*/
public function testConnectStartsTransactionInNoAutoCommitMode()
{
$driverMock = $this->getMock('Doctrine\DBAL\Driver');
$driverMock->expects($this->any())
->method('connect')
->will($this->returnValue(new DriverConnectionMock()));
$conn = new Connection(array('platform' => new Mocks\MockPlatform()), $driverMock);
$conn->setAutoCommit(false);
$this->assertFalse($conn->isTransactionActive());
$conn->connect();
$this->assertTrue($conn->isTransactionActive());
}
/**
* @group DBAL-81
*/
public function testCommitStartsTransactionInNoAutoCommitMode()
{
$driverMock = $this->getMock('Doctrine\DBAL\Driver');
$driverMock->expects($this->any())
->method('connect')
->will($this->returnValue(new DriverConnectionMock()));
$conn = new Connection(array('platform' => new Mocks\MockPlatform()), $driverMock);
$conn->setAutoCommit(false);
$conn->connect();
$conn->commit();
$this->assertTrue($conn->isTransactionActive());
}
/**
* @group DBAL-81
*/
public function testRollBackStartsTransactionInNoAutoCommitMode()
{
$driverMock = $this->getMock('Doctrine\DBAL\Driver');
$driverMock->expects($this->any())
->method('connect')
->will($this->returnValue(new DriverConnectionMock()));
$conn = new Connection(array('platform' => new Mocks\MockPlatform()), $driverMock);
$conn->setAutoCommit(false);
$conn->connect();
$conn->rollBack();
$this->assertTrue($conn->isTransactionActive());
}
/**
* @group DBAL-81
*/
public function testSwitchingAutoCommitModeCommitsAllCurrentTransactions()
{
$driverMock = $this->getMock('Doctrine\DBAL\Driver');
$driverMock->expects($this->any())
->method('connect')
->will($this->returnValue(new DriverConnectionMock()));
$conn = new Connection(array('platform' => new Mocks\MockPlatform()), $driverMock);
$conn->connect();
$conn->beginTransaction();
$conn->beginTransaction();
$conn->setAutoCommit(false);
$this->assertSame(1, $conn->getTransactionNestingLevel());
$conn->beginTransaction();
$conn->beginTransaction();
$conn->setAutoCommit(true);
$this->assertFalse($conn->isTransactionActive());
}
} }
\ No newline at end of file
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