From ad3f5bb6ff858860c819fa4127e08e655126b678 Mon Sep 17 00:00:00 2001 From: zYne Date: Tue, 19 Jun 2007 19:51:25 +0000 Subject: [PATCH] --- lib/Doctrine.php | 8 +- lib/Doctrine/Compiler.php | 2 +- lib/Doctrine/Connection/Profiler.php | 181 ++++++++++++ lib/Doctrine/Connection/Statement.php | 408 ++++++++++++++++++++++++++ lib/Doctrine/Import/Pgsql.php | 10 +- tests/BooleanTestCase.php | 6 +- tests/DBTestCase.php | 2 +- tests/run.php | 2 +- 8 files changed, 607 insertions(+), 12 deletions(-) create mode 100644 lib/Doctrine/Connection/Profiler.php create mode 100644 lib/Doctrine/Connection/Statement.php diff --git a/lib/Doctrine.php b/lib/Doctrine.php index c8662a551..869d137d7 100644 --- a/lib/Doctrine.php +++ b/lib/Doctrine.php @@ -155,9 +155,7 @@ final class Doctrine const ATTR_DBNAME_FORMAT = 117; const ATTR_TBLCLASS_FORMAT = 119; const ATTR_EXPORT = 140; - const ATTR_DECIMAL_PLACES = 141; - - + const ATTR_DECIMAL_PLACES = 141; const ATTR_PORTABILITY = 106; const ATTR_VLD = 107; @@ -428,7 +426,7 @@ final class Doctrine * * @param string $directory */ - public static function export($directory) + public static function export($directory = null) { return Doctrine_Manager::connection()->export->export($directory); } @@ -438,7 +436,7 @@ final class Doctrine * * @param string $directory */ - public static function exportSql($directory) + public static function exportSql($directory = null) { return Doctrine_Manager::connection()->export->exportSql($directory); } diff --git a/lib/Doctrine/Compiler.php b/lib/Doctrine/Compiler.php index 28ad03636..9ff451b4d 100644 --- a/lib/Doctrine/Compiler.php +++ b/lib/Doctrine/Compiler.php @@ -53,7 +53,7 @@ class Doctrine_Compiler require_once $file->getPathName(); } } - + $classes = array_merge(get_declared_classes(), get_declared_interfaces()); $ret = array(); diff --git a/lib/Doctrine/Connection/Profiler.php b/lib/Doctrine/Connection/Profiler.php new file mode 100644 index 000000000..e6596c29d --- /dev/null +++ b/lib/Doctrine/Connection/Profiler.php @@ -0,0 +1,181 @@ +. + */ +Doctrine::autoload('Doctrine_Overloadable'); +/** + * Doctrine_Connection_Profiler + * + * @author Konsta Vesterinen + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @package Doctrine + * @category Object Relational Mapping + * @link www.phpdoctrine.com + * @since 1.0 + * @version $Revision$ + */ +class Doctrine_Connection_Profiler implements Doctrine_Overloadable, IteratorAggregate, Countable +{ + /** + * @param array $listeners an array containing all availible listeners + */ + private $listeners = array('query', + 'prepare', + 'commit', + 'rollback', + 'connect', + 'begintransaction', + 'exec', + 'execute', + ); + /** + * @param array $events an array containing all listened events + */ + private $events = array(); + /** + * constructor + */ + public function __construct() { + + } + /** + * setFilterQueryType + * + * @param integer $filter + * @return boolean + */ + public function setFilterQueryType() { + + } + /** + * method overloader + * this method is used for invoking different listeners, for the full + * list of availible listeners, see Doctrine_Db_EventListener + * + * @param string $m the name of the method + * @param array $a method arguments + * @see Doctrine_Db_EventListener + * @return boolean + */ + public function __call($m, $a) + { + // first argument should be an instance of Doctrine_Db_Event + if ( ! ($a[0] instanceof Doctrine_Db_Event)) { + throw new Doctrine_Connection_Profiler_Exception("Couldn't listen event. Event should be an instance of Doctrine_Db_Event."); + } + + // event methods should start with 'on' + if (substr($m, 0, 2) !== 'on') { + throw new Doctrine_Connection_Profiler_Exception("Couldn't invoke listener :" . $m); + } + + if (substr($m, 2, 3) === 'Pre' && substr($m, 2, 7) !== 'Prepare') { + if ( ! in_array(strtolower(substr($m, 5)), $this->listeners)) { + throw new Doctrine_Connection_Profiler_Exception("Couldn't invoke listener :" . $m); + } + // pre-event listener found + $a[0]->start(); + if( ! in_array($a[0], $this->events, true)) { + $this->events[] = $a[0]; + } + } else { + if ( ! in_array(strtolower(substr($m, 2)), $this->listeners)) { + throw new Doctrine_Connection_Profiler_Exception("Couldn't invoke listener :" . $m); + } + // after-event listener found + $a[0]->end(); + } + /** + * If filtering by query type is enabled, only keep the query if + * it was one of the allowed types. + */ + if ( ! is_null($this->filterTypes)) { + if ( ! ($a[0]->getQueryType() & $this->_filterTypes)) { + + } + } + + } + /** + * get + * + * @param mixed $key + * @return Doctrine_Event + */ + public function get($key) + { + if (isset($this->events[$key])) { + return $this->events[$key]; + } + return null; + } + /** + * getAll + * returns all profiled events as an array + * + * @return array all events in an array + */ + public function getAll() + { + return $this->events; + } + /** + * getIterator + * returns an iterator that iterates through the logged events + * + * @return ArrayIterator + */ + public function getIterator() + { + return new ArrayIterator($this->events); + } + /** + * count + * + * @return integer + */ + public function count() + { + return count($this->events); + } + /** + * pop the last event from the event stack + * + * @return Doctrine_Db_Event + */ + public function pop() + { + return array_pop($this->events); + } + /** + * Get the Doctrine_Db_Event object for the last query that was run, regardless if it has + * ended or not. If the event has not ended, it's end time will be Null. + * + * @return Doctrine_Db_Event + */ + public function lastEvent() + { + if (empty($this->events)) { + return false; + } + + end($this->events); + return current($this->events); + } +} diff --git a/lib/Doctrine/Connection/Statement.php b/lib/Doctrine/Connection/Statement.php new file mode 100644 index 000000000..7123e713a --- /dev/null +++ b/lib/Doctrine/Connection/Statement.php @@ -0,0 +1,408 @@ +?php +/* + * $Id: Statement.php 1532 2007-05-31 17:45:07Z zYne $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * and is licensed under the LGPL. For more information, see + * . + */ +Doctrine::autoload('Doctrine_Adapter_Statement_Interface'); +/** + * Doctrine_Connection_Statement + * + * @author Konsta Vesterinen + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @package Doctrine + * @category Object Relational Mapping + * @link www.phpdoctrine.com + * @since 1.0 + * @version $Revision: 1532 $ + */ +class Doctrine_Connection_Statement implements Doctrine_Adapter_Statement_Interface +{ + protected $_adapter; + + protected $_stmt; + + protected $_executed = false; + + public function __construct($adapter, $stmt) + { + $this->_adapter = $adapter; + $this->_stmt = $stmt; + + if ($stmt === false) { + throw new Doctrine_Db_Exception('Unknown statement object given.'); + } + } + /** + * + */ + public function getDbh() + { + return $this->_adapter; + } + public function getStatement() + { + return $this->_stmt; + } + public function getQuery() + { + return $this->_stmt->queryString; + } + /** + * bindColumn + * Bind a column to a PHP variable + * + * @param mixed $column Number of the column (1-indexed) or name of the column in the result set. + * If using the column name, be aware that the name should match + * the case of the column, as returned by the driver. + * + * @param string $param Name of the PHP variable to which the column will be bound. + * @param integer $type Data type of the parameter, specified by the Doctrine::PARAM_* constants. + * @return boolean Returns TRUE on success or FALSE on failure + */ + public function bindColumn($column, $param, $type = null) + { + if($type === null) { + return $this->_stmt->bindColumn($column, $param); + } else { + return $this->_stmt->bindColumn($column, $param, $type); + } + } + /** + * bindValue + * Binds a value to a corresponding named or question mark + * placeholder in the SQL statement that was use to prepare the statement. + * + * @param mixed $param Parameter identifier. For a prepared statement using named placeholders, + * this will be a parameter name of the form :name. For a prepared statement + * using question mark placeholders, this will be the 1-indexed position of the parameter + * + * @param mixed $value The value to bind to the parameter. + * @param integer $type Explicit data type for the parameter using the Doctrine::PARAM_* constants. + * + * @return boolean Returns TRUE on success or FALSE on failure. + */ + public function bindValue($param, $value, $type = null) + { + if($type === null) { + return $this->_stmt->bindValue($param, $value); + } else { + return $this->_stmt->bindValue($param, $value, $type); + } + } + /** + * bindParam + * Binds a PHP variable to a corresponding named or question mark placeholder in the + * SQL statement that was use to prepare the statement. Unlike Doctrine_Adapter_Statement_Interface->bindValue(), + * the variable is bound as a reference and will only be evaluated at the time + * that Doctrine_Adapter_Statement_Interface->execute() is called. + * + * Most parameters are input parameters, that is, parameters that are + * used in a read-only fashion to build up the query. Some drivers support the invocation + * of stored procedures that return data as output parameters, and some also as input/output + * parameters that both send in data and are updated to receive it. + * + * @param mixed $param Parameter identifier. For a prepared statement using named placeholders, + * this will be a parameter name of the form :name. For a prepared statement + * using question mark placeholders, this will be the 1-indexed position of the parameter + * + * @param mixed $variable Name of the PHP variable to bind to the SQL statement parameter. + * + * @param integer $type Explicit data type for the parameter using the Doctrine::PARAM_* constants. To return + * an INOUT parameter from a stored procedure, use the bitwise OR operator to set the + * Doctrine::PARAM_INPUT_OUTPUT bits for the data_type parameter. + * + * @param integer $length Length of the data type. To indicate that a parameter is an OUT parameter + * from a stored procedure, you must explicitly set the length. + * @param mixed $driverOptions + * @return boolean Returns TRUE on success or FALSE on failure. + */ + public function bindParam($column, $variable, $type = null, $length = null, $driverOptions = array()) + { + if($type === null) { + return $this->_stmt->bindParam($column, $variable); + } else { + return $this->_stmt->bindParam($column, $variable, $type, $length, $driverOptions); + } + } + /** + * closeCursor + * Closes the cursor, enabling the statement to be executed again. + * + * @return boolean Returns TRUE on success or FALSE on failure. + */ + public function closeCursor() + { + return $this->_stmt->closeCursor(); + } + /** + * columnCount + * Returns the number of columns in the result set + * + * @return integer Returns the number of columns in the result set represented + * by the Doctrine_Adapter_Statement_Interface object. If there is no result set, + * this method should return 0. + */ + public function columnCount() + { + return $this->_stmt->columnCount(); + } + /** + * errorCode + * Fetch the SQLSTATE associated with the last operation on the statement handle + * + * @see Doctrine_Adapter_Interface::errorCode() + * @return string error code string + */ + public function errorCode() + { + return $this->_stmt->errorCode(); + } + /** + * errorInfo + * Fetch extended error information associated with the last operation on the statement handle + * + * @see Doctrine_Adapter_Interface::errorInfo() + * @return array error info array + */ + public function errorInfo() + { + return $this->_stmt->errorInfo(); + } + /** + * execute + * Executes a prepared statement + * + * If the prepared statement included parameter markers, you must either: + * call PDOStatement->bindParam() to bind PHP variables to the parameter markers: + * bound variables pass their value as input and receive the output value, + * if any, of their associated parameter markers or pass an array of input-only + * parameter values + * + * + * @param array $params An array of values with as many elements as there are + * bound parameters in the SQL statement being executed. + * @return boolean Returns TRUE on success or FALSE on failure. + */ + public function execute($params = null) + { + $event = new Doctrine_Db_Event($this, Doctrine_Db_Event::EXECUTE, $this->_stmt->queryString, $params); + // print $this->_stmt->queryString . print_r($params, true) . "
"; + $skip = $this->_adapter->getListener()->onPreExecute($event); + + if ( ! $skip) { + $this->_stmt->execute($params); + $this->_adapter->incrementQueryCount(); + } + + $this->_adapter->getListener()->onExecute($event); + + return $this; + } + /** + * fetch + * + * @see Doctrine::FETCH_* constants + * @param integer $fetchStyle Controls how the next row will be returned to the caller. + * This value must be one of the Doctrine::FETCH_* constants, + * defaulting to Doctrine::FETCH_BOTH + * + * @param integer $cursorOrientation For a PDOStatement object representing a scrollable cursor, + * this value determines which row will be returned to the caller. + * This value must be one of the Doctrine::FETCH_ORI_* constants, defaulting to + * Doctrine::FETCH_ORI_NEXT. To request a scrollable cursor for your + * Doctrine_Adapter_Statement_Interface object, + * you must set the Doctrine::ATTR_CURSOR attribute to Doctrine::CURSOR_SCROLL when you + * prepare the SQL statement with Doctrine_Adapter_Interface->prepare(). + * + * @param integer $cursorOffset For a Doctrine_Adapter_Statement_Interface object representing a scrollable cursor for which the + * $cursorOrientation parameter is set to Doctrine::FETCH_ORI_ABS, this value specifies + * the absolute number of the row in the result set that shall be fetched. + * + * For a Doctrine_Adapter_Statement_Interface object representing a scrollable cursor for + * which the $cursorOrientation parameter is set to Doctrine::FETCH_ORI_REL, this value + * specifies the row to fetch relative to the cursor position before + * Doctrine_Adapter_Statement_Interface->fetch() was called. + * + * @return mixed + */ + public function fetch($fetchStyle = Doctrine::FETCH_BOTH, + $cursorOrientation = Doctrine::FETCH_ORI_NEXT, + $cursorOffset = null) + { + $event = new Doctrine_Db_Event($this, Doctrine_Db_Event::FETCHALL, $this->_stmt->queryString, + array($fetchStyle, $cursorOrientation, $cursorOffset)); + + $data = $this->_adapter->getListener()->onPreFetch($event); + + if ($data === null) { + $data = $this->_stmt->fetch($fetchStyle, $cursorOrientation, $cursorOffset); + } + + $this->_adapter->getListener()->onFetch($event); + + return $data; + } + /** + * fetchAll + * Returns an array containing all of the result set rows + * + * + * + * @param integer $columnIndex Returns the indicated 0-indexed column when the value of $fetchStyle is + * Doctrine::FETCH_COLUMN. Defaults to 0. + * + * @return array + */ + public function fetchAll($fetchStyle = Doctrine::FETCH_BOTH, + $columnIndex = null) + { + $event = new Doctrine_Db_Event($this, Doctrine_Db_Event::FETCHALL, $this->_stmt->queryString, array($fetchStyle, $columnIndex)); + + $data = $this->_adapter->getListener()->onPreFetchAll($event); + + if ($data === null) { + if ($columnIndex !== null) { + $data = $this->_stmt->fetchAll($fetchStyle, $columnIndex); + } else { + $data = $this->_stmt->fetchAll($fetchStyle); + } + } + + $this->_adapter->getListener()->onFetchAll($event); + + return $data; + } + /** + * fetchColumn + * Returns a single column from the next row of a + * result set or FALSE if there are no more rows. + * + * @param integer $columnIndex 0-indexed number of the column you wish to retrieve from the row. If no + * value is supplied, Doctrine_Adapter_Statement_Interface->fetchColumn() + * fetches the first column. + * + * @return string returns a single column in the next row of a result set. + */ + public function fetchColumn($columnIndex = 0) + { + return $this->_stmt->fetchColumn($columnIndex); + } + /** + * fetchObject + * Fetches the next row and returns it as an object. + * + * Fetches the next row and returns it as an object. This function is an alternative to + * Doctrine_Adapter_Statement_Interface->fetch() with Doctrine::FETCH_CLASS or Doctrine::FETCH_OBJ style. + * + * @param string $className Name of the created class, defaults to stdClass. + * @param array $args Elements of this array are passed to the constructor. + * + * @return mixed an instance of the required class with property names that correspond + * to the column names or FALSE in case of an error. + */ + public function fetchObject($className = 'stdClass', $args = array()) + { + return $this->_stmt->fetchObject($className, $args); + } + /** + * getAttribute + * Retrieve a statement attribute + * + * @param integer $attribute + * @see Doctrine::ATTR_* constants + * @return mixed the attribute value + */ + public function getAttribute($attribute) + { + return $this->_stmt->getAttribute($attribute); + } + /** + * getColumnMeta + * Returns metadata for a column in a result set + * + * @param integer $column The 0-indexed column in the result set. + * + * @return array Associative meta data array with the following structure: + * + * native_type The PHP native type used to represent the column value. + * driver:decl_ type The SQL type used to represent the column value in the database. If the column in the result set is the result of a function, this value is not returned by PDOStatement->getColumnMeta(). + * flags Any flags set for this column. + * name The name of this column as returned by the database. + * len The length of this column. Normally -1 for types other than floating point decimals. + * precision The numeric precision of this column. Normally 0 for types other than floating point decimals. + * pdo_type The type of this column as represented by the PDO::PARAM_* constants. + */ + public function getColumnMeta($column) + { + return $this->_stmt->getColumnMeta($column); + } + /** + * nextRowset + * Advances to the next rowset in a multi-rowset statement handle + * + * Some database servers support stored procedures that return more than one rowset + * (also known as a result set). The nextRowset() method enables you to access the second + * and subsequent rowsets associated with a PDOStatement object. Each rowset can have a + * different set of columns from the preceding rowset. + * + * @return boolean Returns TRUE on success or FALSE on failure. + */ + public function nextRowset() + { + return $this->_stmt->nextRowset(); + } + /** + * rowCount + * rowCount() returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement + * executed by the corresponding object. + * + * If the last SQL statement executed by the associated Statement object was a SELECT statement, + * some databases may return the number of rows returned by that statement. However, + * this behaviour is not guaranteed for all databases and should not be + * relied on for portable applications. + * + * @return integer Returns the number of rows. + */ + public function rowCount() + { + return $this->_stmt->rowCount(); + } + /** + * setAttribute + * Set a statement attribute + * + * @param integer $attribute + * @param mixed $value the value of given attribute + * @return boolean Returns TRUE on success or FALSE on failure. + */ + public function setAttribute($attribute, $value) + { + return $this->_stmt->setAttribute($attribute, $value); + } + /** + * setFetchMode + * Set the default fetch mode for this statement + * + * @param integer $mode The fetch mode must be one of the Doctrine::FETCH_* constants. + * @return boolean Returns 1 on success or FALSE on failure. + */ + public function setFetchMode($mode, $arg1 = null, $arg2 = null) + { + return $this->_stmt->setFetchMode($mode, $arg1, $arg2); + } +} diff --git a/lib/Doctrine/Import/Pgsql.php b/lib/Doctrine/Import/Pgsql.php index 28c6bdcfd..140b1c106 100644 --- a/lib/Doctrine/Import/Pgsql.php +++ b/lib/Doctrine/Import/Pgsql.php @@ -208,7 +208,15 @@ class Doctrine_Import_Pgsql extends Doctrine_Import */ public function listTableTriggers($table) { - + $query = 'SELECT trg.tgname AS trigger_name + FROM pg_trigger trg, + pg_class tbl + WHERE trg.tgrelid = tbl.oid'; + if ($table !== null) { + $table = $this->conn->quote(strtoupper($table), 'string'); + $query .= " AND tbl.relname = $table"; + } + return $this->conn->fetchColumn($query); } /** * list the views in the database that reference a given table diff --git a/tests/BooleanTestCase.php b/tests/BooleanTestCase.php index 7d199bbb3..4ec2c1037 100644 --- a/tests/BooleanTestCase.php +++ b/tests/BooleanTestCase.php @@ -70,7 +70,7 @@ class Doctrine_Boolean_TestCase extends Doctrine_UnitTestCase { $query = new Doctrine_Query($this->connection); $ret = $query->query('FROM BooleanTest WHERE BooleanTest.is_working = 1'); - Doctrine_Lib::formatSql($query->getQuery()); + $this->assertEqual(count($ret), 1); } public function testPreparedQueries() { @@ -89,7 +89,7 @@ class Doctrine_Boolean_TestCase extends Doctrine_UnitTestCase { $query = new Doctrine_Query($this->connection); $ret = $query->query('FROM BooleanTest WHERE BooleanTest.is_working = true'); - Doctrine_Lib::formatSql($query->getQuery()); + $this->assertEqual(count($ret), 1); } @@ -107,7 +107,7 @@ class Doctrine_Boolean_TestCase extends Doctrine_UnitTestCase { $test = new BooleanTest(); $this->is_working_notnull = null; - $this->assertIdentical($this->is_working_notnull, false); + $this->assertIdentical($this->is_working_notnull, null); $this->assertEqual($test->state(), Doctrine_Record::STATE_TDIRTY); $test->save(); diff --git a/tests/DBTestCase.php b/tests/DBTestCase.php index 128af1339..38c66a76b 100644 --- a/tests/DBTestCase.php +++ b/tests/DBTestCase.php @@ -32,7 +32,7 @@ class Doctrine_Db_TestCase extends Doctrine_UnitTestCase { $this->dbh->exec("INSERT INTO entity (id, name) VALUES (2, 'John')"); - $this->assertEqual($this->dbh->getAttribute(PDO::ATTR_DRIVER_NAME), 'sqlite'); + $this->assertEqual($this->dbh->getAttribute(Doctrine::ATTR_DRIVER_NAME), 'sqlite'); } public function testAddValidEventListener() { diff --git a/tests/run.php b/tests/run.php index 4a3403fc2..05aa3c532 100644 --- a/tests/run.php +++ b/tests/run.php @@ -294,7 +294,7 @@ $test->addTestCase(new Doctrine_Query_JoinCondition_TestCase()); $test->addTestCase(new Doctrine_Query_MultipleAggregateValue_TestCase()); $test->addTestCase(new Doctrine_Query_TestCase()); - /** + /** //$test->addTestCase(new Doctrine_IntegrityAction_TestCase()); */ //$test->addTestCase(new Doctrine_AuditLog_TestCase());