From 8b5cf30ef80bebc4318f18a4a65e3851dcf23029 Mon Sep 17 00:00:00 2001 From: zYne Date: Tue, 14 Nov 2006 18:21:36 +0000 Subject: [PATCH] Updated Pgsql datadict driver, moved transaction isolation functionality to Doctrine_Transaction --- lib/Doctrine/Connection.php | 119 +++++++++++++++++++++----------- lib/Doctrine/DataDict/Mssql.php | 97 ++++++++++++++++++++++---- lib/Doctrine/DataDict/Pgsql.php | 59 ++++++++++++---- lib/Doctrine/Transaction.php | 62 ++++++++++++++--- 4 files changed, 262 insertions(+), 75 deletions(-) diff --git a/lib/Doctrine/Connection.php b/lib/Doctrine/Connection.php index 91ca27d34..fcfc697d2 100644 --- a/lib/Doctrine/Connection.php +++ b/lib/Doctrine/Connection.php @@ -21,14 +21,14 @@ /** * Doctrine_Connection * - * @package Doctrine - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @category Object Relational Mapping - * @link www.phpdoctrine.com - * @since 1.0 - * @version $Revision$ - * @author Konsta Vesterinen - */ + * @package Doctrine + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @category Object Relational Mapping + * @link www.phpdoctrine.com + * @since 1.0 + * @version $Revision$ + * @author Konsta Vesterinen + */ abstract class Doctrine_Connection extends Doctrine_Configurable implements Countable, IteratorAggregate { /** * @var $dbh the database handler @@ -295,41 +295,82 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun throw new Doctrine_Connection_Exception('Altering charset not supported by this driver.'); } /** - * setTransactionIsolation + * fetchAll * - * Set the transacton isolation level. - * (implemented by the connection drivers) - * - * example: - * - * - * $conn->setTransactionIsolation('READ UNCOMMITTED'); - * - * - * @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_Exception if the feature is not supported by the driver - * @throws PDOException if something fails at the PDO level - * @return void + * @param string $statement sql query to be executed + * @param array $params prepared statement params + * @return array */ - public function setTransactionIsolation($isolation) { - throw new Doctrine_Connection_Exception('Transaction isolation levels not supported by this driver.'); + public function fetchAll($statement, array $params = array()) { + return $this->query($statement, $params)->fetchAll(PDO::FETCH_ASSOC); + } + /** + * fetchOne + * + * @param string $statement sql query to be executed + * @param array $params prepared statement params + * @return mixed + */ + public function fetchOne($statement, array $params = array()) { + return current($this->query($statement, $params)->fetch(PDO::FETCH_NUM)); + } + /** + * fetchRow + * + * @param string $statement sql query to be executed + * @param array $params prepared statement params + * @return array + */ + public function fetchRow($statement, array $params = array()) { + return $this->query($statement, $params)->fetch(PDO::FETCH_ASSOC); + } + /** + * fetchArray + * + * @param string $statement sql query to be executed + * @param array $params prepared statement params + * @return array + */ + public function fetchArray($statement, array $params = array()) { + return $this->query($statement, $params)->fetch(PDO::FETCH_NUM); + } + /** + * fetchColumn + * + * @param string $statement sql query to be executed + * @param array $params prepared statement params + * @return array + */ + public function fetchColumn($statement, array $params = array()) { + $result = $this->query($statement, $params)->fetchAll(PDO::FETCH_COLUMN); + + if($this->options['portability'] & Doctrine::PORTABILITY_FIX_CASE) + $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result); + + return $result; + } + /** + * fetchAssoc + * + * @param string $statement sql query to be executed + * @param array $params prepared statement params + * @return array + */ + public function fetchAssoc($statement, array $params = array()) { + return $this->query($statement, $params)->fetchAll(PDO::FETCH_ASSOC); + } + /** + * fetchBoth + * + * @param string $statement sql query to be executed + * @param array $params prepared statement params + * @return array + */ + public function fetchBoth($statement, array $params = array()) { + return $this->query($statement, $params)->fetchAll(PDO::FETCH_BOTH); } - /** - * getTransactionIsolation - * - * @throws Doctrine_Connection_Exception if the feature is not supported by the driver - * @throws PDOException if something fails at the PDO level - * @return string returns the current session transaction isolation level - */ - public function getTransactionIsolation() { - throw new Doctrine_Connection_Exception('Fetching transaction isolation level not supported by this driver.'); - } + /** * query * queries the database using Doctrine Query Language diff --git a/lib/Doctrine/DataDict/Mssql.php b/lib/Doctrine/DataDict/Mssql.php index a217aff57..101a96a43 100644 --- a/lib/Doctrine/DataDict/Mssql.php +++ b/lib/Doctrine/DataDict/Mssql.php @@ -19,15 +19,17 @@ * . */ /** - * @package Doctrine - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @author Konsta Vesterinen + * @package Doctrine + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen * @author Lukas Smith (PEAR MDB2 library) - * @version $Revision$ - * @category Object Relational Mapping - * @link www.phpdoctrine.com - * @since 1.0 - */ + * @author Frank M. Kromann (PEAR MDB2 Mssql driver) + * @author David Coallier (PEAR MDB2 Mssql driver) + * @version $Revision$ + * @category Object Relational Mapping + * @link www.phpdoctrine.com + * @since 1.0 + */ class Doctrine_DataDict_Mssql extends Doctrine_DataDict { /** * Obtain DBMS specific SQL code portion needed to declare an text type @@ -189,7 +191,22 @@ class Doctrine_DataDict_Mssql extends Doctrine_DataDict { * @return array */ public function listSequences($database = null) { - + $query = "SELECT name FROM sysobjects WHERE xtype = 'U'"; + $table_names = $db->queryCol($query); + if (PEAR::isError($table_names)) { + return $table_names; + } + $result = array(); + foreach ($table_names as $table_name) { + if ($sqn = $this->_fixSequenceName($table_name, true)) { + $result[] = $sqn; + } + } + if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { + $result = array_map(($db->options['field_case'] == CASE_LOWER ? + 'strtolower' : 'strtoupper'), $result); + } + return $result; } /** * lists table constraints @@ -207,7 +224,7 @@ class Doctrine_DataDict_Mssql extends Doctrine_DataDict { * @return array */ public function listTableColumns($table) { - $sql = "exec sp_columns @table_name = " . $this->quoteIdentifier($table); + $sql = 'EXEC sp_columns @table_name = ' . $this->quoteIdentifier($table); $result = $this->dbh->query($sql)->fetchAll(PDO::FETCH_ASSOC); $columns = array(); @@ -262,7 +279,24 @@ class Doctrine_DataDict_Mssql extends Doctrine_DataDict { * @return array */ public function listTableTriggers($table) { - + $table = $db->quote($table, 'text'); + $query = "SELECT name FROM sysobjects WHERE xtype = 'TR'"; + if (!is_null($table)) { + $query .= "AND object_name(parent_obj) = $table"; + } + + $result = $db->queryCol($query); + if (PEAR::isError($results)) { + return $result; + } + + if ($db->options['portability'] & Doctrine::PORTABILITY_FIX_CASE && + $db->options['field_case'] == CASE_LOWER) + { + $result = array_map(($db->options['field_case'] == CASE_LOWER ? + 'strtolower' : 'strtoupper'), $result); + } + return $result; } /** * lists table views @@ -271,7 +305,34 @@ class Doctrine_DataDict_Mssql extends Doctrine_DataDict { * @return array */ public function listTableViews($table) { - + $keyName = 'INDEX_NAME'; + $pkName = 'PK_NAME'; + if ($db->options['portability'] & Doctrine::PORTABILITY_FIX_CASE) { + if ($db->options['field_case'] == CASE_LOWER) { + $keyName = strtolower($keyName); + $pkName = strtolower($pkName); + } else { + $keyName = strtoupper($keyName); + $pkName = strtoupper($pkName); + } + } + $table = $db->quote($table, 'text'); + $query = 'EXEC sp_statistics @table_name = ' . $table; + $indexes = $db->queryCol($query, 'text', $keyName); + + $query = 'EXEC sp_pkeys @table_name = ' . $table; + $pkAll = $db->queryCol($query, 'text', $pkName); + $result = array(); + foreach ($indexes as $index) { + if (!in_array($index, $pkAll) && $index != null) { + $result[$this->_fixIndexName($index)] = true; + } + } + + if ($db->options['portability'] & Doctrine::PORTABILITY_FIX_CASE) { + $result = array_change_key_case($result, $db->options['field_case']); + } + return array_keys($result); } /** * lists database users @@ -288,6 +349,16 @@ class Doctrine_DataDict_Mssql extends Doctrine_DataDict { * @return array */ public function listViews($database = null) { - + $query = "SELECT name FROM sysobjects WHERE xtype = 'V'"; + + $result = $db->queryCol($query); + + if ($db->options['portability'] & Doctrine::PORTABILITY_FIX_CASE && + $db->options['field_case'] == CASE_LOWER) + { + $result = array_map(($db->options['field_case'] == CASE_LOWER ? + 'strtolower' : 'strtoupper'), $result); + } + return $result; } } diff --git a/lib/Doctrine/DataDict/Pgsql.php b/lib/Doctrine/DataDict/Pgsql.php index 90a8e1243..c3d219254 100644 --- a/lib/Doctrine/DataDict/Pgsql.php +++ b/lib/Doctrine/DataDict/Pgsql.php @@ -532,12 +532,15 @@ class Doctrine_DataDict_Pgsql extends Doctrine_DataDict { return array($type, $length, $unsigned, $fixed); } /** + * listDatabases * lists all databases * * @return array */ public function listDatabases() { - + $query = 'SELECT datname FROM pg_database'; + + return $this->conn->fetchColumn($query); } /** * lists all availible database functions @@ -545,7 +548,20 @@ class Doctrine_DataDict_Pgsql extends Doctrine_DataDict { * @return array */ public function listFunctions() { - + $query = " + SELECT + proname + FROM + pg_proc pr, + pg_type tp + WHERE + tp.oid = pr.prorettype + AND pr.proisagg = FALSE + AND tp.typname <> 'trigger' + AND pr.pronamespace IN + (SELECT oid FROM pg_namespace WHERE nspname NOT LIKE 'pg_%' AND nspname != 'information_schema')"; + + return $this->conn->fetchColumn($query); } /** * lists all database triggers @@ -563,7 +579,10 @@ class Doctrine_DataDict_Pgsql extends Doctrine_DataDict { * @return array */ public function listSequences($database = null) { - + $query = "SELECT relname FROM pg_class WHERE relkind = 'S' AND relnamespace IN"; + $query.= "(SELECT oid FROM pg_namespace WHERE nspname NOT LIKE 'pg_%' AND nspname != 'information_schema')"; + + return $this->conn->fetchColumn($query); } /** * lists table constraints @@ -572,7 +591,12 @@ class Doctrine_DataDict_Pgsql extends Doctrine_DataDict { * @return array */ public function listTableConstraints($table) { - + $table = $db->quote($table, 'text'); + $subquery = "SELECT indexrelid FROM pg_index, pg_class"; + $subquery.= " WHERE pg_class.relname=$table AND pg_class.oid=pg_index.indrelid AND (indisunique = 't' OR indisprimary = 't')"; + $query = "SELECT relname FROM pg_class WHERE oid IN ($subquery)"; + + return $this->conn->fetchColumn($query); } /** * lists table constraints @@ -619,13 +643,18 @@ class Doctrine_DataDict_Pgsql extends Doctrine_DataDict { return $columns; } /** - * lists table constraints + * list all indexes in a table * * @param string $table database table name * @return array */ public function listTableIndexes($table) { - + $table = $db->quote($table, 'text'); + $subquery = "SELECT indexrelid FROM pg_index, pg_class"; + $subquery.= " WHERE pg_class.relname=$table AND pg_class.oid=pg_index.indrelid AND indisunique != 't' AND indisprimary != 't'"; + $query = "SELECT relname FROM pg_class WHERE oid IN ($subquery)"; + + return $this->conn->fetchColumn($query); } /** * lists tables @@ -659,21 +688,25 @@ class Doctrine_DataDict_Pgsql extends Doctrine_DataDict { } /** - * lists table views + * list the views in the database that reference a given table * * @param string $table database table name * @return array */ public function listTableViews($table) { - + $query = 'SELECT viewname FROM pg_views'; + + return $this->conn->fetchColumn($query); } /** * lists database users * * @return array */ - public function listUsers() { - + public function listUsers() { + $query = 'SELECT usename FROM pg_user'; + + return $this->conn->fetchColumn($query); } /** * lists database views @@ -681,7 +714,9 @@ class Doctrine_DataDict_Pgsql extends Doctrine_DataDict { * @param string|null $database * @return array */ - public function listViews($database = null) { - + public function listViews($database = null) { + $query = 'SELECT viewname FROM pg_views'; + + return $this->conn->fetchColumn($query); } } diff --git a/lib/Doctrine/Transaction.php b/lib/Doctrine/Transaction.php index 25db7cd5a..fa52cfde9 100644 --- a/lib/Doctrine/Transaction.php +++ b/lib/Doctrine/Transaction.php @@ -30,21 +30,17 @@ */ class Doctrine_Transaction { /** - * Doctrine_Transaction is in open state when it is opened and there are no active transactions + * Doctrine_Transaction is in sleep state when it has no active transactions */ - const STATE_OPEN = 0; - /** - * Doctrine_Transaction is in closed state when it is closed - */ - const STATE_CLOSED = 1; + const STATE_SLEEP = 0; /** * Doctrine_Transaction is in active state when it has one active transaction */ - const STATE_ACTIVE = 2; + const STATE_ACTIVE = 1; /** * Doctrine_Transaction is in busy state when it has multiple active transactions */ - const STATE_BUSY = 3; + const STATE_BUSY = 2; /** * @var Doctrine_Connection $conn the connection object */ @@ -93,6 +89,8 @@ class Doctrine_Transaction { * beginTransaction * Start a transaction or set a savepoint. * + * 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 @@ -122,8 +120,9 @@ class Doctrine_Transaction { * commit * Commit the database changes done during a transaction that is in * progress or release a savepoint. This function may only be called when - * auto-committing is disabled, otherwise it will fail. Therefore, a new - * transaction is implicitly started after committing the pending changes. + * auto-committing is disabled, otherwise it will fail. + * + * Listeners: onPreTransactionCommit, onTransactionCommit * * @param string $savepoint name of a savepoint to release * @throws Doctrine_Transaction_Exception if the transaction fails at PDO level @@ -145,7 +144,7 @@ class Doctrine_Transaction { try { $this->bulkDelete(); - + } catch(Exception $e) { $this->rollback(); @@ -227,4 +226,45 @@ class Doctrine_Transaction { public function rollbackSavePoint($savepoint) { throw new Doctrine_Transaction_Exception('Savepoints not supported by this driver.'); } + /** + * setIsolation + * + * Set the transacton isolation level. + * (implemented by the connection drivers) + * + * example: + * + * + * $tx->setIsolation('READ UNCOMMITTED'); + * + * + * @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_Exception if the feature is not supported by the driver + * @throws PDOException if something fails at the PDO level + * @return void + */ + public function setIsolation($isolation) { + throw new Doctrine_Connection_Exception('Transaction isolation levels not supported by this driver.'); + } + + /** + * getTransactionIsolation + * + * fetches the current session transaction isolation level + * + * note: some drivers may support setting the transaction isolation level + * but not fetching it + * + * @throws Doctrine_Connection_Exception if the feature is not supported by the driver + * @throws PDOException if something fails at the PDO level + * @return string returns the current session transaction isolation level + */ + public function getIsolation() { + throw new Doctrine_Connection_Exception('Fetching transaction isolation level not supported by this driver.'); + } }