From 02bddbcf52b8d678feea122135dd66fcaf1c9eb2 Mon Sep 17 00:00:00 2001 From: zYne Date: Sun, 22 Oct 2006 20:50:27 +0000 Subject: [PATCH] Support for transaction isolation levels, fixes #186 --- lib/Doctrine/Connection.php | 16 ++++++ lib/Doctrine/Connection/Firebird.php | 35 ++++++++++++- lib/Doctrine/Connection/Informix.php | 31 +++++++++-- lib/Doctrine/Connection/Mssql.php | 26 ++++++++- lib/Doctrine/Connection/Mysql.php | 54 ++++++++++++++++++- lib/Doctrine/Connection/Oracle.php | 52 +++++++++++++++++- lib/Doctrine/Connection/Pgsql.php | 50 +++++++++++++++++- lib/Doctrine/Connection/Sqlite.php | 27 ++++++++++ lib/Doctrine/Connection/Transaction.php | 2 +- lib/Doctrine/DataDict/Mssql.php | 70 ++++++++++++------------- 10 files changed, 315 insertions(+), 48 deletions(-) diff --git a/lib/Doctrine/Connection.php b/lib/Doctrine/Connection.php index 22d837824..b02e234fd 100644 --- a/lib/Doctrine/Connection.php +++ b/lib/Doctrine/Connection.php @@ -136,6 +136,22 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun } return $this->dataDict; } + /** + * Set the transacton isolation level. + * (implemented by the connection drivers) + * + * @param string standard isolation level + * READ UNCOMMITTED (allows dirty reads) + * READ COMMITTED (prevents dirty reads) + * REPEATABLE READ (prevents nonrepeatable reads) + * SERIALIZABLE (prevents phantom reads) + * @throws Doctrine_Connection_Mysql_Exception if using unknown isolation level + * @throws PDOException if something fails at the PDO level + * @return void + */ + public function setTransactionIsolation($isolation) { + throw new Doctrine_Connection_Exception('Transaction isolation levels not supported by this database driver.'); + } /** * getRegexpOperator * returns the regular expression operator diff --git a/lib/Doctrine/Connection/Firebird.php b/lib/Doctrine/Connection/Firebird.php index 4e680ba59..5e5c0c939 100644 --- a/lib/Doctrine/Connection/Firebird.php +++ b/lib/Doctrine/Connection/Firebird.php @@ -1,9 +1,40 @@ . + */ +Doctrine::autoload('Doctrine_Connection'); /** - * firebird driver + * Doctrine_Connection_Firebird + * + * @package Doctrine ORM + * @url www.phpdoctrine.com + * @license LGPL */ class Doctrine_Connection_Firebird extends Doctrine_Connection { - public function modifyLimitQuery($query,$limit,$offset) { + /** + * Adds an driver-specific LIMIT clause to the query + * + * @param string $query + * @param mixed $limit + * @param mixed $offset + */ + public function modifyLimitQuery($query, $limit, $offset) { return preg_replace('/^([\s(])*SELECT(?!\s*FIRST\s*\d+)/i', "SELECT FIRST $limit SKIP $offset", $query); } diff --git a/lib/Doctrine/Connection/Informix.php b/lib/Doctrine/Connection/Informix.php index 05dbee7c1..a5b1519f4 100644 --- a/lib/Doctrine/Connection/Informix.php +++ b/lib/Doctrine/Connection/Informix.php @@ -1,6 +1,29 @@ . */ -class Doctrine_Connection_Informix extends Doctrine_Connection { } - +Doctrine::autoload('Doctrine_Connection'); +/** + * Doctrine_Connection_Mysql + * + * @package Doctrine ORM + * @url www.phpdoctrine.com + * @license LGPL + */ +class Doctrine_Connection_Informix extends Doctrine_Connection { } diff --git a/lib/Doctrine/Connection/Mssql.php b/lib/Doctrine/Connection/Mssql.php index f94a32cd3..b414b73e6 100644 --- a/lib/Doctrine/Connection/Mssql.php +++ b/lib/Doctrine/Connection/Mssql.php @@ -1,6 +1,30 @@ . + */ +Doctrine::autoload('Doctrine_Connection'); /** - * mssql driver + * Doctrine_Connection_Mssql + * + * @package Doctrine ORM + * @url www.phpdoctrine.com + * @license LGPL */ class Doctrine_Connection_Mssql extends Doctrine_Connection { /** diff --git a/lib/Doctrine/Connection/Mysql.php b/lib/Doctrine/Connection/Mysql.php index 7a9845243..ba4e4c6aa 100644 --- a/lib/Doctrine/Connection/Mysql.php +++ b/lib/Doctrine/Connection/Mysql.php @@ -1,7 +1,30 @@ . + */ +Doctrine::autoload('Doctrine_Connection_Common'); /** - * mysql driver + * Doctrine_Connection_Mysql + * + * @package Doctrine ORM + * @url www.phpdoctrine.com + * @license LGPL */ class Doctrine_Connection_Mysql extends Doctrine_Connection_Common { @@ -23,6 +46,33 @@ class Doctrine_Connection_Mysql extends Doctrine_Connection_Common { public function getRegexpOperator() { return 'RLIKE'; } + /** + * Set the transacton isolation level. + * + * @param string standard isolation level + * READ UNCOMMITTED (allows dirty reads) + * READ COMMITTED (prevents dirty reads) + * REPEATABLE READ (prevents nonrepeatable reads) + * SERIALIZABLE (prevents phantom reads) + * @throws Doctrine_Connection_Mysql_Exception if using unknown isolation level + * @throws PDOException if something fails at the PDO level + * @return void + */ + public function setTransactionIsolation($isolation) { + switch ($isolation) { + case 'READ UNCOMMITTED': + case 'READ COMMITTED': + case 'REPEATABLE READ': + case 'SERIALIZABLE': + + break; + default: + throw new Doctrine_Connection_Mysql_Exception('Isolation level ' . $isolation . ' is not supported.'); + } + + $query = "SET SESSION TRANSACTION ISOLATION LEVEL $isolation"; + return $this->dbh->query($query); + } /** * Returns string to concatenate two or more string parameters * diff --git a/lib/Doctrine/Connection/Oracle.php b/lib/Doctrine/Connection/Oracle.php index c08a07634..b5703020e 100644 --- a/lib/Doctrine/Connection/Oracle.php +++ b/lib/Doctrine/Connection/Oracle.php @@ -1,6 +1,30 @@ . + */ +Doctrine::autoload('Doctrine_Connection'); /** - * oracle driver + * Doctrine_Connection_Oracle + * + * @package Doctrine ORM + * @url www.phpdoctrine.com + * @license LGPL */ class Doctrine_Connection_Oracle extends Doctrine_Connection { /** @@ -18,6 +42,32 @@ class Doctrine_Connection_Oracle extends Doctrine_Connection { $query = "SELECT $fields FROM (SELECT rownum as linenum, $fields FROM ($query) WHERE rownum <= ($offset + $limit)) WHERE linenum >= ".++$offset; return $query; } + /** + * Set the transacton isolation level. + * + * @param string standard isolation level + * READ UNCOMMITTED (allows dirty reads) + * READ COMMITTED (prevents dirty reads) + * REPEATABLE READ (prevents nonrepeatable reads) + * SERIALIZABLE (prevents phantom reads) + * @return mixed MDB2_OK on success, a MDB2 error on failure + */ + function setTransactionIsolation($isolation) { + switch ($isolation) { + case 'READ UNCOMMITTED': + $isolation = 'READ COMMITTED'; + case 'READ COMMITTED': + case 'REPEATABLE READ': + $isolation = 'SERIALIZABLE'; + case 'SERIALIZABLE': + break; + default: + throw new Doctrine_Connection_Oracle_Exception('Isolation level ' . $isolation . 'is not supported.'); + } + + $query = 'ALTER SESSION ISOLATION LEVEL ' . $isolation; + return $this->dbh->query($query); + } /** * returns the next value in the given sequence * @param string $sequence diff --git a/lib/Doctrine/Connection/Pgsql.php b/lib/Doctrine/Connection/Pgsql.php index bf0fa7889..37fbfb3c2 100644 --- a/lib/Doctrine/Connection/Pgsql.php +++ b/lib/Doctrine/Connection/Pgsql.php @@ -1,7 +1,30 @@ . + */ +Doctrine::autoload("Doctrine_Connection_Common"); /** - * pgsql driver + * Doctrine_Connection_Pgsql + * + * @package Doctrine ORM + * @url www.phpdoctrine.com + * @license LGPL */ class Doctrine_Connection_Pgsql extends Doctrine_Connection_Common { /** @@ -14,6 +37,29 @@ class Doctrine_Connection_Pgsql extends Doctrine_Connection_Common { $data = $stmt->fetch(PDO::FETCH_NUM); return $data[0]; } + /** + * Set the transacton isolation level. + * + * @param string standard isolation level + * READ UNCOMMITTED (allows dirty reads) + * READ COMMITTED (prevents dirty reads) + * REPEATABLE READ (prevents nonrepeatable reads) + * SERIALIZABLE (prevents phantom reads) + * @return void + */ + function setTransactionIsolation($isolation) { + switch ($isolation) { + case 'READ UNCOMMITTED': + case 'READ COMMITTED': + case 'REPEATABLE READ': + case 'SERIALIZABLE': + break; + throw new Doctrine_Connection_Pgsql_Exception('Isolation level '.$isolation.' is not supported.'); + } + + $query = 'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL ' . $isolation; + return $this->dbh->query($query); + } /** * getRegexpOperator * diff --git a/lib/Doctrine/Connection/Sqlite.php b/lib/Doctrine/Connection/Sqlite.php index 3706314bc..b485943c2 100644 --- a/lib/Doctrine/Connection/Sqlite.php +++ b/lib/Doctrine/Connection/Sqlite.php @@ -45,6 +45,33 @@ class Doctrine_Connection_Sqlite extends Doctrine_Connection_Common { return 'datetime(\'now\')'; } } + /** + * Set the transacton isolation level. + * + * @param string standard isolation level + * READ UNCOMMITTED (allows dirty reads) + * READ COMMITTED (prevents dirty reads) + * REPEATABLE READ (prevents nonrepeatable reads) + * SERIALIZABLE (prevents phantom reads) + * @return void + */ + function setTransactionIsolation($isolation) { + switch ($isolation) { + case 'READ UNCOMMITTED': + $isolation = 0; + break; + case 'READ COMMITTED': + case 'REPEATABLE READ': + case 'SERIALIZABLE': + $isolation = 1; + break; + default: + throw new Doctrine_Connection_Sqlite_Exception('Isolation level ' . $isolation . 'is not supported.'); + } + + $query = "PRAGMA read_uncommitted=$isolation"; + return $this->_doQuery($query, true); + } /** * return string to call a function to get a substring inside an SQL statement * diff --git a/lib/Doctrine/Connection/Transaction.php b/lib/Doctrine/Connection/Transaction.php index fcb7ee40a..d99852e79 100644 --- a/lib/Doctrine/Connection/Transaction.php +++ b/lib/Doctrine/Connection/Transaction.php @@ -154,7 +154,7 @@ class Doctrine_Connection_Transaction implements Countable, IteratorAggregate { } catch(Exception $e) { $this->rollback(); - throw new Doctrine_Exception($e->__toString()); + throw new Doctrine_Connection_Transaction_Exception($e->__toString()); } if(count($this->invalid) > 0) { diff --git a/lib/Doctrine/DataDict/Mssql.php b/lib/Doctrine/DataDict/Mssql.php index fa1887cbc..ee3de0e65 100644 --- a/lib/Doctrine/DataDict/Mssql.php +++ b/lib/Doctrine/DataDict/Mssql.php @@ -32,7 +32,7 @@ class Doctrine_DataDict_Mssql extends Doctrine_DataDict { /** * Obtain DBMS specific SQL code portion needed to declare an text type * field to be used in statements like CREATE TABLE. - * + * * @param array $field associative array with the name of the properties * of the field being declared as array indexes. Currently, the types * of supported field properties are as follows: @@ -109,48 +109,48 @@ class Doctrine_DataDict_Mssql extends Doctrine_DataDict { // todo: unsigned handling seems to be missing $unsigned = $fixed = null; switch ($db_type) { - case 'bit': - $type[0] = 'boolean'; + case 'bit': + $type[0] = 'boolean'; break; - case 'int': - $type[0] = 'integer'; + case 'int': + $type[0] = 'integer'; break; - case 'datetime': - $type[0] = 'timestamp'; + case 'datetime': + $type[0] = 'timestamp'; break; - case 'float': - case 'real': - case 'numeric': - $type[0] = 'float'; + case 'float': + case 'real': + case 'numeric': + $type[0] = 'float'; break; - case 'decimal': - case 'money': - $type[0] = 'decimal'; + case 'decimal': + case 'money': + $type[0] = 'decimal'; break; - case 'text': - case 'varchar': - $fixed = false; - case 'char': - $type[0] = 'text'; - if ($length == '1') { - $type[] = 'boolean'; - if (preg_match('/^[is|has]/', $field['name'])) { - $type = array_reverse($type); + case 'text': + case 'varchar': + $fixed = false; + case 'char': + $type[0] = 'text'; + if ($length == '1') { + $type[] = 'boolean'; + if (preg_match('/^[is|has]/', $field['name'])) { + $type = array_reverse($type); + } + } elseif (strstr($db_type, 'text')) { + $type[] = 'clob'; + } + if ($fixed !== false) { + $fixed = true; } - } elseif (strstr($db_type, 'text')) { - $type[] = 'clob'; - } - if ($fixed !== false) { - $fixed = true; - } break; - case 'image': - case 'varbinary': - $type[] = 'blob'; - $length = null; + case 'image': + case 'varbinary': + $type[] = 'blob'; + $length = null; break; - default: - throw new Doctrine_DataDict_Mssql_Exception('mapNativeDatatype: unknown database attribute type: '.$db_type); + default: + throw new Doctrine_DataDict_Mssql_Exception('mapNativeDatatype: unknown database attribute type: '.$db_type); } return array($type, $length, $unsigned, $fixed);