mirror of
https://github.com/kamilwylegala/cakephp2-php8.git
synced 2024-11-15 03:18:26 +00:00
Merge branch '2.2-nested-transaction' into 2.2
This commit is contained in:
commit
7fe333ce4e
9 changed files with 318 additions and 87 deletions
|
@ -77,17 +77,6 @@ class Mysql extends DboSource {
|
|||
*/
|
||||
protected $_useAlias = true;
|
||||
|
||||
/**
|
||||
* Index of basic SQL commands
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_commands = array(
|
||||
'begin' => 'START TRANSACTION',
|
||||
'commit' => 'COMMIT',
|
||||
'rollback' => 'ROLLBACK'
|
||||
);
|
||||
|
||||
/**
|
||||
* List of engine specific additional field parameters used on table creating
|
||||
*
|
||||
|
@ -262,15 +251,6 @@ class Mysql extends DboSource {
|
|||
return $this->_execute('SHOW VARIABLES LIKE ?', array('character_set_client'))->fetchObject()->Value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the version string of the database server
|
||||
*
|
||||
* @return string The database encoding
|
||||
*/
|
||||
public function getVersion() {
|
||||
return $this->_connection->getAttribute(PDO::ATTR_SERVER_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query charset by collation
|
||||
*
|
||||
|
@ -696,4 +676,13 @@ class Mysql extends DboSource {
|
|||
return $this->config['database'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the server support nested transactions
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function supportNestedTransaction() {
|
||||
return $this->nestedTransaction && version_compare($this->getVersion(), '4.1', '>=');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,17 +33,6 @@ class Postgres extends DboSource {
|
|||
*/
|
||||
public $description = "PostgreSQL DBO Driver";
|
||||
|
||||
/**
|
||||
* Index of basic SQL commands
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_commands = array(
|
||||
'begin' => 'BEGIN',
|
||||
'commit' => 'COMMIT',
|
||||
'rollback' => 'ROLLBACK'
|
||||
);
|
||||
|
||||
/**
|
||||
* Base driver configuration settings. Merged with user settings.
|
||||
*
|
||||
|
@ -906,4 +895,13 @@ class Postgres extends DboSource {
|
|||
return $this->config['schema'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the server support nested transactions
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function supportNestedTransaction() {
|
||||
return $this->nestedTransaction && version_compare($this->getVersion(), '8.0', '>=');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -559,4 +559,13 @@ class Sqlite extends DboSource {
|
|||
return "main"; // Sqlite Datasource does not support multidb
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the server support nested transactions
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function supportNestedTransaction() {
|
||||
return $this->nestedTransaction && version_compare($this->getVersion(), '3.6.8', '>=');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -98,31 +98,12 @@ class Sqlserver extends DboSource {
|
|||
'boolean' => array('name' => 'bit')
|
||||
);
|
||||
|
||||
/**
|
||||
* Index of basic SQL commands
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_commands = array(
|
||||
'begin' => 'BEGIN TRANSACTION',
|
||||
'commit' => 'COMMIT',
|
||||
'rollback' => 'ROLLBACK'
|
||||
);
|
||||
|
||||
/**
|
||||
* Magic column name used to provide pagination support for SQLServer 2008
|
||||
* which lacks proper limit/offset support.
|
||||
*/
|
||||
const ROW_COUNTER = '_cake_page_rownum_';
|
||||
|
||||
/**
|
||||
* The version of SQLServer being used. If greater than 11
|
||||
* Normal limit offset statements will be used
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_version;
|
||||
|
||||
/**
|
||||
* Connects to the database using options in the given configuration array.
|
||||
*
|
||||
|
@ -151,7 +132,6 @@ class Sqlserver extends DboSource {
|
|||
throw new MissingConnectionException(array('class' => $e->getMessage()));
|
||||
}
|
||||
|
||||
$this->_version = $this->_connection->getAttribute(PDO::ATTR_SERVER_VERSION);
|
||||
return $this->connected;
|
||||
}
|
||||
|
||||
|
@ -515,7 +495,7 @@ class Sqlserver extends DboSource {
|
|||
}
|
||||
|
||||
// For older versions use the subquery version of pagination.
|
||||
if (version_compare($this->_version, '11', '<') && preg_match('/FETCH\sFIRST\s+([0-9]+)/i', $limit, $offset)) {
|
||||
if (version_compare($this->getVersion(), '11', '<') && preg_match('/FETCH\sFIRST\s+([0-9]+)/i', $limit, $offset)) {
|
||||
preg_match('/OFFSET\s*(\d+)\s*.*?(\d+)\s*ROWS/', $limit, $limitOffset);
|
||||
|
||||
$limit = 'TOP ' . intval($limitOffset[2]);
|
||||
|
|
|
@ -69,6 +69,15 @@ class DboSource extends DataSource {
|
|||
*/
|
||||
public $cacheMethods = true;
|
||||
|
||||
/**
|
||||
* Flag to support nested transactions. If it is set to false, you will be able to use
|
||||
* the transaction methods (begin/commit/rollback), but just the global transaction will
|
||||
* be executed.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $nestedTransaction = true;
|
||||
|
||||
/**
|
||||
* Print full query debug info?
|
||||
*
|
||||
|
@ -183,17 +192,6 @@ class DboSource extends DataSource {
|
|||
*/
|
||||
protected $_transactionNesting = 0;
|
||||
|
||||
/**
|
||||
* Index of basic SQL commands
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_commands = array(
|
||||
'begin' => 'BEGIN',
|
||||
'commit' => 'COMMIT',
|
||||
'rollback' => 'ROLLBACK'
|
||||
);
|
||||
|
||||
/**
|
||||
* Default fields that are used by the DBO
|
||||
*
|
||||
|
@ -294,12 +292,21 @@ class DboSource extends DataSource {
|
|||
/**
|
||||
* Get the underlying connection object.
|
||||
*
|
||||
* @return PDOConnection
|
||||
* @return PDO
|
||||
*/
|
||||
public function getConnection() {
|
||||
return $this->_connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the version string of the database server
|
||||
*
|
||||
* @return string The database version
|
||||
*/
|
||||
public function getVersion() {
|
||||
return $this->_connection->getAttribute(PDO::ATTR_SERVER_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a quoted and escaped string of $data for use in an SQL statement.
|
||||
*
|
||||
|
@ -2019,6 +2026,15 @@ class DboSource extends DataSource {
|
|||
return $this->execute('TRUNCATE TABLE ' . $this->fullTableName($table));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the server support nested transactions
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function supportNestedTransaction() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin a transaction
|
||||
*
|
||||
|
@ -2027,15 +2043,33 @@ class DboSource extends DataSource {
|
|||
* or a transaction has not started).
|
||||
*/
|
||||
public function begin() {
|
||||
if ($this->_transactionStarted || $this->_connection->beginTransaction()) {
|
||||
if ($this->fullDebug && empty($this->_transactionNesting)) {
|
||||
$this->logQuery('BEGIN');
|
||||
if ($this->_transactionStarted) {
|
||||
if ($this->supportNestedTransaction()) {
|
||||
return $this->_beginNested();
|
||||
}
|
||||
$this->_transactionStarted = true;
|
||||
$this->_transactionNesting++;
|
||||
return true;
|
||||
return $this->_transactionStarted;
|
||||
}
|
||||
return false;
|
||||
|
||||
$this->_transactionNesting = 0;
|
||||
if ($this->fullDebug) {
|
||||
$this->logQuery('BEGIN');
|
||||
}
|
||||
return $this->_transactionStarted = $this->_connection->beginTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin a nested transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _beginNested() {
|
||||
$query = 'SAVEPOINT LEVEL' . ++$this->_transactionNesting;
|
||||
if ($this->fullDebug) {
|
||||
$this->logQuery($query);
|
||||
}
|
||||
$this->_connection->exec($query);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2046,19 +2080,38 @@ class DboSource extends DataSource {
|
|||
* or a transaction has not started).
|
||||
*/
|
||||
public function commit() {
|
||||
if ($this->_transactionStarted) {
|
||||
$this->_transactionNesting--;
|
||||
if ($this->_transactionNesting <= 0) {
|
||||
$this->_transactionStarted = false;
|
||||
$this->_transactionNesting = 0;
|
||||
if ($this->fullDebug) {
|
||||
$this->logQuery('COMMIT');
|
||||
}
|
||||
return $this->_connection->commit();
|
||||
}
|
||||
return true;
|
||||
if (!$this->_transactionStarted) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
if ($this->_transactionNesting === 0) {
|
||||
if ($this->fullDebug) {
|
||||
$this->logQuery('COMMIT');
|
||||
}
|
||||
$this->_transactionStarted = false;
|
||||
return $this->_connection->commit();
|
||||
}
|
||||
|
||||
if ($this->supportNestedTransaction()) {
|
||||
return $this->_commitNested();
|
||||
}
|
||||
|
||||
$this->_transactionNesting--;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit a nested transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _commitNested() {
|
||||
$query = 'RELEASE SAVEPOINT LEVEL' . $this->_transactionNesting--;
|
||||
if ($this->fullDebug) {
|
||||
$this->logQuery($query);
|
||||
}
|
||||
$this->_connection->exec($query);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2069,15 +2122,38 @@ class DboSource extends DataSource {
|
|||
* or a transaction has not started).
|
||||
*/
|
||||
public function rollback() {
|
||||
if ($this->_transactionStarted && $this->_connection->rollBack()) {
|
||||
if (!$this->_transactionStarted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->_transactionNesting === 0) {
|
||||
if ($this->fullDebug) {
|
||||
$this->logQuery('ROLLBACK');
|
||||
}
|
||||
$this->_transactionStarted = false;
|
||||
$this->_transactionNesting = 0;
|
||||
return true;
|
||||
return $this->_connection->rollBack();
|
||||
}
|
||||
return false;
|
||||
|
||||
if ($this->supportNestedTransaction()) {
|
||||
return $this->_rollbackNested();
|
||||
}
|
||||
|
||||
$this->_transactionNesting--;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback a nested transaction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _rollbackNested() {
|
||||
$query = 'ROLLBACK TO SAVEPOINT LEVEL' . $this->_transactionNesting--;
|
||||
if ($this->fullDebug) {
|
||||
$this->logQuery($query);
|
||||
}
|
||||
$this->_connection->exec($query);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -45,7 +45,7 @@ class MysqlTest extends CakeTestCase {
|
|||
public $fixtures = array(
|
||||
'core.apple', 'core.article', 'core.articles_tag', 'core.attachment', 'core.comment',
|
||||
'core.sample', 'core.tag', 'core.user', 'core.post', 'core.author', 'core.data_test',
|
||||
'core.binary_test'
|
||||
'core.binary_test', 'app.address'
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -3579,4 +3579,38 @@ class MysqlTest extends CakeTestCase {
|
|||
->with("TRUNCATE TABLE `$schema`.`tbl_articles`");
|
||||
$this->Dbo->truncate('articles');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test nested transaction
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testNestedTransaction() {
|
||||
$this->skipIf($this->Dbo->supportNestedTransaction() === false, 'The MySQL server do not support nested transaction');
|
||||
|
||||
$this->loadFixtures('Address');
|
||||
$model = ClassRegistry::init('Address');
|
||||
$model->hasOne = $model->hasMany = $model->belongsTo = $model->hasAndBelongsToMany = array();
|
||||
$model->cacheQueries = false;
|
||||
$this->Dbo->cacheMethods = false;
|
||||
|
||||
$this->assertTrue($this->Dbo->begin());
|
||||
$this->assertNotEmpty($model->read(null, 1));
|
||||
|
||||
$this->assertTrue($this->Dbo->begin());
|
||||
$this->assertTrue($model->delete(1));
|
||||
$this->assertEmpty($model->read(null, 1));
|
||||
$this->assertTrue($this->Dbo->rollback());
|
||||
$this->assertNotEmpty($model->read(null, 1));
|
||||
|
||||
$this->assertTrue($this->Dbo->begin());
|
||||
$this->assertTrue($model->delete(1));
|
||||
$this->assertEmpty($model->read(null, 1));
|
||||
$this->assertTrue($this->Dbo->commit());
|
||||
$this->assertEmpty($model->read(null, 1));
|
||||
|
||||
$this->assertTrue($this->Dbo->rollback());
|
||||
$this->assertNotEmpty($model->read(null, 1));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -909,4 +909,37 @@ class PostgresTest extends CakeTestCase {
|
|||
$this->Dbo->truncate('articles');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test nested transaction
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testNestedTransaction() {
|
||||
$this->skipIf($this->Dbo->supportNestedTransaction() === false, 'The Postgres server do not support nested transaction');
|
||||
|
||||
$this->loadFixtures('Article');
|
||||
$model = new Article();
|
||||
$model->hasOne = $model->hasMany = $model->belongsTo = $model->hasAndBelongsToMany = array();
|
||||
$model->cacheQueries = false;
|
||||
$this->Dbo->cacheMethods = false;
|
||||
|
||||
$this->assertTrue($this->Dbo->begin());
|
||||
$this->assertNotEmpty($model->read(null, 1));
|
||||
|
||||
$this->assertTrue($this->Dbo->begin());
|
||||
$this->assertTrue($model->delete(1));
|
||||
$this->assertEmpty($model->read(null, 1));
|
||||
$this->assertTrue($this->Dbo->rollback());
|
||||
$this->assertNotEmpty($model->read(null, 1));
|
||||
|
||||
$this->assertTrue($this->Dbo->begin());
|
||||
$this->assertTrue($model->delete(1));
|
||||
$this->assertEmpty($model->read(null, 1));
|
||||
$this->assertTrue($this->Dbo->commit());
|
||||
$this->assertEmpty($model->read(null, 1));
|
||||
|
||||
$this->assertTrue($this->Dbo->rollback());
|
||||
$this->assertNotEmpty($model->read(null, 1));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -383,4 +383,37 @@ class SqliteTest extends CakeTestCase {
|
|||
$this->assertTrue(Validation::uuid($result['Uuid']['id']), 'Not a uuid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test nested transaction
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testNestedTransaction() {
|
||||
$this->skipIf($this->Dbo->supportNestedTransaction() === false, 'The Sqlite version do not support nested transaction');
|
||||
|
||||
$this->loadFixtures('User');
|
||||
$model = new User();
|
||||
$model->hasOne = $model->hasMany = $model->belongsTo = $model->hasAndBelongsToMany = array();
|
||||
$model->cacheQueries = false;
|
||||
$this->Dbo->cacheMethods = false;
|
||||
|
||||
$this->assertTrue($this->Dbo->begin());
|
||||
$this->assertNotEmpty($model->read(null, 1));
|
||||
|
||||
$this->assertTrue($this->Dbo->begin());
|
||||
$this->assertTrue($model->delete(1));
|
||||
$this->assertEmpty($model->read(null, 1));
|
||||
$this->assertTrue($this->Dbo->rollback());
|
||||
$this->assertNotEmpty($model->read(null, 1));
|
||||
|
||||
$this->assertTrue($this->Dbo->begin());
|
||||
$this->assertTrue($model->delete(1));
|
||||
$this->assertEmpty($model->read(null, 1));
|
||||
$this->assertTrue($this->Dbo->commit());
|
||||
$this->assertEmpty($model->read(null, 1));
|
||||
|
||||
$this->assertTrue($this->Dbo->rollback());
|
||||
$this->assertNotEmpty($model->read(null, 1));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -35,6 +35,8 @@ class MockDataSource extends DataSource {
|
|||
|
||||
class DboTestSource extends DboSource {
|
||||
|
||||
public static $nested = true;
|
||||
|
||||
public function connect($config = array()) {
|
||||
$this->connected = true;
|
||||
}
|
||||
|
@ -51,6 +53,10 @@ class DboTestSource extends DboSource {
|
|||
$this->_connection = $conn;
|
||||
}
|
||||
|
||||
public function supportNestedTransaction() {
|
||||
return $this->nestedTransaction && self::$nested;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -834,6 +840,79 @@ class DboSourceTest extends CakeTestCase {
|
|||
$this->assertEquals($expected, $log['log'][0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test nested transaction calls
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testTransactionNested() {
|
||||
$conn = $this->getMock('MockPDO');
|
||||
$db = new DboTestSource();
|
||||
$db->setConnection($conn);
|
||||
DboTestSource::$nested = true;
|
||||
|
||||
$conn->expects($this->at(0))->method('beginTransaction')->will($this->returnValue(true));
|
||||
$conn->expects($this->at(1))->method('exec')->with($this->equalTo('SAVEPOINT LEVEL1'))->will($this->returnValue(true));
|
||||
$conn->expects($this->at(2))->method('exec')->with($this->equalTo('RELEASE SAVEPOINT LEVEL1'))->will($this->returnValue(true));
|
||||
$conn->expects($this->at(3))->method('exec')->with($this->equalTo('SAVEPOINT LEVEL1'))->will($this->returnValue(true));
|
||||
$conn->expects($this->at(4))->method('exec')->with($this->equalTo('ROLLBACK TO SAVEPOINT LEVEL1'))->will($this->returnValue(true));
|
||||
$conn->expects($this->at(5))->method('commit')->will($this->returnValue(true));
|
||||
|
||||
$this->_runTransactions($db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test nested transaction calls without support
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testTransactionNestedWithoutSupport() {
|
||||
$conn = $this->getMock('MockPDO');
|
||||
$db = new DboTestSource();
|
||||
$db->setConnection($conn);
|
||||
$db->nestedTransaction = false;
|
||||
DboTestSource::$nested = true;
|
||||
|
||||
$conn->expects($this->once())->method('beginTransaction')->will($this->returnValue(true));
|
||||
$conn->expects($this->never())->method('exec');
|
||||
$conn->expects($this->once())->method('commit')->will($this->returnValue(true));
|
||||
|
||||
$this->_runTransactions($db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test nested transaction disabled
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testTransactionNestedDisabled() {
|
||||
$conn = $this->getMock('MockPDO');
|
||||
$db = new DboTestSource();
|
||||
$db->setConnection($conn);
|
||||
DboTestSource::$nested = false;
|
||||
|
||||
$conn->expects($this->once())->method('beginTransaction')->will($this->returnValue(true));
|
||||
$conn->expects($this->never())->method('exec');
|
||||
$conn->expects($this->once())->method('commit')->will($this->returnValue(true));
|
||||
|
||||
$this->_runTransactions($db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Nested transaction calls
|
||||
*
|
||||
* @param DboTestSource $db
|
||||
* @return void
|
||||
*/
|
||||
protected function _runTransactions($db) {
|
||||
$db->begin();
|
||||
$db->begin();
|
||||
$db->commit();
|
||||
$db->begin();
|
||||
$db->rollback();
|
||||
$db->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test build statement with some fields missing
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue