add nested transactions (mysql >= 5.5 only)
This commit is contained in:
52
db.class.php
52
db.class.php
@@ -35,6 +35,7 @@ class DB {
|
|||||||
public static $throw_exception_on_error = false;
|
public static $throw_exception_on_error = false;
|
||||||
public static $nonsql_error_handler = null;
|
public static $nonsql_error_handler = null;
|
||||||
public static $throw_exception_on_nonsql_error = false;
|
public static $throw_exception_on_nonsql_error = false;
|
||||||
|
public static $nested_transactions = false;
|
||||||
|
|
||||||
// internal
|
// internal
|
||||||
protected static $mdb = null;
|
protected static $mdb = null;
|
||||||
@@ -54,10 +55,12 @@ class DB {
|
|||||||
if ($mdb->throw_exception_on_error !== DB::$throw_exception_on_error) $mdb->throw_exception_on_error = DB::$throw_exception_on_error;
|
if ($mdb->throw_exception_on_error !== DB::$throw_exception_on_error) $mdb->throw_exception_on_error = DB::$throw_exception_on_error;
|
||||||
if ($mdb->nonsql_error_handler !== DB::$nonsql_error_handler) $mdb->nonsql_error_handler = DB::$nonsql_error_handler;
|
if ($mdb->nonsql_error_handler !== DB::$nonsql_error_handler) $mdb->nonsql_error_handler = DB::$nonsql_error_handler;
|
||||||
if ($mdb->throw_exception_on_nonsql_error !== DB::$throw_exception_on_nonsql_error) $mdb->throw_exception_on_nonsql_error = DB::$throw_exception_on_nonsql_error;
|
if ($mdb->throw_exception_on_nonsql_error !== DB::$throw_exception_on_nonsql_error) $mdb->throw_exception_on_nonsql_error = DB::$throw_exception_on_nonsql_error;
|
||||||
|
if ($mdb->nested_transactions !== DB::$nested_transactions) $mdb->nested_transactions = DB::$nested_transactions;
|
||||||
|
|
||||||
return $mdb;
|
return $mdb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function get() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'get'), $args); }
|
||||||
public static function query() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'query'), $args); }
|
public static function query() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'query'), $args); }
|
||||||
public static function quickPrepare() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'quickPrepare'), $args); }
|
public static function quickPrepare() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'quickPrepare'), $args); }
|
||||||
public static function queryFirstRow() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'queryFirstRow'), $args); }
|
public static function queryFirstRow() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'queryFirstRow'), $args); }
|
||||||
@@ -94,6 +97,8 @@ class DB {
|
|||||||
public static function sqlEval() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'sqlEval'), $args); }
|
public static function sqlEval() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'sqlEval'), $args); }
|
||||||
public static function nonSQLError() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'nonSQLError'), $args); }
|
public static function nonSQLError() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'nonSQLError'), $args); }
|
||||||
|
|
||||||
|
public static function serverVersion() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'serverVersion'), $args); }
|
||||||
|
|
||||||
public static function debugMode($handler = true) {
|
public static function debugMode($handler = true) {
|
||||||
DB::$success_handler = $handler;
|
DB::$success_handler = $handler;
|
||||||
}
|
}
|
||||||
@@ -119,9 +124,11 @@ class MeekroDB {
|
|||||||
public $throw_exception_on_error = false;
|
public $throw_exception_on_error = false;
|
||||||
public $nonsql_error_handler = null;
|
public $nonsql_error_handler = null;
|
||||||
public $throw_exception_on_nonsql_error = false;
|
public $throw_exception_on_nonsql_error = false;
|
||||||
|
public $nested_transactions = false;
|
||||||
|
|
||||||
// internal
|
// internal
|
||||||
public $internal_mysql = null;
|
public $internal_mysql = null;
|
||||||
|
public $server_info = null;
|
||||||
public $insert_id = 0;
|
public $insert_id = 0;
|
||||||
public $num_rows = 0;
|
public $num_rows = 0;
|
||||||
public $affected_rows = 0;
|
public $affected_rows = 0;
|
||||||
@@ -129,6 +136,7 @@ class MeekroDB {
|
|||||||
public $queryResultType = null;
|
public $queryResultType = null;
|
||||||
public $old_db = null;
|
public $old_db = null;
|
||||||
public $current_db = null;
|
public $current_db = null;
|
||||||
|
public $nested_transactions_count = 0;
|
||||||
|
|
||||||
|
|
||||||
public function __construct($host=null, $user=null, $password=null, $dbName=null, $port=null, $encoding=null) {
|
public function __construct($host=null, $user=null, $password=null, $dbName=null, $port=null, $encoding=null) {
|
||||||
@@ -161,6 +169,7 @@ class MeekroDB {
|
|||||||
|
|
||||||
$mysql->set_charset($this->encoding);
|
$mysql->set_charset($this->encoding);
|
||||||
$this->internal_mysql = $mysql;
|
$this->internal_mysql = $mysql;
|
||||||
|
$this->server_info = $mysql->server_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $mysql;
|
return $mysql;
|
||||||
@@ -184,6 +193,7 @@ class MeekroDB {
|
|||||||
$this->success_handler = $handler;
|
$this->success_handler = $handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function serverVersion() { return $this->server_info; }
|
||||||
public function insertId() { return $this->insert_id; }
|
public function insertId() { return $this->insert_id; }
|
||||||
public function affectedRows() { return $this->affected_rows; }
|
public function affectedRows() { return $this->affected_rows; }
|
||||||
public function count() { $args = func_get_args(); return call_user_func_array(array($this, 'numRows'), $args); }
|
public function count() { $args = func_get_args(); return call_user_func_array(array($this, 'numRows'), $args); }
|
||||||
@@ -199,15 +209,51 @@ class MeekroDB {
|
|||||||
|
|
||||||
|
|
||||||
public function startTransaction() {
|
public function startTransaction() {
|
||||||
$this->queryNull('START TRANSACTION');
|
if ($this->nested_transactions && $this->serverVersion() < '5.5') {
|
||||||
|
return $this->nonSQLError("Nested transactions are only available on MySQL 5.5 and greater. You are using MySQL " . $this->serverVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->nested_transactions || $this->nested_transactions_count == 0) {
|
||||||
|
$this->queryNull('START TRANSACTION');
|
||||||
|
} else {
|
||||||
|
$this->queryNull("SAVEPOINT LEVEL{$this->nested_transactions_count}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->nested_transactions) return ++$this->nested_transactions_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function commit() {
|
public function commit() {
|
||||||
$this->queryNull('COMMIT');
|
if ($this->nested_transactions && $this->serverVersion() < '5.5') {
|
||||||
|
return $this->nonSQLError("Nested transactions are only available on MySQL 5.5 and greater. You are using MySQL " . $this->serverVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->nested_transactions && $this->nested_transactions_count > 0)
|
||||||
|
$this->nested_transactions_count--;
|
||||||
|
|
||||||
|
if (!$this->nested_transactions || $this->nested_transactions_count == 0) {
|
||||||
|
$this->queryNull('COMMIT');
|
||||||
|
} else {
|
||||||
|
$this->queryNull("RELEASE SAVEPOINT LEVEL{$this->nested_transactions_count}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->nested_transactions_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rollback() {
|
public function rollback() {
|
||||||
$this->queryNull('ROLLBACK');
|
if ($this->nested_transactions && $this->serverVersion() < '5.5') {
|
||||||
|
return $this->nonSQLError("Nested transactions are only available on MySQL 5.5 and greater. You are using MySQL " . $this->serverVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->nested_transactions && $this->nested_transactions_count > 0)
|
||||||
|
$this->nested_transactions_count--;
|
||||||
|
|
||||||
|
if (!$this->nested_transactions || $this->nested_transactions_count == 0) {
|
||||||
|
$this->queryNull('ROLLBACK');
|
||||||
|
} else {
|
||||||
|
$this->queryNull("ROLLBACK TO SAVEPOINT LEVEL{$this->nested_transactions_count}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->nested_transactions_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function escape($str) {
|
public function escape($str) {
|
||||||
|
|||||||
24
simpletest/TransactionTest.php
Normal file
24
simpletest/TransactionTest.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?
|
||||||
|
class TransactionTest extends SimpleTest {
|
||||||
|
function test_1_transactions() {
|
||||||
|
DB::$nested_transactions = false;
|
||||||
|
|
||||||
|
DB::query("UPDATE accounts SET age=%i WHERE username=%s", 600, 'Abe');
|
||||||
|
|
||||||
|
DB::startTransaction();
|
||||||
|
DB::query("UPDATE accounts SET age=%i WHERE username=%s", 700, 'Abe');
|
||||||
|
DB::startTransaction();
|
||||||
|
DB::query("UPDATE accounts SET age=%i WHERE username=%s", 800, 'Abe');
|
||||||
|
DB::rollback();
|
||||||
|
|
||||||
|
$age = DB::queryFirstField("SELECT age FROM accounts WHERE username=%s", 'Abe');
|
||||||
|
$this->assert($age == 700);
|
||||||
|
|
||||||
|
DB::rollback();
|
||||||
|
|
||||||
|
$age = DB::queryFirstField("SELECT age FROM accounts WHERE username=%s", 'Abe');
|
||||||
|
$this->assert($age == 700);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
?>
|
||||||
63
simpletest/TransactionTest_55.php
Normal file
63
simpletest/TransactionTest_55.php
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?
|
||||||
|
class TransactionTest_55 extends SimpleTest {
|
||||||
|
function test_1_transactions() {
|
||||||
|
DB::$nested_transactions = true;
|
||||||
|
|
||||||
|
$depth = DB::startTransaction();
|
||||||
|
$this->assert($depth === 1);
|
||||||
|
DB::query("UPDATE accounts SET age=%i WHERE username=%s", 700, 'Abe');
|
||||||
|
|
||||||
|
$depth = DB::startTransaction();
|
||||||
|
$this->assert($depth === 2);
|
||||||
|
DB::query("UPDATE accounts SET age=%i WHERE username=%s", 800, 'Abe');
|
||||||
|
|
||||||
|
$depth = DB::startTransaction();
|
||||||
|
$this->assert($depth === 3);
|
||||||
|
DB::query("UPDATE accounts SET age=%i WHERE username=%s", 500, 'Abe');
|
||||||
|
$depth = DB::commit();
|
||||||
|
|
||||||
|
$this->assert($depth === 2);
|
||||||
|
|
||||||
|
$age = DB::queryFirstField("SELECT age FROM accounts WHERE username=%s", 'Abe');
|
||||||
|
$this->assert($age == 500);
|
||||||
|
|
||||||
|
$depth = DB::rollback();
|
||||||
|
$this->assert($depth === 1);
|
||||||
|
|
||||||
|
$age = DB::queryFirstField("SELECT age FROM accounts WHERE username=%s", 'Abe');
|
||||||
|
$this->assert($age == 700);
|
||||||
|
|
||||||
|
$depth = DB::commit();
|
||||||
|
$this->assert($depth === 0);
|
||||||
|
|
||||||
|
$age = DB::queryFirstField("SELECT age FROM accounts WHERE username=%s", 'Abe');
|
||||||
|
$this->assert($age == 700);
|
||||||
|
|
||||||
|
|
||||||
|
DB::$nested_transactions = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_2_transactions() {
|
||||||
|
DB::$nested_transactions = true;
|
||||||
|
|
||||||
|
DB::query("UPDATE accounts SET age=%i WHERE username=%s", 600, 'Abe');
|
||||||
|
|
||||||
|
DB::startTransaction();
|
||||||
|
DB::query("UPDATE accounts SET age=%i WHERE username=%s", 700, 'Abe');
|
||||||
|
DB::startTransaction();
|
||||||
|
DB::query("UPDATE accounts SET age=%i WHERE username=%s", 800, 'Abe');
|
||||||
|
DB::rollback();
|
||||||
|
|
||||||
|
$age = DB::queryFirstField("SELECT age FROM accounts WHERE username=%s", 'Abe');
|
||||||
|
$this->assert($age == 700);
|
||||||
|
|
||||||
|
DB::rollback();
|
||||||
|
|
||||||
|
$age = DB::queryFirstField("SELECT age FROM accounts WHERE username=%s", 'Abe');
|
||||||
|
$this->assert($age == 600);
|
||||||
|
|
||||||
|
DB::$nested_transactions = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -25,23 +25,25 @@ else $is_php_53 = false;
|
|||||||
|
|
||||||
error_reporting(E_ALL | E_STRICT);
|
error_reporting(E_ALL | E_STRICT);
|
||||||
require_once '../db.class.php';
|
require_once '../db.class.php';
|
||||||
DB::$user = 'meekrodb_test_us';
|
|
||||||
|
|
||||||
include 'test_setup.php'; //test config values go here
|
include 'test_setup.php'; //test config values go here
|
||||||
|
DB::$user = $set_db_user;
|
||||||
DB::$password = $set_password;
|
DB::$password = $set_password;
|
||||||
DB::$dbName = $set_db;
|
DB::$dbName = $set_db;
|
||||||
DB::$host = $set_host;
|
DB::$host = $set_host;
|
||||||
|
DB::get(); //connect to mysql
|
||||||
|
|
||||||
require_once 'BasicTest.php';
|
require_once 'BasicTest.php';
|
||||||
require_once 'ObjectTest.php';
|
require_once 'ObjectTest.php';
|
||||||
require_once 'WhereClauseTest.php';
|
require_once 'WhereClauseTest.php';
|
||||||
require_once 'ErrorTest.php';
|
require_once 'ErrorTest.php';
|
||||||
|
require_once 'TransactionTest.php';
|
||||||
|
|
||||||
$classes_to_test = array(
|
$classes_to_test = array(
|
||||||
'BasicTest',
|
'BasicTest',
|
||||||
'WhereClauseTest',
|
'WhereClauseTest',
|
||||||
'ObjectTest',
|
'ObjectTest',
|
||||||
'ErrorTest',
|
'ErrorTest',
|
||||||
|
'TransactionTest',
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($is_php_53) {
|
if ($is_php_53) {
|
||||||
@@ -51,6 +53,14 @@ if ($is_php_53) {
|
|||||||
echo "PHP 5.3 not detected, skipping 5.3 tests..\n";
|
echo "PHP 5.3 not detected, skipping 5.3 tests..\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$mysql_version = DB::serverVersion();
|
||||||
|
if ($mysql_version >= '5.5') {
|
||||||
|
require_once 'TransactionTest_55.php';
|
||||||
|
$classes_to_test[] = 'TransactionTest_55';
|
||||||
|
} else {
|
||||||
|
echo "MySQL 5.5 not available (version is $mysql_version) -- skipping MySQL 5.5 tests\n";
|
||||||
|
}
|
||||||
|
|
||||||
$time_start = microtime_float();
|
$time_start = microtime_float();
|
||||||
foreach ($classes_to_test as $class) {
|
foreach ($classes_to_test as $class) {
|
||||||
$object = new $class();
|
$object = new $class();
|
||||||
|
|||||||
Reference in New Issue
Block a user