diff --git a/lib/Doctrine/Adapter/Interface.php b/lib/Doctrine/Adapter/Interface.php index ccb54591b..b4a897cd5 100644 --- a/lib/Doctrine/Adapter/Interface.php +++ b/lib/Doctrine/Adapter/Interface.php @@ -20,10 +20,15 @@ */ /** * Doctrine_Adapter_Interface + * This adapter interface should be implemented by all custom adapters * - * @author Konsta Vesterinen - * @license LGPL + * @author Konsta Vesterinen + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @package Doctrine + * @category Object Relational Mapping + * @link www.phpdoctrine.com + * @since 1.0 + * @version $Revision$ */ interface Doctrine_Adapter_Interface { public function prepare($prepareString); diff --git a/lib/Doctrine/Connection.php b/lib/Doctrine/Connection.php index 8362a58b6..eb49973b8 100644 --- a/lib/Doctrine/Connection.php +++ b/lib/Doctrine/Connection.php @@ -357,11 +357,11 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun * @throws Doctrine_Connection_Exception if some of the key values was null * @throws Doctrine_Connection_Exception if there were no key fields * @throws PDOException if something fails at PDO level - * @return void + * @return integer number of rows affected */ public function replace($table, array $fields, array $keys) { - if( ! $this->supports('replace')) - throw new Doctrine_Connection_Exception('replace query is not supported'); + //if( ! $this->supports('replace')) + // throw new Doctrine_Connection_Exception('replace query is not supported'); if(empty($keys)) diff --git a/lib/Doctrine/Transaction.php b/lib/Doctrine/Transaction.php index 6795379bb..7a655e063 100644 --- a/lib/Doctrine/Transaction.php +++ b/lib/Doctrine/Transaction.php @@ -43,7 +43,7 @@ class Doctrine_Transaction extends Doctrine_Connection_Module { */ const STATE_BUSY = 2; /** - * @var integer $transaction_level the nesting level of transactions, used by transaction methods + * @var integer $transactionLevel the nesting level of transactions, used by transaction methods */ protected $transactionLevel = 0; /** @@ -55,6 +55,10 @@ class Doctrine_Transaction extends Doctrine_Connection_Module { * this list will be deleted when transaction is committed */ protected $delete = array(); + /** + * @var array $savepoints an array containing all savepoints + */ + public $savePoints = array(); /** * getState * returns the state of this connection @@ -148,27 +152,27 @@ class Doctrine_Transaction extends Doctrine_Connection_Module { * beginTransaction * Start a transaction or set a savepoint. * + * if trying to set a savepoint and there is no active transaction + * a new transaction is being started + * * Listeners: onPreTransactionBegin, onTransactionBegin * * @param string $savepoint name of a savepoint to set - * @throws Doctrine_Transaction_Exception if trying to create a savepoint and there - * are no active transactions - * * @return integer current transaction nesting level */ public function beginTransaction($savepoint = null) { - - if( ! is_null($savepoint)) { - if($this->transactionLevel == 0) - throw new Doctrine_Transaction_Exception('Savepoint cannot be created when changes are auto committed'); + $this->beginTransaction(); + + $this->savePoints[] = $savepoint; + $this->createSavePoint($savepoint); } else { if($this->transactionLevel == 0) { $this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionBegin($this->conn); - $this->conn->getDbh()->beginTransaction(); + $this->conn->getDbh()->beginTransaction(); $this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionBegin($this->conn); } @@ -189,18 +193,21 @@ class Doctrine_Transaction extends Doctrine_Connection_Module { * * @param string $savepoint name of a savepoint to release * @throws Doctrine_Transaction_Exception if the transaction fails at PDO level - * @throws Doctrine_Transaction_Exception if there are no active transactions * @throws Doctrine_Validator_Exception if the transaction fails due to record validations - * @return void + * @return boolean false if commit couldn't be performed, true otherwise */ public function commit($savepoint = null) { if($this->transactionLevel == 0) - throw new Doctrine_Transaction_Exception('Commit/release savepoint cannot be done. There is no active transaction.'); + return false; + + $this->transactionLevel--; if ( ! is_null($savepoint)) { + $this->transactionLevel = $this->removeSavePoints($savepoint); + $this->releaseSavePoint($savepoint); } else { - $this->transactionLevel--; + if($this->transactionLevel == 0) { $this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionCommit($this->conn); @@ -230,6 +237,7 @@ class Doctrine_Transaction extends Doctrine_Connection_Module { $this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionCommit($this->conn); } } + return true; } /** * rollback @@ -242,16 +250,18 @@ class Doctrine_Transaction extends Doctrine_Connection_Module { * eventlistener methods * * @param string $savepoint name of a savepoint to rollback to - * @throws Doctrine_Transaction_Exception if there are no active transactions - * @return void + * @return boolean false if rollback couldn't be performed, true otherwise */ public function rollback($savepoint = null) { - //if($this->transactionLevel == 0) - // throw new Doctrine_Transaction_Exception('Rollback cannot be done. There is no active transaction.'); + if($this->transactionLevel == 0) + return false; $this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionRollback($this->conn); - if ( ! is_null($savepoint)) { + if( ! is_null($savepoint)) { + + $this->transactionLevel = $this->removeSavePoints($savepoint); + $this->rollbackSavePoint($savepoint); } else { //$this->conn->unitOfWork->reset(); @@ -262,6 +272,8 @@ class Doctrine_Transaction extends Doctrine_Connection_Module { $this->conn->getDbh()->rollback(); } $this->conn->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionRollback($this->conn); + + return true; } /** * releaseSavePoint @@ -270,7 +282,7 @@ class Doctrine_Transaction extends Doctrine_Connection_Module { * @param string $savepoint name of a savepoint to create * @return void */ - public function createSavePoint($savepoint) { + protected function createSavePoint($savepoint) { throw new Doctrine_Transaction_Exception('Savepoints not supported by this driver.'); } /** @@ -280,7 +292,7 @@ class Doctrine_Transaction extends Doctrine_Connection_Module { * @param string $savepoint name of a savepoint to release * @return void */ - public function releaseSavePoint($savepoint) { + protected function releaseSavePoint($savepoint) { throw new Doctrine_Transaction_Exception('Savepoints not supported by this driver.'); } /** @@ -290,9 +302,27 @@ class Doctrine_Transaction extends Doctrine_Connection_Module { * @param string $savepoint name of a savepoint to rollback to * @return void */ - public function rollbackSavePoint($savepoint) { + protected function rollbackSavePoint($savepoint) { throw new Doctrine_Transaction_Exception('Savepoints not supported by this driver.'); } + /** + * removeSavePoints + * removes a savepoint from the internal savePoints array of this transaction object + * and all its children savepoints + * + * @param sring $savepoint name of the savepoint to remove + * @return integer the current transaction level + */ + private function removeSavePoints($savepoint) { + $i = array_search($savepoint, $this->savePoints); + + $c = count($this->savePoints); + + for($x = $i; $x < count($this->savePoints); $x++) { + unset($this->savePoints[$x]); + } + return ($c - $i); + } /** * setIsolation * diff --git a/lib/Doctrine/Transaction/Firebird.php b/lib/Doctrine/Transaction/Firebird.php index 3de3f0024..9f21f6f8a 100644 --- a/lib/Doctrine/Transaction/Firebird.php +++ b/lib/Doctrine/Transaction/Firebird.php @@ -38,7 +38,7 @@ class Doctrine_Transaction_Firebird extends Doctrine_Transaction { * @param string $savepoint name of a savepoint to set * @return void */ - public function createSavePoint($savepoint) { + protected function createSavePoint($savepoint) { $query = 'SAVEPOINT '.$savepoint; return $this->conn->getDbh()->query($query); @@ -50,7 +50,7 @@ class Doctrine_Transaction_Firebird extends Doctrine_Transaction { * @param string $savepoint name of a savepoint to release * @return void */ - public function releaseSavePoint($savepoint) { + protected function releaseSavePoint($savepoint) { $query = 'RELEASE SAVEPOINT '.$savepoint; return $this->conn->getDbh()->query($query); @@ -62,7 +62,7 @@ class Doctrine_Transaction_Firebird extends Doctrine_Transaction { * @param string $savepoint name of a savepoint to rollback to * @return void */ - public function rollbackSavePoint($savepoint) { + protected function rollbackSavePoint($savepoint) { $query = 'ROLLBACK TO SAVEPOINT '.$savepoint; return $this->conn->getDbh()->query($query); diff --git a/lib/Doctrine/Transaction/Mysql.php b/lib/Doctrine/Transaction/Mysql.php index a44faf4cd..28e713624 100644 --- a/lib/Doctrine/Transaction/Mysql.php +++ b/lib/Doctrine/Transaction/Mysql.php @@ -38,7 +38,7 @@ class Doctrine_Transaction_Mysql extends Doctrine_Transaction { * @param string $savepoint name of a savepoint to set * @return void */ - public function createSavePoint($savepoint) { + protected function createSavePoint($savepoint) { $query = 'SAVEPOINT '.$savepoint; return $this->conn->getDbh()->query($query); @@ -50,7 +50,7 @@ class Doctrine_Transaction_Mysql extends Doctrine_Transaction { * @param string $savepoint name of a savepoint to release * @return void */ - public function releaseSavePoint($savepoint) { + protected function releaseSavePoint($savepoint) { $query = 'RELEASE SAVEPOINT '.$savepoint; return $this->conn->getDbh()->query($query); @@ -62,7 +62,7 @@ class Doctrine_Transaction_Mysql extends Doctrine_Transaction { * @param string $savepoint name of a savepoint to rollback to * @return void */ - public function rollbackSavePoint($savepoint) { + protected function rollbackSavePoint($savepoint) { $query = 'ROLLBACK TO SAVEPOINT '.$savepoint; return $this->conn->getDbh()->query($query); diff --git a/lib/Doctrine/Transaction/Oracle.php b/lib/Doctrine/Transaction/Oracle.php index 050193fda..504312ef7 100644 --- a/lib/Doctrine/Transaction/Oracle.php +++ b/lib/Doctrine/Transaction/Oracle.php @@ -38,7 +38,7 @@ class Doctrine_Transaction_Oracle extends Doctrine_Transaction { * @param string $savepoint name of a savepoint to set * @return void */ - public function createSavePoint($savepoint) { + protected function createSavePoint($savepoint) { $query = 'SAVEPOINT '.$savepoint; return $this->conn->getDbh()->query($query); @@ -50,7 +50,7 @@ class Doctrine_Transaction_Oracle extends Doctrine_Transaction { * @param string $savepoint name of a savepoint to release * @return void */ - public function releaseSavePoint($savepoint) { + protected function releaseSavePoint($savepoint) { // oracle doesn't support manual releasing of savepoints return true; } @@ -61,7 +61,7 @@ class Doctrine_Transaction_Oracle extends Doctrine_Transaction { * @param string $savepoint name of a savepoint to rollback to * @return void */ - public function rollbackSavePoint($savepoint) { + protected function rollbackSavePoint($savepoint) { $query = 'ROLLBACK TO SAVEPOINT '.$savepoint; return $this->conn->getDbh()->query($query); diff --git a/lib/Doctrine/Transaction/Pgsql.php b/lib/Doctrine/Transaction/Pgsql.php index 428f34743..913d5a4e7 100644 --- a/lib/Doctrine/Transaction/Pgsql.php +++ b/lib/Doctrine/Transaction/Pgsql.php @@ -39,7 +39,7 @@ class Doctrine_Transaction_Pgsql extends Doctrine_Transaction { * @param string $savepoint name of a savepoint to set * @return void */ - public function createSavePoint($savepoint) { + protected function createSavePoint($savepoint) { $query = 'SAVEPOINT '.$savepoint; return $this->conn->getDbh()->query($query); @@ -51,7 +51,7 @@ class Doctrine_Transaction_Pgsql extends Doctrine_Transaction { * @param string $savepoint name of a savepoint to release * @return void */ - public function releaseSavePoint($savepoint) { + protected function releaseSavePoint($savepoint) { $query = 'RELEASE SAVEPOINT '.$savepoint; return $this->conn->getDbh()->query($query); @@ -63,7 +63,7 @@ class Doctrine_Transaction_Pgsql extends Doctrine_Transaction { * @param string $savepoint name of a savepoint to rollback to * @return void */ - public function rollbackSavePoint($savepoint) { + protected function rollbackSavePoint($savepoint) { $query = 'ROLLBACK TO SAVEPOINT '.$savepoint; return $this->conn->getDbh()->query($query); diff --git a/tests/TransactionFirebirdTestCase.php b/tests/TransactionFirebirdTestCase.php index 0d46cf427..91fde5ffb 100644 --- a/tests/TransactionFirebirdTestCase.php +++ b/tests/TransactionFirebirdTestCase.php @@ -4,17 +4,17 @@ class Doctrine_Transaction_Firebird_TestCase extends Doctrine_Driver_UnitTestCas parent::__construct('firebird'); } public function testCreateSavePointExecutesSql() { - $this->transaction->createSavePoint('mypoint'); - + $this->transaction->beginTransaction('mypoint'); + $this->assertEqual($this->adapter->pop(), 'SAVEPOINT mypoint'); } public function testReleaseSavePointExecutesSql() { - $this->transaction->releaseSavePoint('mypoint'); + $this->transaction->commit('mypoint'); $this->assertEqual($this->adapter->pop(), 'RELEASE SAVEPOINT mypoint'); } public function testRollbackSavePointExecutesSql() { - $this->transaction->rollbackSavePoint('mypoint'); + $this->transaction->rollback('mypoint'); $this->assertEqual($this->adapter->pop(), 'ROLLBACK TO SAVEPOINT mypoint'); } @@ -46,7 +46,7 @@ class Doctrine_Transaction_Firebird_TestCase extends Doctrine_Driver_UnitTestCas $this->transaction->setIsolation('READ UNCOMMITTED'); $this->transaction->setIsolation('READ COMMITTED'); $this->transaction->setIsolation('REPEATABLE READ'); - $this->transaction->setIsolation('SERIALIZABLE'); + $this->transaction->setIsolation('SERIALIZABLE'); $this->assertEqual($this->adapter->pop(), 'SET TRANSACTION ISOLATION LEVEL SNAPSHOT TABLE STABILITY'); $this->assertEqual($this->adapter->pop(), 'SET TRANSACTION ISOLATION LEVEL SNAPSHOT'); diff --git a/tests/TransactionMysqlTestCase.php b/tests/TransactionMysqlTestCase.php index a27f9c4b6..d953e7b7b 100644 --- a/tests/TransactionMysqlTestCase.php +++ b/tests/TransactionMysqlTestCase.php @@ -4,17 +4,17 @@ class Doctrine_Transaction_Mysql_TestCase extends Doctrine_Driver_UnitTestCase { parent::__construct('mysql'); } public function testCreateSavePointExecutesSql() { - $this->transaction->createSavePoint('mypoint'); - + $this->transaction->beginTransaction('mypoint'); + $this->assertEqual($this->adapter->pop(), 'SAVEPOINT mypoint'); } public function testReleaseSavePointExecutesSql() { - $this->transaction->releaseSavePoint('mypoint'); + $this->transaction->commit('mypoint'); $this->assertEqual($this->adapter->pop(), 'RELEASE SAVEPOINT mypoint'); } public function testRollbackSavePointExecutesSql() { - $this->transaction->rollbackSavePoint('mypoint'); + $this->transaction->rollback('mypoint'); $this->assertEqual($this->adapter->pop(), 'ROLLBACK TO SAVEPOINT mypoint'); } diff --git a/tests/TransactionOracleTestCase.php b/tests/TransactionOracleTestCase.php index 67eba1e32..f110d2dbe 100644 --- a/tests/TransactionOracleTestCase.php +++ b/tests/TransactionOracleTestCase.php @@ -4,15 +4,15 @@ class Doctrine_Transaction_Oracle_TestCase extends Doctrine_Driver_UnitTestCase parent::__construct('oci'); } public function testCreateSavePointExecutesSql() { - $this->transaction->createSavePoint('mypoint'); + $this->transaction->beginTransaction('mypoint'); $this->assertEqual($this->adapter->pop(), 'SAVEPOINT mypoint'); } public function testReleaseSavePointAlwaysReturnsTrue() { - $this->assertEqual($this->transaction->releaseSavePoint('mypoint'), true); + $this->assertEqual($this->transaction->commit('mypoint'), true); } public function testRollbackSavePointExecutesSql() { - $this->transaction->rollbackSavePoint('mypoint'); + $this->transaction->rollback('mypoint'); $this->assertEqual($this->adapter->pop(), 'ROLLBACK TO SAVEPOINT mypoint'); } diff --git a/tests/TransactionPgsqlTestCase.php b/tests/TransactionPgsqlTestCase.php index 421e76cc6..5c0f4f6d2 100644 --- a/tests/TransactionPgsqlTestCase.php +++ b/tests/TransactionPgsqlTestCase.php @@ -4,17 +4,17 @@ class Doctrine_Transaction_Pgsql_TestCase extends Doctrine_Driver_UnitTestCase { parent::__construct('pgsql'); } public function testCreateSavePointExecutesSql() { - $this->transaction->createSavePoint('mypoint'); + $this->transaction->beginTransaction('mypoint'); $this->assertEqual($this->adapter->pop(), 'SAVEPOINT mypoint'); } public function testReleaseSavePointExecutesSql() { - $this->transaction->releaseSavePoint('mypoint'); + $this->transaction->commit('mypoint'); $this->assertEqual($this->adapter->pop(), 'RELEASE SAVEPOINT mypoint'); } public function testRollbackSavePointExecutesSql() { - $this->transaction->rollbackSavePoint('mypoint'); + $this->transaction->rollback('mypoint'); $this->assertEqual($this->adapter->pop(), 'ROLLBACK TO SAVEPOINT mypoint'); } diff --git a/tests/TransactionTestCase.php b/tests/TransactionTestCase.php index 556a9348d..6e847debd 100644 --- a/tests/TransactionTestCase.php +++ b/tests/TransactionTestCase.php @@ -3,9 +3,10 @@ class Doctrine_Transaction_TestCase extends Doctrine_Driver_UnitTestCase { public function __construct() { parent::__construct('sqlite', true); } +/** public function testCreateSavepointIsOnlyImplementedAtDriverLevel() { try { - $this->transaction->createSavePoint('point'); + $this->transaction->beginTransaction('point'); $this->fail(); } catch(Doctrine_Transaction_Exception $e) { $this->pass(); @@ -13,15 +14,18 @@ class Doctrine_Transaction_TestCase extends Doctrine_Driver_UnitTestCase { } public function testReleaseSavepointIsOnlyImplementedAtDriverLevel() { try { - $this->transaction->releaseSavePoint('point'); + $this->transaction->commit('point'); $this->fail(); } catch(Doctrine_Transaction_Exception $e) { $this->pass(); } } +*/ public function testRollbackSavepointIsOnlyImplementedAtDriverLevel() { try { - $this->transaction->rollbackSavePoint('point'); + $this->transaction->beginTransaction(); + + $this->transaction->rollback('point'); $this->fail(); } catch(Doctrine_Transaction_Exception $e) { $this->pass(); @@ -49,21 +53,11 @@ class Doctrine_Transaction_TestCase extends Doctrine_Driver_UnitTestCase { public function testGetStateReturnsStateConstant() { $this->assertEqual($this->transaction->getState(), Doctrine_Transaction::STATE_SLEEP); } - public function testExceptionIsThrownWhenCommittingNotActiveTransaction() { - try { - $this->transaction->commit(); - $this->fail(); - } catch(Doctrine_Transaction_Exception $e) { - $this->pass(); - } + public function testCommittingNotActiveTransactionReturnsFalse() { + $this->assertEqual($this->transaction->commit(), false); } public function testExceptionIsThrownWhenUsingRollbackOnNotActiveTransaction() { - try { - $this->transaction->rollback(); - $this->fail(); - } catch(Doctrine_Transaction_Exception $e) { - $this->pass(); - } + $this->assertEqual($this->transaction->rollback(), false); } public function testBeginTransactionStartsNewTransaction() { $this->transaction->beginTransaction();