Commit ec003aad authored by Lukas Kahwe Smith's avatar Lukas Kahwe Smith Committed by Juozas Kaziukenas

initial support for settings savepoints in nested transactions

parent f1c0b3a4
...@@ -740,6 +740,23 @@ class Connection implements DriverConnection ...@@ -740,6 +740,23 @@ class Connection implements DriverConnection
} }
} }
/**
* Returns the savepoint name to use for nested transactions are false if they are not supported
* "savepointFormat" parameter is not set
*
* @return mixed a string with the savepoint name or false
*/
protected function _getNestedTransactionSavePointName($transactionNestingLevel) {
//TODO this should be configured not hardcoded, but how to do user configuration
$this->_params['savepointFormat'] = 'DOCTRINE2_SAVEPOINT_%s';
if ($this->_platform->supportsSavepoints() && !empty($this->_params['savepointFormat'])) {
return sprintf($this->_params['savepointFormat'], $this->_transactionNestingLevel);
}
return false;
}
/** /**
* Starts a transaction by suspending auto-commit mode. * Starts a transaction by suspending auto-commit mode.
* *
...@@ -749,11 +766,16 @@ class Connection implements DriverConnection ...@@ -749,11 +766,16 @@ class Connection implements DriverConnection
{ {
$this->connect(); $this->connect();
if ($this->_transactionNestingLevel == 0) { ++$this->_transactionNestingLevel;
if ($this->_transactionNestingLevel == 1) {
$this->_conn->beginTransaction(); $this->_conn->beginTransaction();
} else {
$savepointName = $this->_getNestedTransactionSavePointName($this->_transactionNestingLevel);
if ($savepointName) {
$this->createSavePoint($savepointName);
}
} }
++$this->_transactionNestingLevel;
} }
/** /**
...@@ -776,6 +798,11 @@ class Connection implements DriverConnection ...@@ -776,6 +798,11 @@ class Connection implements DriverConnection
if ($this->_transactionNestingLevel == 1) { if ($this->_transactionNestingLevel == 1) {
$this->_conn->commit(); $this->_conn->commit();
} else {
$savepointName = $this->_getNestedTransactionSavePointName($this->_transactionNestingLevel);
if ($savepointName) {
$this->releaseSavePoint($savepointName);
}
} }
--$this->_transactionNestingLevel; --$this->_transactionNestingLevel;
...@@ -802,11 +829,55 @@ class Connection implements DriverConnection ...@@ -802,11 +829,55 @@ class Connection implements DriverConnection
$this->_conn->rollback(); $this->_conn->rollback();
$this->_isRollbackOnly = false; $this->_isRollbackOnly = false;
} else { } else {
$this->_isRollbackOnly = true; $savepointName = $this->_getNestedTransactionSavePointName($this->_transactionNestingLevel);
if (!$this->_isRollbackOnly && $savepointName) {
$this->rollbackSavePoint($savepointName);
} else {
$this->_isRollbackOnly = true;
}
--$this->_transactionNestingLevel; --$this->_transactionNestingLevel;
} }
} }
/**
* createSavepoint
* creates a new savepoint
*
* @param string $savepoint name of a savepoint to set
* @return void
*/
public function createSavePoint($savepoint)
{
//TODO check if save points are supported? if so where?
return $this->_conn->exec($this->_platform->createSavePoint($savepoint));
}
/**
* releaseSavePoint
* releases given savepoint
*
* @param string $savepoint name of a savepoint to release
* @return void
*/
public function releaseSavePoint($savepoint)
{
//TODO check if save points are supported? if so where?
return $this->_conn->exec($this->_platform->releaseSavePoint($savepoint));
}
/**
* rollbackSavePoint
* releases given savepoint
*
* @param string $savepoint name of a savepoint to rollback to
* @return void
*/
public function rollbackSavePoint($savepoint)
{
//TODO check if save points are supported? if so where?
return $this->_conn->exec($this->_platform->rollbackSavePoint($savepoint));
}
/** /**
* Gets the wrapped driver connection. * Gets the wrapped driver connection.
* *
......
...@@ -92,4 +92,46 @@ class Driver implements \Doctrine\DBAL\Driver ...@@ -92,4 +92,46 @@ class Driver implements \Doctrine\DBAL\Driver
$params = $conn->getParams(); $params = $conn->getParams();
return $params['dbname']; return $params['dbname'];
} }
/**
* createSavepoint
* creates a new savepoint
*
* @param string $savepoint name of a savepoint to set
* @return void
*/
protected function createSavePoint($savepoint)
{
$query = 'SAVEPOINT ' . $savepoint;
return $this->conn->execute($query);
}
/**
* releaseSavePoint
* releases given savepoint
*
* @param string $savepoint name of a savepoint to release
* @return void
*/
protected function releaseSavePoint($savepoint)
{
$query = 'RELEASE SAVEPOINT ' . $savepoint;
return $this->conn->execute($query);
}
/**
* rollbackSavePoint
* releases given savepoint
*
* @param string $savepoint name of a savepoint to rollback to
* @return void
*/
protected function rollbackSavePoint($savepoint)
{
$query = 'ROLLBACK TO SAVEPOINT ' . $savepoint;
return $this->conn->execute($query);
}
} }
\ No newline at end of file
...@@ -1986,4 +1986,37 @@ abstract class AbstractPlatform ...@@ -1986,4 +1986,37 @@ abstract class AbstractPlatform
{ {
return 'SELECT 1'; return 'SELECT 1';
} }
/**
* Generate SQL to create a new savepoint
*
* @param string $savepoint
* @return string
*/
public function createSavePoint($savepoint)
{
return 'SAVEPOINT ' . $savepoint;
}
/**
* Generate SQL to release a savepoint
*
* @param string $savepoint
* @return string
*/
public function releaseSavePoint($savepoint)
{
return 'RELEASE SAVEPOINT ' . $savepoint;
}
/**
* Generate SQL to rollback a savepoint
*
* @param string $savepoint
* @return string
*/
public function rollbackSavePoint($savepoint)
{
return 'ROLLBACK TO SAVEPOINT ' . $savepoint;
}
} }
\ No newline at end of file
...@@ -267,7 +267,7 @@ class MySqlPlatform extends AbstractPlatform ...@@ -267,7 +267,7 @@ class MySqlPlatform extends AbstractPlatform
*/ */
public function supportsSavepoints() public function supportsSavepoints()
{ {
return false; return true;
} }
public function getShowDatabasesSQL() public function getShowDatabasesSQL()
......
...@@ -43,10 +43,14 @@ class ConnectionTest extends \Doctrine\Tests\DbalFunctionalTestCase ...@@ -43,10 +43,14 @@ class ConnectionTest extends \Doctrine\Tests\DbalFunctionalTestCase
$this->assertEquals(1, $this->_conn->getTransactionNestingLevel()); $this->assertEquals(1, $this->_conn->getTransactionNestingLevel());
//no rethrow //no rethrow
} }
$this->assertTrue($this->_conn->isRollbackOnly()); if ($this->_conn->getDatabasePlatform()->supportsSavepoints()) {
$this->assertFalse($this->_conn->isRollbackOnly());
$this->_conn->commit(); // should throw exception } else {
$this->fail('Transaction commit after failed nested transaction should fail.'); $this->assertTrue($this->_conn->isRollbackOnly());
$this->_conn->commit(); // should throw exception
$this->fail('Transaction commit after failed nested transaction should fail.');
}
} catch (ConnectionException $e) { } catch (ConnectionException $e) {
$this->assertEquals(1, $this->_conn->getTransactionNestingLevel()); $this->assertEquals(1, $this->_conn->getTransactionNestingLevel());
$this->_conn->rollback(); $this->_conn->rollback();
......
...@@ -125,9 +125,9 @@ class MySqlPlatformTest extends AbstractPlatformTestCase ...@@ -125,9 +125,9 @@ class MySqlPlatformTest extends AbstractPlatformTestCase
$this->assertTrue($this->_platform->supportsIdentityColumns()); $this->assertTrue($this->_platform->supportsIdentityColumns());
} }
public function testDoesNotSupportSavePoints() public function testDoesSupportSavePoints()
{ {
$this->assertFalse($this->_platform->supportsSavepoints()); $this->assertTrue($this->_platform->supportsSavepoints());
} }
public function getGenerateIndexSql() public function getGenerateIndexSql()
......
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