From 8ff6db52b4be89465e4ae31be370433ed972143d Mon Sep 17 00:00:00 2001 From: AlexeyDsov Date: Tue, 1 May 2012 11:06:59 +0400 Subject: [PATCH 1/4] introducing InnerTransaction --- .../DB/Transaction/InnerTransaction.class.php | 113 ++++++++++ .../InnerTransactionWrapper.class.php | 107 +++++++++ ...InnerTransactionWrapperException.class.php | 50 +++++ test/core/InnerTransactionTest.class.php | 203 ++++++++++++++++++ 4 files changed, 473 insertions(+) create mode 100644 core/DB/Transaction/InnerTransaction.class.php create mode 100644 core/DB/Transaction/InnerTransactionWrapper.class.php create mode 100644 core/DB/Transaction/InnerTransactionWrapperException.class.php create mode 100644 test/core/InnerTransactionTest.class.php diff --git a/core/DB/Transaction/InnerTransaction.class.php b/core/DB/Transaction/InnerTransaction.class.php new file mode 100644 index 0000000000..5525d2f230 --- /dev/null +++ b/core/DB/Transaction/InnerTransaction.class.php @@ -0,0 +1,113 @@ +db = $database; + } elseif ($database instanceof GenericDAO) { + $this->db = DBPool::getByDao($database); + } else { + throw new WrongStateException( + '$database must be instance of DB or GenericDAO' + ); + } + + $this->beginTransaction($level, $mode); + } + + public function commit() + { + $this->assertFinished(); + $this->finished = true; + if (!$this->savepointName) { + $this->db->commit(); + } else { + $this->db->queryRaw("release savepoint {$this->savepointName};"); + } + } + + public function rollback() + { + $this->assertFinished(); + $this->finished = true; + if (!$this->savepointName) { + $this->db->rollback(); + } else { + $this->db->queryRaw("rollback to savepoint {$this->savepointName};"); + } + } + + private function beginTransaction( + /* IsolationLevel */ $level = null, + /* AccessMode */ $mode = null + ) + { + $this->assertFinished(); + if (!$this->db->inTransaction()) { + $this->db->begin($level, $mode); + } else { + $this->savepointName = $this->createSavepointName(); + $this->db->queryRaw("savepoint {$this->savepointName};"); + } + } + + private function assertFinished() + { + if ($this->finished) + throw new WrongStateException('This Transaction already finished'); + } + + private static function createSavepointName() + { + static $i = 1; + return 'innerSavepoint'.($i++); + } + } +?> \ No newline at end of file diff --git a/core/DB/Transaction/InnerTransactionWrapper.class.php b/core/DB/Transaction/InnerTransactionWrapper.class.php new file mode 100644 index 0000000000..ccd8003d3e --- /dev/null +++ b/core/DB/Transaction/InnerTransactionWrapper.class.php @@ -0,0 +1,107 @@ +db = $db; + return $this; + } + + /** + * @param StorableDAO $dao + * @return InnerTransactionWrapper + */ + public function setDao(StorableDAO $dao) + { + $this->dao = $dao; + return $this; + } + + /** + * @param mixed $function + * @return InnerTransactionWrapper + */ + public function setFunction($function) + { + $this->function = $function; + return $this; + } + + /** + * @param IsolationLevel $level + * @return InnerTransactionWrapper + */ + public function setLevel(IsolationLevel $level) + { + $this->level = $level; + return $this; + } + + /** + * @param AccessMode $mode + * @return InnerTransactionWrapper + */ + public function setMode(AccessMode $mode) + { + $this->mode = $mode; + return $this; + } + + public function run() + { + Assert::isTrue(!is_null($this->dao) || !is_null($this->db), 'set first dao or db'); + Assert::isNotNull($this->function, 'set first function'); + + $transaction = InnerTransaction::begin( + $this->dao ?: $this->db, + $this->level, + $this->mode + ); + + try { + $result = call_user_func_array($this->function, func_get_args()); + $transaction->commit(); + return $result; + } catch (InnerTransactionWrapperException $e) { + $transaction->rollback(); + return $e->getReturnValue(); + } catch (Exception $e) { + $transaction->rollback(); + throw $e; + } + } + } +?> \ No newline at end of file diff --git a/core/DB/Transaction/InnerTransactionWrapperException.class.php b/core/DB/Transaction/InnerTransactionWrapperException.class.php new file mode 100644 index 0000000000..58116e6788 --- /dev/null +++ b/core/DB/Transaction/InnerTransactionWrapperException.class.php @@ -0,0 +1,50 @@ +setReturnValue($returnValue); + } + + /** + * @param mixed $returnValue + * @return InnerTransactionWrapperException + **/ + public function setReturnValue($returnValue) + { + $this->returnValue = $returnValue; + return $this; + } + + /** + * @return mixed + **/ + public function getReturnValue() + { + return $this->returnValue; + } + } +?> \ No newline at end of file diff --git a/test/core/InnerTransactionTest.class.php b/test/core/InnerTransactionTest.class.php new file mode 100644 index 0000000000..fa8111be2a --- /dev/null +++ b/test/core/InnerTransactionTest.class.php @@ -0,0 +1,203 @@ +spawnDb(array( + 'begin' => 1, + 'commit' => 1, + )); + + //execute + $transaction = InnerTransaction::begin($db); + $transaction->commit(); + + //test Exception on second commit + try { + $transaction->commit(); + $this->fail('expecting exception on second transaction commit'); + } catch (WrongStateException $e) { + /* all ok */ + } + } + + public function testRollbackExt() + { + //setup + $db = $this->spawnDb(array( + 'begin' => 1, + 'rollback' => 1, + )); + + //execute + $transaction = InnerTransaction::begin($db); + $transaction->rollback(); + + //test Exception on second commit + try { + $transaction->rollback(); + $this->fail('expecting exception on second transaction commit'); + } catch (WrongStateException $e) { + /* all ok */ + } + } + + public function testCommitInt() + { + //setup + $db = $this->spawnDb(array( + 'queries' => array( + 'savepoint innerSavepoint', + 'release savepoint innerSavepoint', + ), + 'inTransaction' => true, + )); + + //execute + $transaction = InnerTransaction::begin($db); + $transaction->commit(); + + //test Exception on second commit + try { + $transaction->rollback(); + $this->fail('expecting exception on second transaction commit'); + } catch (WrongStateException $e) { + /* all ok */ + } + } + + public function testRollbackInt() + { + //setup + $db = $this->spawnDb(array( + 'queries' => array( + 'savepoint innerSavepoint', + 'rollback to savepoint innerSavepoint', + ), + 'inTransaction' => true, + )); + + //execute + $transaction = InnerTransaction::begin($db); + $transaction->rollback(); + + //test Exception on second commit + try { + $transaction->commit(); + $this->fail('expecting exception on second transaction commit'); + } catch (WrongStateException $e) { + /* all ok */ + } + } + + public function testWrapCommit() + { + $db = $this->spawnDb(array( + 'begin' => 1, + 'commit' => 1, + )); + + $foo = 'foo'; + $bar = 'bar'; + $innerFunction = function($foo) use ($bar) { + return $foo . $bar; + }; + + $wrapper = InnerTransactionWrapper::create() + ->setDB($db) + ->setFunction($innerFunction); + + $this->assertEquals($foo . $bar, $wrapper->run($foo)); + } + + public function testWrapRollbackByWrapException() + { + $db = $this->spawnDb(array( + 'begin' => 1, + 'rollback' => 1, + )); + + $foo = 'foo'; + $bar = 'bar'; + + $wrapper = InnerTransactionWrapper::create() + ->setDB($db) + ->setFunction(array($this, 'wrapExceptionFunction')); + + $this->assertEquals($foo . $bar, $wrapper->run($foo, $bar)); + } + + public function testWrapRollbackByOtherException() + { + $db = $this->spawnDb(array( + 'begin' => 1, + 'rollback' => 1, + )); + + $exception = new DatabaseException('Some database exception'); + + $function = function () use ($exception) {throw $exception;}; + + $wrapper = InnerTransactionWrapper::create() + ->setDB($db) + ->setFunction($function); + + try { + $wrapper->run(); + } catch (Exception $e) { + $this->assertEquals($exception, $e); + } + } + + public function wrapExceptionFunction($foo, $bar) + { + throw InnerTransactionWrapperException::createValue($foo.$bar); + } + + /** + * @param array $options + * @return DB + */ + private function spawnDb($options = array()) + { + $options += array( + 'begin' => 0, + 'commit' => 0, + 'rollback' => 0, + 'queries' => array(), + 'inTransaction' => false, + ); + + $mock = $this->getMock('DB'); + $mock->expects($this->exactly($options['begin']))->method('begin'); + $mock->expects($this->exactly($options['commit']))->method('commit'); + $mock->expects($this->exactly($options['rollback']))->method('rollback'); + $mock->expects($this->any())->method('inTransaction')->will($this->returnValue($options['inTransaction'])); + + $this->applyQueryList($mock, $options['queries']); + + return $mock; + } + + private function applyQueryList( + PHPUnit_Framework_MockObject_MockObject $mock, + $queryList + ) + { + $mock->expects($this->exactly(count($queryList)))->method('queryRaw'); + + $self = $this; + $i = 1; + foreach ($queryList as $at => $expQuery) { + $cb = function ($query) use ($self, $expQuery) { + $self->assertStringStartsWith($expQuery, $query); + }; + $mock-> + expects($this->at($i++))-> + method('queryRaw')-> + will($this->returnCallback($cb)); + } + } + } +?> \ No newline at end of file From 29bad3f27051be45c45a686ad08ab2945702794d Mon Sep 17 00:00:00 2001 From: AlexeyDsov Date: Thu, 3 May 2012 00:56:45 +0400 Subject: [PATCH 2/4] Added savepoints methods to DB; InnerTransaction fixes; --- core/DB/DB.class.php | 97 +++++++++++++++++++ .../DB/Transaction/InnerTransaction.class.php | 18 ++-- .../InnerTransactionWrapper.class.php | 1 + test/core/InnerTransactionTest.class.php | 72 ++++++-------- 4 files changed, 136 insertions(+), 52 deletions(-) diff --git a/core/DB/DB.class.php b/core/DB/DB.class.php index 3477408563..8eee6a375a 100644 --- a/core/DB/DB.class.php +++ b/core/DB/DB.class.php @@ -35,6 +35,10 @@ abstract class DB * flag to indicate whether we're in transaction **/ private $transaction = false; + /** + * @var list of all started savepoints + */ + private $savepointList = array(); private $queue = array(); private $toQueue = false; @@ -140,6 +144,7 @@ public function commit() $this->queryRaw("commit;\n"); $this->transaction = false; + $this->savepointList = array(); return $this; } @@ -155,6 +160,7 @@ public function rollback() $this->queryRaw("rollback;\n"); $this->transaction = false; + $this->savepointList = array(); return $this; } @@ -222,6 +228,66 @@ public function isQueueActive() } //@} + /** + * @param string $savepointName + * @return DB + */ + public function savepointBegin($savepointName) + { + if (!$this->inTransaction()) + throw new DatabaseException('To use savepoint begin transaction first'); + + $query = 'savepoint '.$this->savepointName; + if ($this->toQueue) + $this->queue[] = $query; + else + $this->queryRaw("{$query};\n"); + + return $this->addSavepoint($savepointName); + } + + /** + * @param string $savepointName + * @return DB + */ + public function savepointRelease($savepointName) + { + if (!$this->inTransaction()) + throw new DatabaseException('To release savepoint begin transaction first'); + + if (!$this->checkSavepointExist($savepointName)) + throw new DatabaseException("savepoint with name '{$savepointName}' nor registered"); + + $query = 'release savepoint '.$this->savepointName; + if ($this->toQueue) + $this->queue[] = $query; + else + $this->queryRaw("{$query};\n"); + + return $this->dropSavepoint($savepointName); + } + + /** + * @param string $savepointName + * @return DB + */ + public function savepointRollback($savepointName) + { + if (!$this->inTransaction()) + throw new DatabaseException('To rollback savepoint begin transaction first'); + + if (!$this->checkSavepointExist($savepointName)) + throw new DatabaseException("savepoint with name '{$savepointName}' nor registered"); + + $query = 'rollback to savepoint '.$this->savepointName; + if ($this->toQueue) + $this->queue[] = $query; + else + $this->queryRaw("{$query};\n"); + + return $this->dropSavepoint($savepointName); + } + /** * base queries **/ @@ -331,5 +397,36 @@ public function setEncoding($encoding) return $this; } + + /** + * @param string $savepointName + * @return DB + */ + private function addSavepoint($savepointName) + { + if ($this->checkSavepointExist($savepointName)) + throw new DatabaseException("savepoint with name '{$savepointName}' already marked"); + + $this->savepointList[$savepointName] = true; + return $this; + } + + /** + * @param string $savepointName + * @return DB + */ + private function dropSavepoint($savepointName) + { + if (!$this->checkSavepointExist($savepointName)) + throw new DatabaseException("savepoint with name '{$savepointName}' nor registered"); + + unset($this->savepointList[$savepointName]); + return $this; + } + + private function checkSavepointExist($savepointName) + { + return isset($this->savepointList[$savepointName]); + } } ?> \ No newline at end of file diff --git a/core/DB/Transaction/InnerTransaction.class.php b/core/DB/Transaction/InnerTransaction.class.php index 5525d2f230..9b38811a6f 100644 --- a/core/DB/Transaction/InnerTransaction.class.php +++ b/core/DB/Transaction/InnerTransaction.class.php @@ -31,8 +31,8 @@ final class InnerTransaction **/ public static function begin( $database, - /* IsolationLevel */ $level = null, - /* AccessMode */ $mode = null + IsolationLevel $level = null, + AccessMode $mode = null ) { return new self($database, $level, $mode); @@ -45,8 +45,8 @@ public static function begin( **/ public function __construct( $database, - /* IsolationLevel */ $level = null, - /* AccessMode */ $mode = null + IsolationLevel $level = null, + AccessMode $mode = null ) { if ($database instanceof DB) { @@ -69,7 +69,7 @@ public function commit() if (!$this->savepointName) { $this->db->commit(); } else { - $this->db->queryRaw("release savepoint {$this->savepointName};"); + $this->db->savepointRelease($this->savepointName); } } @@ -80,13 +80,13 @@ public function rollback() if (!$this->savepointName) { $this->db->rollback(); } else { - $this->db->queryRaw("rollback to savepoint {$this->savepointName};"); + $this->db->savepointRollback($this->savepointName); } } private function beginTransaction( - /* IsolationLevel */ $level = null, - /* AccessMode */ $mode = null + IsolationLevel $level = null, + AccessMode $mode = null ) { $this->assertFinished(); @@ -94,7 +94,7 @@ private function beginTransaction( $this->db->begin($level, $mode); } else { $this->savepointName = $this->createSavepointName(); - $this->db->queryRaw("savepoint {$this->savepointName};"); + $this->db->savepointBegin($this->savepointName); } } diff --git a/core/DB/Transaction/InnerTransactionWrapper.class.php b/core/DB/Transaction/InnerTransactionWrapper.class.php index ccd8003d3e..ed59ddb3a2 100644 --- a/core/DB/Transaction/InnerTransactionWrapper.class.php +++ b/core/DB/Transaction/InnerTransactionWrapper.class.php @@ -56,6 +56,7 @@ public function setDao(StorableDAO $dao) */ public function setFunction($function) { + Assert::isTrue(is_callable($function, false), '$function must be callable'); $this->function = $function; return $this; } diff --git a/test/core/InnerTransactionTest.class.php b/test/core/InnerTransactionTest.class.php index fa8111be2a..38cc8baf0e 100644 --- a/test/core/InnerTransactionTest.class.php +++ b/test/core/InnerTransactionTest.class.php @@ -47,10 +47,8 @@ public function testCommitInt() { //setup $db = $this->spawnDb(array( - 'queries' => array( - 'savepoint innerSavepoint', - 'release savepoint innerSavepoint', - ), + 'savepointBegin' => 1, + 'savepointRelease' => 1, 'inTransaction' => true, )); @@ -71,10 +69,8 @@ public function testRollbackInt() { //setup $db = $this->spawnDb(array( - 'queries' => array( - 'savepoint innerSavepoint', - 'rollback to savepoint innerSavepoint', - ), + 'savepointBegin' => 1, + 'savepointRollback' => 1, 'inTransaction' => true, )); @@ -104,9 +100,9 @@ public function testWrapCommit() return $foo . $bar; }; - $wrapper = InnerTransactionWrapper::create() - ->setDB($db) - ->setFunction($innerFunction); + $wrapper = InnerTransactionWrapper::create()-> + setDB($db)-> + setFunction($innerFunction); $this->assertEquals($foo . $bar, $wrapper->run($foo)); } @@ -121,9 +117,9 @@ public function testWrapRollbackByWrapException() $foo = 'foo'; $bar = 'bar'; - $wrapper = InnerTransactionWrapper::create() - ->setDB($db) - ->setFunction(array($this, 'wrapExceptionFunction')); + $wrapper = InnerTransactionWrapper::create()-> + setDB($db)-> + setFunction(array($this, 'wrapExceptionFunction')); $this->assertEquals($foo . $bar, $wrapper->run($foo, $bar)); } @@ -139,9 +135,9 @@ public function testWrapRollbackByOtherException() $function = function () use ($exception) {throw $exception;}; - $wrapper = InnerTransactionWrapper::create() - ->setDB($db) - ->setFunction($function); + $wrapper = InnerTransactionWrapper::create()-> + setDB($db)-> + setFunction($function); try { $wrapper->run(); @@ -165,39 +161,29 @@ private function spawnDb($options = array()) 'begin' => 0, 'commit' => 0, 'rollback' => 0, - 'queries' => array(), + 'savepointBegin' => 0, + 'savepointRelease' => 0, + 'savepointRollback' => 0, 'inTransaction' => false, ); $mock = $this->getMock('DB'); - $mock->expects($this->exactly($options['begin']))->method('begin'); - $mock->expects($this->exactly($options['commit']))->method('commit'); - $mock->expects($this->exactly($options['rollback']))->method('rollback'); - $mock->expects($this->any())->method('inTransaction')->will($this->returnValue($options['inTransaction'])); + $countMethods = array( + 'begin', 'commit', 'rollback', + 'savepointBegin', 'savepointRelease', 'savepointRollback' + ); + foreach ($countMethods as $method) + $mock-> + expects($this->exactly($options[$method]))-> + method($method)-> + will($this->returnSelf()); - $this->applyQueryList($mock, $options['queries']); + $mock-> + expects($this->any())-> + method('inTransaction')-> + will($this->returnValue($options['inTransaction'])); return $mock; } - - private function applyQueryList( - PHPUnit_Framework_MockObject_MockObject $mock, - $queryList - ) - { - $mock->expects($this->exactly(count($queryList)))->method('queryRaw'); - - $self = $this; - $i = 1; - foreach ($queryList as $at => $expQuery) { - $cb = function ($query) use ($self, $expQuery) { - $self->assertStringStartsWith($expQuery, $query); - }; - $mock-> - expects($this->at($i++))-> - method('queryRaw')-> - will($this->returnCallback($cb)); - } - } } ?> \ No newline at end of file From 1a1448e07e6825ffd37b994c6bec15440d6df731 Mon Sep 17 00:00:00 2001 From: AlexeyDsov Date: Thu, 17 May 2012 17:29:54 +0400 Subject: [PATCH 3/4] fix DB savepoints part; update InnerTransactionWrapper API; added InnerTransaction/savepoint test to DAOTest --- core/DB/DB.class.php | 6 +-- .../InnerTransactionWrapper.class.php | 42 +++++++++++++--- ...InnerTransactionWrapperException.class.php | 50 ------------------- test/config.inc.php.tpl | 2 +- test/core/InnerTransactionTest.class.php | 15 ++++-- test/misc/DAOTest.class.php | 36 +++++++++++++ 6 files changed, 84 insertions(+), 67 deletions(-) delete mode 100644 core/DB/Transaction/InnerTransactionWrapperException.class.php diff --git a/core/DB/DB.class.php b/core/DB/DB.class.php index 8eee6a375a..de4ceace93 100644 --- a/core/DB/DB.class.php +++ b/core/DB/DB.class.php @@ -237,7 +237,7 @@ public function savepointBegin($savepointName) if (!$this->inTransaction()) throw new DatabaseException('To use savepoint begin transaction first'); - $query = 'savepoint '.$this->savepointName; + $query = 'savepoint '.$savepointName; if ($this->toQueue) $this->queue[] = $query; else @@ -258,7 +258,7 @@ public function savepointRelease($savepointName) if (!$this->checkSavepointExist($savepointName)) throw new DatabaseException("savepoint with name '{$savepointName}' nor registered"); - $query = 'release savepoint '.$this->savepointName; + $query = 'release savepoint '.$savepointName; if ($this->toQueue) $this->queue[] = $query; else @@ -279,7 +279,7 @@ public function savepointRollback($savepointName) if (!$this->checkSavepointExist($savepointName)) throw new DatabaseException("savepoint with name '{$savepointName}' nor registered"); - $query = 'rollback to savepoint '.$this->savepointName; + $query = 'rollback to savepoint '.$savepointName; if ($this->toQueue) $this->queue[] = $query; else diff --git a/core/DB/Transaction/InnerTransactionWrapper.class.php b/core/DB/Transaction/InnerTransactionWrapper.class.php index ed59ddb3a2..c9574d1ba8 100644 --- a/core/DB/Transaction/InnerTransactionWrapper.class.php +++ b/core/DB/Transaction/InnerTransactionWrapper.class.php @@ -16,10 +16,23 @@ **/ final class InnerTransactionWrapper { + /** + * @var DB + */ private $db = null; + /** + * @var StorableDAO + */ private $dao = null; private $function = null; + private $exceptionFunction = null; + /** + * @var IsolationLevel + */ private $level = null; + /** + * @var AccessMode + */ private $mode = null; /** @@ -42,7 +55,7 @@ public function setDB(DB $db) /** * @param StorableDAO $dao - * @return InnerTransactionWrapper + * @return InnerTransactionWrapper */ public function setDao(StorableDAO $dao) { @@ -51,8 +64,8 @@ public function setDao(StorableDAO $dao) } /** - * @param mixed $function - * @return InnerTransactionWrapper + * @param collable $function + * @return InnerTransactionWrapper */ public function setFunction($function) { @@ -60,10 +73,21 @@ public function setFunction($function) $this->function = $function; return $this; } + + /** + * @param collable $function + * @return InnerTransactionWrapper + */ + public function setExceptionFunction($function) + { + Assert::isTrue(is_callable($function, false), '$function must be callable'); + $this->exceptionFunction = $function; + return $this; + } /** * @param IsolationLevel $level - * @return InnerTransactionWrapper + * @return InnerTransactionWrapper */ public function setLevel(IsolationLevel $level) { @@ -73,7 +97,7 @@ public function setLevel(IsolationLevel $level) /** * @param AccessMode $mode - * @return InnerTransactionWrapper + * @return InnerTransactionWrapper */ public function setMode(AccessMode $mode) { @@ -96,11 +120,13 @@ public function run() $result = call_user_func_array($this->function, func_get_args()); $transaction->commit(); return $result; - } catch (InnerTransactionWrapperException $e) { - $transaction->rollback(); - return $e->getReturnValue(); } catch (Exception $e) { $transaction->rollback(); + if ($this->exceptionFunction) { + $args = func_get_args(); + array_unshift($args, $e); + return call_user_func_array($this->exceptionFunction, $args); + } throw $e; } } diff --git a/core/DB/Transaction/InnerTransactionWrapperException.class.php b/core/DB/Transaction/InnerTransactionWrapperException.class.php deleted file mode 100644 index 58116e6788..0000000000 --- a/core/DB/Transaction/InnerTransactionWrapperException.class.php +++ /dev/null @@ -1,50 +0,0 @@ -setReturnValue($returnValue); - } - - /** - * @param mixed $returnValue - * @return InnerTransactionWrapperException - **/ - public function setReturnValue($returnValue) - { - $this->returnValue = $returnValue; - return $this; - } - - /** - * @return mixed - **/ - public function getReturnValue() - { - return $this->returnValue; - } - } -?> \ No newline at end of file diff --git a/test/config.inc.php.tpl b/test/config.inc.php.tpl index 66c7b1f7c7..847ed05472 100644 --- a/test/config.inc.php.tpl +++ b/test/config.inc.php.tpl @@ -37,7 +37,7 @@ 'host' => '127.0.0.1', 'base' => 'onphp' ), - 'SQLite' => array( + 'SQLitePDO' => array( 'user' => 'onphp', 'pass' => 'onphp', 'host' => '127.0.0.1', diff --git a/test/core/InnerTransactionTest.class.php b/test/core/InnerTransactionTest.class.php index 38cc8baf0e..2b6edebcef 100644 --- a/test/core/InnerTransactionTest.class.php +++ b/test/core/InnerTransactionTest.class.php @@ -107,7 +107,7 @@ public function testWrapCommit() $this->assertEquals($foo . $bar, $wrapper->run($foo)); } - public function testWrapRollbackByWrapException() + public function testWrapRollbackByException() { $db = $this->spawnDb(array( 'begin' => 1, @@ -117,11 +117,16 @@ public function testWrapRollbackByWrapException() $foo = 'foo'; $bar = 'bar'; + $catchExceptionFunc = function ($e, $foo, $bar) { + return $e->getMessage().' '.$foo.$bar; + }; + $wrapper = InnerTransactionWrapper::create()-> setDB($db)-> - setFunction(array($this, 'wrapExceptionFunction')); + setFunction(array($this, 'wrapExceptionFunction'))-> + setExceptionFunction($catchExceptionFunc); - $this->assertEquals($foo . $bar, $wrapper->run($foo, $bar)); + $this->assertEquals('some unimplemented feature foobar', $wrapper->run($foo, $bar)); } public function testWrapRollbackByOtherException() @@ -148,11 +153,11 @@ public function testWrapRollbackByOtherException() public function wrapExceptionFunction($foo, $bar) { - throw InnerTransactionWrapperException::createValue($foo.$bar); + throw new UnimplementedFeatureException('some unimplemented feature'); } /** - * @param array $options + * @param array $options * @return DB */ private function spawnDb($options = array()) diff --git a/test/misc/DAOTest.class.php b/test/misc/DAOTest.class.php index e7ca277076..e0e2baaf21 100644 --- a/test/misc/DAOTest.class.php +++ b/test/misc/DAOTest.class.php @@ -91,6 +91,42 @@ public function testBoolean() $this->drop(); } + public function testInnerTransaction() + { + $this->create(); + + foreach (DBTestPool::me()->getPool() as $connector => $db) { + DBPool::me()->setDefault($db); + $this->fill(); + + $moscow = TestCity::dao()->getByLogic(Expression::eq('name', 'Moscow')); + $piter = TestCity::dao()->getByLogic(Expression::eq('name', 'Saint-Peterburg')); + + $cityNewer = function(TestCity $city) { + $city->dao()->merge($city->setName('New '.$city->getName())); + }; + + $citiesNewer = function($moscow, $piter) use ($cityNewer, $db) { + $cityNewer($moscow); + + InnerTransactionWrapper::create()-> + setDB($db)-> + setFunction($cityNewer)-> + run($piter); + }; + + InnerTransactionWrapper::create()-> + setDao($moscow->dao())-> + setFunction($citiesNewer)-> + run($moscow, $piter); + + $this->assertNotNull(TestCity::dao()->getByLogic(Expression::eq('name', 'New Moscow'))); + $this->assertNotNull(TestCity::dao()->getByLogic(Expression::eq('name', 'New Saint-Peterburg'))); + } + + $this->drop(); + } + public function testCriteria() { $this->create(); From d0063a735a587ad37f899ae1d68d8fb674ec8f1c Mon Sep 17 00:00:00 2001 From: AlexeyDsov Date: Fri, 18 May 2012 15:38:38 +0400 Subject: [PATCH 4/4] added private DB::assertSavePointName --- core/DB/DB.class.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/core/DB/DB.class.php b/core/DB/DB.class.php index de4ceace93..2bbf12992c 100644 --- a/core/DB/DB.class.php +++ b/core/DB/DB.class.php @@ -234,6 +234,7 @@ public function isQueueActive() */ public function savepointBegin($savepointName) { + $this->assertSavePointName($savepointName); if (!$this->inTransaction()) throw new DatabaseException('To use savepoint begin transaction first'); @@ -252,8 +253,9 @@ public function savepointBegin($savepointName) */ public function savepointRelease($savepointName) { + $this->assertSavePointName($savepointName); if (!$this->inTransaction()) - throw new DatabaseException('To release savepoint begin transaction first'); + throw new DatabaseException('To release savepoint need first begin transaction'); if (!$this->checkSavepointExist($savepointName)) throw new DatabaseException("savepoint with name '{$savepointName}' nor registered"); @@ -273,8 +275,9 @@ public function savepointRelease($savepointName) */ public function savepointRollback($savepointName) { + $this->assertSavePointName($savepointName); if (!$this->inTransaction()) - throw new DatabaseException('To rollback savepoint begin transaction first'); + throw new DatabaseException('To rollback savepoint need first begin transaction'); if (!$this->checkSavepointExist($savepointName)) throw new DatabaseException("savepoint with name '{$savepointName}' nor registered"); @@ -428,5 +431,10 @@ private function checkSavepointExist($savepointName) { return isset($this->savepointList[$savepointName]); } + + private function assertSavePointName($savepointName) + { + Assert::isEqual(1, preg_match('~^[A-Za-z][A-Za-z0-9]*$~iu', $savepointName)); + } } ?> \ No newline at end of file