Commit 6abf6aaf authored by Benjamin Eberlei's avatar Benjamin Eberlei

Merge remote branch 'lsmith77/savepoints' into DBAL-55

parents 3d55dc8e f482ce3f
......@@ -101,6 +101,13 @@ class Connection implements DriverConnection
*/
private $_transactionIsolationLevel;
/**
* If nested transations should use savepoints
*
* @var integer
*/
private $_nestTransactionsWithSavepoints;
/**
* The parameters used during creation of the Connection instance.
*
......@@ -740,6 +747,48 @@ class Connection implements DriverConnection
}
}
/**
* Set if nested transactions should use savepoints
*
* @param boolean
*/
public function setNestTransactionsWithSavepoints($nestTransactionsWithSavepoints)
{
if ($this->_transactionNestingLevel > 0) {
throw ConnectionException::mayNotAlterNestedTransactionWithSavepointsInTransaction();
}
if ($nestTransactionsWithSavepoints && !$this->_platform->supportsSavepoints()) {
ConnectionException::savepointsNotSupported();
}
$this->_nestTransactionsWithSavepoints = $nestTransactionsWithSavepoints;
}
/**
* Get if nested transactions should use savepoints
*
* @return boolean
*/
public function getNestTransactionsWithSavepoints()
{
return $this->_nestTransactionsWithSavepoints;
}
/**
* 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() {
if ($this->_platform->supportsSavepoints() && !empty($this->_nestTransactionsWithSavepoints)) {
return 'DOCTRINE2_SAVEPOINT_'.$this->_transactionNestingLevel;
}
return false;
}
/**
* Starts a transaction by suspending auto-commit mode.
*
......@@ -749,11 +798,16 @@ class Connection implements DriverConnection
{
$this->connect();
if ($this->_transactionNestingLevel == 0) {
++$this->_transactionNestingLevel;
if ($this->_transactionNestingLevel == 1) {
$this->_conn->beginTransaction();
} else {
$savepointName = $this->_getNestedTransactionSavePointName($this->_transactionNestingLevel);
if ($savepointName) {
$this->createSavePoint($savepointName);
}
}
++$this->_transactionNestingLevel;
}
/**
......@@ -776,6 +830,11 @@ class Connection implements DriverConnection
if ($this->_transactionNestingLevel == 1) {
$this->_conn->commit();
} else {
$savepointName = $this->_getNestedTransactionSavePointName($this->_transactionNestingLevel);
if ($savepointName && $this->_platform->supportsReleaseSavepoints()) {
$this->releaseSavePoint($savepointName);
}
}
--$this->_transactionNestingLevel;
......@@ -801,12 +860,67 @@ class Connection implements DriverConnection
$this->_transactionNestingLevel = 0;
$this->_conn->rollback();
$this->_isRollbackOnly = false;
} else {
$savepointName = $this->_getNestedTransactionSavePointName($this->_transactionNestingLevel);
if (!$this->_isRollbackOnly && $savepointName) {
$this->rollbackSavePoint($savepointName);
} else {
$this->_isRollbackOnly = true;
}
--$this->_transactionNestingLevel;
}
}
/**
* createSavepoint
* creates a new savepoint
*
* @param string $savepoint name of a savepoint to set
* @return void
*/
public function createSavePoint($savepoint)
{
if (!$this->_platform->supportsSavepoints()) {
ConnectionException::savepointsNotSupported();
}
$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)
{
if (!$this->_platform->supportsSavepoints()) {
ConnectionException::savepointsNotSupported();
}
if ($this->_platform->supportsReleaseSavepoints()) {
$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)
{
if (!$this->_platform->supportsSavepoints()) {
ConnectionException::savepointsNotSupported();
}
$this->_conn->exec($this->_platform->rollbackSavePoint($savepoint));
}
/**
* Gets the wrapped driver connection.
*
......
......@@ -41,4 +41,14 @@ class ConnectionException extends DBALException
{
return new self("There is no active transaction.");
}
public static function savepointsNotSupported()
{
return new self("Savepoints are not supported transaction.");
}
public static function mayNotAlterNestedTransactionWithSavepointsInTransaction()
{
return new self("May not alter the nested transaction with savepoints behavior while a transaction is open.");
}
}
\ No newline at end of file
......@@ -1773,6 +1773,16 @@ abstract class AbstractPlatform
return true;
}
/**
* Whether the platform supports releasing savepoints.
*
* @return boolean
*/
public function supportsReleaseSavepoints()
{
return $this->supportsSavepoints();
}
/**
* Whether the platform supports primary key constraints.
*
......@@ -1986,4 +1996,37 @@ abstract class AbstractPlatform
{
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
......@@ -291,6 +291,16 @@ class DB2Platform extends AbstractPlatform
return false;
}
/**
* Whether the platform supports releasing savepoints.
*
* @return boolean
*/
public function supportsReleaseSavepoints()
{
return false;
}
/**
* Gets the SQL specific for the platform to get the current date.
*
......
......@@ -61,12 +61,11 @@ class MsSqlPlatform extends AbstractPlatform
}
/**
* Whether the platform supports savepoints. MsSql does not.
* Whether the platform supports releasing savepoints.
*
* @return boolean
* @override
*/
public function supportsSavepoints()
public function supportsReleaseSavepoints()
{
return false;
}
......@@ -128,7 +127,6 @@ DROP DATABASE ' . $name . ';';
public function getDropIndexSQL($index, $table=null)
{
if($index instanceof \Doctrine\DBAL\Schema\Index) {
$index_ = $index;
$index = $index->getName();
} else if(!is_string($index)) {
throw new \InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.');
......@@ -667,4 +665,37 @@ DROP DATABASE ' . $name . ';';
{
}
/**
* Generate SQL to create a new savepoint
*
* @param string $savepoint
* @return string
*/
public function createSavePoint($savepoint)
{
return 'SAVE TRANSACTION ' . $savepoint;
}
/**
* Generate SQL to release a savepoint
*
* @param string $savepoint
* @return string
*/
public function releaseSavePoint($savepoint)
{
return '';
}
/**
* Generate SQL to rollback a savepoint
*
* @param string $savepoint
* @return string
*/
public function rollbackSavePoint($savepoint)
{
return 'ROLLBACK TRANSACTION ' . $savepoint;
}
}
......@@ -259,17 +259,6 @@ class MySqlPlatform extends AbstractPlatform
return true;
}
/**
* Whether the platform supports savepoints. MySql does not.
*
* @return boolean
* @override
*/
public function supportsSavepoints()
{
return false;
}
public function getShowDatabasesSQL()
{
return 'SHOW DATABASES';
......
......@@ -659,6 +659,16 @@ LEFT JOIN all_cons_columns r_cols
return false;
}
/**
* Whether the platform supports releasing savepoints.
*
* @return boolean
*/
public function supportsReleaseSavepoints()
{
return false;
}
/**
* @inheritdoc
*/
......@@ -700,4 +710,15 @@ LEFT JOIN all_cons_columns r_cols
'urowid' => 'string'
);
}
/**
* Generate SQL to release a savepoint
*
* @param string $savepoint
* @return string
*/
public function releaseSavePoint($savepoint)
{
return '';
}
}
......@@ -54,6 +54,43 @@ class ConnectionTest extends \Doctrine\Tests\DbalFunctionalTestCase
}
}
public function testTransactionNestingBehaviorWithSavepoints()
{
if ($this->_conn->getDatabasePlatform()->supportsSavepoints()) {
$this->_conn->setNestTransactionsWithSavepoints(true);
try {
$this->_conn->beginTransaction();
$this->assertEquals(1, $this->_conn->getTransactionNestingLevel());
try {
$this->_conn->beginTransaction();
$this->assertEquals(2, $this->_conn->getTransactionNestingLevel());
$this->_conn->beginTransaction();
$this->assertEquals(3, $this->_conn->getTransactionNestingLevel());
$this->_conn->commit();
$this->assertEquals(2, $this->_conn->getTransactionNestingLevel());
throw new \Exception;
$this->_conn->commit(); // never reached
} catch (\Exception $e) {
$this->_conn->rollback();
$this->assertEquals(1, $this->_conn->getTransactionNestingLevel());
//no rethrow
}
$this->assertFalse($this->_conn->isRollbackOnly());
try {
$this->_conn->setNestTransactionsWithSavepoints(false);
$this->fail('Should not be able to disable savepoints in usage for nested transactions inside an open transaction.');
} catch (ConnectionException $e) {
$this->assertTrue($this->_conn->getNestTransactionsWithSavepoints());
}
$this->_conn->commit(); // should not throw exception
} catch (ConnectionException $e) {
$this->fail('Transaction commit after failed nested transaction should not fail when using savepoints.');
$this->_conn->rollback();
}
}
}
public function testTransactionBehaviorWithRollback()
{
try {
......
......@@ -127,7 +127,7 @@ DDB;
public function testDoesNotSupportSavePoints()
{
$this->assertFalse($this->_platform->supportsSavepoints());
$this->assertTrue($this->_platform->supportsSavepoints());
}
public function getGenerateIndexSql()
......
......@@ -125,9 +125,9 @@ class MySqlPlatformTest extends AbstractPlatformTestCase
$this->assertTrue($this->_platform->supportsIdentityColumns());
}
public function testDoesNotSupportSavePoints()
public function testDoesSupportSavePoints()
{
$this->assertFalse($this->_platform->supportsSavepoints());
$this->assertTrue($this->_platform->supportsSavepoints());
}
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