Commit 78f5a2ed authored by romanb's avatar romanb

More refactorings. Commented out the plugin tests for now. They seem to wreak...

More refactorings. Commented out the plugin tests for now. They seem to wreak havoc with a lot of other tests (especially validator tests) and i havent found the reason yet.
parent 24a8cef0
......@@ -238,20 +238,6 @@ abstract class Doctrine_Configurable extends Doctrine_Locator_Injectable
return true;
}
/**
* getCacheDriver
*
* @return Doctrine_Cache_Interface
*/
public function getCacheDriver()
{
if ( ! isset($this->attributes[Doctrine::ATTR_CACHE])) {
throw new Doctrine_Exception('Cache driver not initialized.');
}
return $this->attributes[Doctrine::ATTR_CACHE];
}
/**
* @param Doctrine_EventListener $listener
* @return void
......
......@@ -914,17 +914,15 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun
$this->getAttribute(Doctrine::ATTR_LISTENER)->preQuery($event);
if ( ! $event->skipOperation) {
//echo $query . "<br />";
$stmt = $this->dbh->query($query);
$this->_count++;
}
$this->getAttribute(Doctrine::ATTR_LISTENER)->postQuery($event);
return $stmt;
}
} catch(Doctrine_Adapter_Exception $e) {
} catch(PDOException $e) { }
} catch (Doctrine_Adapter_Exception $e) {
} catch (PDOException $e) { }
$this->rethrowException($e, $this);
}
......@@ -959,8 +957,8 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun
return $count;
}
} catch(Doctrine_Adapter_Exception $e) {
} catch(PDOException $e) { }
} catch (Doctrine_Adapter_Exception $e) {
} catch (PDOException $e) { }
$this->rethrowException($e, $this);
}
......@@ -975,7 +973,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun
$event = new Doctrine_Event($this, Doctrine_Event::CONN_ERROR);
$this->getListener()->preError($event);
$name = 'Doctrine_Connection_' . $this->driverName . '_Exception';
$exc = new $name($e->getMessage(), (int) $e->getCode());
......@@ -1201,6 +1199,31 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun
return $this->dbh->errorInfo();
}
/**
* getCacheDriver
*
* @return Doctrine_Cache_Interface
* @deprecated Use getResultCacheDriver()
*/
public function getCacheDriver()
{
return $this->getResultCacheDriver();
}
/**
* getResultCacheDriver
*
* @return Doctrine_Cache_Interface
*/
public function getResultCacheDriver()
{
if ( ! isset($this->attributes[Doctrine::ATTR_CACHE])) {
throw new Doctrine_Exception('Result Cache driver not initialized.');
}
return $this->attributes[Doctrine::ATTR_CACHE];
}
/**
* lastInsertId
......
......@@ -21,6 +21,9 @@
Doctrine::autoload('Doctrine_Query_Abstract');
/**
* Doctrine_Query
* A Doctrine_Query object represents a DQL query. It is used to query databases for
* data in an object-oriented fashion. A DQL query understands relations and inheritance
* and is dbms independant.
*
* @package Doctrine
* @subpackage Query
......@@ -29,21 +32,112 @@ Doctrine::autoload('Doctrine_Query_Abstract');
* @since 1.0
* @version $Revision$
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @todo Proposal: This class does far too much. It should have only 1 task: Collecting
* the DQL query parts and the query parameters (the query state and caching options/methods
* can remain here, too).
* The actual SQL construction could be done by a separate object (Doctrine_Query_SqlBuilder?)
* whose task it is to convert DQL into SQL.
* Furthermore the SqlBuilder? can then use other objects (Doctrine_Query_Tokenizer?),
* (Doctrine_Query_Parser(s)?) to accomplish his work. Doctrine_Query does not need
* to know the tokenizer/parsers. There could be extending
* implementations of SqlBuilder? that cover the specific SQL dialects.
* This would release Doctrine_Connection and the Doctrine_Connection_xxx classes
* from this tedious task.
* This would also largely reduce the currently huge interface of Doctrine_Query(_Abstract)
* and better hide all these transformation internals from the public Query API.
*
* @internal The lifecycle of a Query object is the following:
* After construction the query object is empty. Through using the fluent
* query interface the user fills the query object with DQL parts and query parameters.
* These get collected in {@link $_dqlParts} and {@link $_params}, respectively.
* When the query is executed the first time, or when {@link getSqlQuery()}
* is called the first time, the collected DQL parts get parsed and the resulting
* connection-driver specific SQL is generated. The generated SQL parts are
* stored in {@link $_sqlParts} and the final resulting SQL query is stored in
* {@link $_sql}.
*/
class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Serializable
{
protected $subqueryAliases = array();
/**
* @var array The DQL keywords.
*/
protected static $_keywords = array('ALL',
'AND',
'ANY',
'AS',
'ASC',
'AVG',
'BETWEEN',
'BIT_LENGTH',
'BY',
'CHARACTER_LENGTH',
'CHAR_LENGTH',
'CURRENT_DATE',
'CURRENT_TIME',
'CURRENT_TIMESTAMP',
'DELETE',
'DESC',
'DISTINCT',
'EMPTY',
'EXISTS',
'FALSE',
'FETCH',
'FROM',
'GROUP',
'HAVING',
'IN',
'INDEXBY',
'INNER',
'IS',
'JOIN',
'LEFT',
'LIKE',
'LOWER',
'MEMBER',
'MOD',
'NEW',
'NOT',
'NULL',
'OBJECT',
'OF',
'OR',
'ORDER',
'OUTER',
'POSITION',
'SELECT',
'SOME',
'TRIM',
'TRUE',
'UNKNOWN',
'UPDATE',
'WHERE');
/**
* @var array
*/
protected $_subqueryAliases = array();
/**
* @var array $_aggregateAliasMap an array containing all aggregate aliases, keys as dql aliases
* and values as sql aliases
*/
protected $_aggregateAliasMap = array();
/**
* @var array
*/
protected $_pendingAggregates = array();
/**
* @param boolean $needsSubquery
*/
protected $needsSubquery = false;
protected $_needsSubquery = false;
/**
* @param boolean $isSubquery whether or not this query object is a subquery of another
* query object
*/
protected $isSubquery;
protected $_isSubquery;
/**
* @var array $_neededTables an array containing the needed table aliases
......@@ -66,34 +160,15 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
*/
protected $_parsers = array();
/**
* @var array $_dqlParts an array containing all DQL query parts
*/
protected $_dqlParts = array(
'from' => array(),
'select' => array(),
'forUpdate' => false,
'set' => array(),
'join' => array(),
'where' => array(),
'groupby' => array(),
'having' => array(),
'orderby' => array(),
'limit' => array(),
'offset' => array(),
);
/**
* @var array $_pendingJoinConditions an array containing pending joins
*/
protected $_pendingJoinConditions = array();
protected $_expressionMap = array();
/**
* @var integer $_state The current state of this query.
* @var array
*/
protected $_state = Doctrine_Query::STATE_CLEAN;
protected $_expressionMap = array();
/**
* @var string $_sql cached SQL query
......@@ -123,9 +198,9 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
$this->_pendingFields = array();
$this->_neededTables = array();
$this->_expressionMap = array();
$this->subqueryAliases = array();
$this->needsSubquery = false;
$this->isLimitSubqueryUsed = false;
$this->_subqueryAliases = array();
$this->_needsSubquery = false;
$this->_isLimitSubqueryUsed = false;
}
/**
......@@ -149,13 +224,13 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
}
/**
* addPendingJoinCondition
* _addPendingJoinCondition
*
* @param string $componentAlias component alias
* @param string $joinCondition dql join condition
* @return Doctrine_Query this object
*/
public function addPendingJoinCondition($componentAlias, $joinCondition)
protected function _addPendingJoinCondition($componentAlias, $joinCondition)
{
$this->_pendingJoins[$componentAlias] = $joinCondition;
}
......@@ -189,6 +264,29 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
return $this->_enumParams;
}
/**
* getDql
* returns the DQL query that is represented by this query object.
*
* the query is built from $_dqlParts
*
* @return string the DQL query
*/
public function getDql()
{
$q = '';
$q .= ( ! empty($this->_dqlParts['select']))? 'SELECT ' . implode(', ', $this->_dqlParts['select']) : '';
$q .= ( ! empty($this->_dqlParts['from']))? ' FROM ' . implode(' ', $this->_dqlParts['from']) : '';
$q .= ( ! empty($this->_dqlParts['where']))? ' WHERE ' . implode(' AND ', $this->_dqlParts['where']) : '';
$q .= ( ! empty($this->_dqlParts['groupby']))? ' GROUP BY ' . implode(', ', $this->_dqlParts['groupby']) : '';
$q .= ( ! empty($this->_dqlParts['having']))? ' HAVING ' . implode(' AND ', $this->_dqlParts['having']) : '';
$q .= ( ! empty($this->_dqlParts['orderby']))? ' ORDER BY ' . implode(', ', $this->_dqlParts['orderby']) : '';
$q .= ( ! empty($this->_dqlParts['limit']))? ' LIMIT ' . implode(' ', $this->_dqlParts['limit']) : '';
$q .= ( ! empty($this->_dqlParts['offset']))? ' OFFSET ' . implode(' ', $this->_dqlParts['offset']) : '';
return $q;
}
/**
* getParams
*
......@@ -261,10 +359,10 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
public function isSubquery($bool = null)
{
if ($bool === null) {
return $this->isSubquery;
return $this->_isSubquery;
}
$this->isSubquery = (bool) $bool;
$this->_isSubquery = (bool) $bool;
return $this;
}
......@@ -273,46 +371,33 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
*
* @param string $dqlAlias the dql alias of an aggregate value
* @return string
* @deprecated
*/
public function getAggregateAlias($dqlAlias)
{
if (isset($this->aggregateMap[$dqlAlias])) {
// mark the expression as used
$this->_expressionMap[$dqlAlias][1] = true;
return $this->aggregateMap[$dqlAlias];
}
if ( ! empty($this->pendingAggregates)) {
$this->processPendingAggregates();
return $this->getAggregateAlias($dqlAlias);
}
throw new Doctrine_Query_Exception('Unknown aggregate alias ' . $dqlAlias);
return $this->getSqlAggregateAlias($dqlAlias);
}
/**
* getParser
* parser lazy-loader
* getSqlAggregateAlias
*
* @throws Doctrine_Query_Exception if unknown parser name given
* @return Doctrine_Query_Part
* @todo Doc/Description: What is the parameter for? Which parsers are available?
* @param string $dqlAlias the dql alias of an aggregate value
* @return string
*/
public function getParser($name)
public function getSqlAggregateAlias($dqlAlias)
{
if ( ! isset($this->_parsers[$name])) {
$class = 'Doctrine_Query_' . ucwords(strtolower($name));
Doctrine::autoload($class);
if (isset($this->_aggregateAliasMap[$dqlAlias])) {
// mark the expression as used
$this->_expressionMap[$dqlAlias][1] = true;
if ( ! class_exists($class)) {
throw new Doctrine_Query_Exception('Unknown parser ' . $name);
}
return $this->_aggregateAliasMap[$dqlAlias];
} else if ( ! empty($this->_pendingAggregates)) {
$this->processPendingAggregates();
$this->_parsers[$name] = new $class($this);
return $this->getSqlAggregateAlias($dqlAlias);
} else {
throw new Doctrine_Query_Exception('Unknown aggregate alias: ' . $dqlAlias);
}
return $this->_parsers[$name];
}
/**
......@@ -326,7 +411,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
* the given query part stack with $queryPart
* @return Doctrine_Query this object
*/
public function parseQueryPart($queryPartName, $queryPart, $append = false)
/*protected function parseQueryPart($queryPartName, $queryPart, $append = false)
{
if ($this->_state === self::STATE_LOCKED) {
throw new Doctrine_Query_Exception('This query object is locked. No query parts can be manipulated.');
......@@ -345,15 +430,15 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
}
if ($this->_state === self::STATE_DIRECT) {
$parser = $this->getParser($queryPartName);
$parser = $this->_getParser($queryPartName);
$sql = $parser->parse($queryPart);
if (isset($sql)) {
if ($append) {
$this->addQueryPart($queryPartName, $sql);
$this->addSqlQueryPart($queryPartName, $sql);
} else {
$this->setQueryPart($queryPartName, $sql);
$this->setSqlQueryPart($queryPartName, $sql);
}
}
}
......@@ -361,7 +446,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
$this->_state = Doctrine_Query::STATE_DIRTY;
return $this;
}
}*/
/**
* getDqlPart
......@@ -381,29 +466,6 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
return $this->_dqlParts[$queryPart];
}
/**
* getDql
* returns the DQL query that is represented by this query object.
*
* the query is built from $_dqlParts
*
* @return string the DQL query
*/
public function getDql()
{
$q = '';
$q .= ( ! empty($this->_dqlParts['select']))? 'SELECT ' . implode(', ', $this->_dqlParts['select']) : '';
$q .= ( ! empty($this->_dqlParts['from']))? ' FROM ' . implode(' ', $this->_dqlParts['from']) : '';
$q .= ( ! empty($this->_dqlParts['where']))? ' WHERE ' . implode(' AND ', $this->_dqlParts['where']) : '';
$q .= ( ! empty($this->_dqlParts['groupby']))? ' GROUP BY ' . implode(', ', $this->_dqlParts['groupby']) : '';
$q .= ( ! empty($this->_dqlParts['having']))? ' HAVING ' . implode(' AND ', $this->_dqlParts['having']) : '';
$q .= ( ! empty($this->_dqlParts['orderby']))? ' ORDER BY ' . implode(', ', $this->_dqlParts['orderby']) : '';
$q .= ( ! empty($this->_dqlParts['limit']))? ' LIMIT ' . implode(' ', $this->_dqlParts['limit']) : '';
$q .= ( ! empty($this->_dqlParts['offset']))? ' OFFSET ' . implode(' ', $this->_dqlParts['offset']) : '';
return $q;
}
/**
* processPendingFields
* the fields in SELECT clause cannot be parsed until the components
......@@ -419,7 +481,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
public function processPendingFields($componentAlias)
{
$tableAlias = $this->getTableAlias($componentAlias);
$table = $this->_aliasMap[$componentAlias]['table'];
$table = $this->_queryComponents[$componentAlias]['table'];
if ( ! isset($this->_pendingFields[$componentAlias])) {
return;
......@@ -434,7 +496,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
} else {
// only auto-add the primary key fields if this query object is not
// a subquery of another query object
if ( ! $this->isSubquery) {
if ( ! $this->_isSubquery) {
$fields = array_unique(array_merge((array) $table->getIdentifier(), $fields));
}
}
......@@ -446,18 +508,13 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
$owner !== $table->getComponentName()) {
$parent = $this->_conn->getTable($owner);
$columnName = $parent->getColumnName($fieldName);
$parentAlias = $this->getTableAlias($componentAlias . '.' . $parent->getComponentName());
$sql[] = $this->_conn->quoteIdentifier($parentAlias . '.' . $columnName)
. ' AS '
. $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName);
} else {
$columnName = $table->getColumnName($fieldName);
$sql[] = $this->_conn->quoteIdentifier($tableAlias . '.' . $columnName)
. ' AS '
. $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName);
......@@ -487,13 +544,13 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
$componentAlias = $terms[0];
$field = $terms[1];
} else {
reset($this->_aliasMap);
$componentAlias = key($this->_aliasMap);
reset($this->_queryComponents);
$componentAlias = key($this->_queryComponents);
$fields = $terms[0];
}
$tableAlias = $this->getTableAlias($componentAlias);
$table = $this->_aliasMap[$componentAlias]['table'];
$table = $this->_queryComponents[$componentAlias]['table'];
// check for wildcards
......@@ -554,14 +611,14 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
*/
public function parseSelect($dql)
{
$refs = Doctrine_Tokenizer::sqlExplode($dql, ',');
$refs = $this->_tokenizer->sqlExplode($dql, ',');
$pos = strpos(trim($refs[0]), ' ');
$first = substr($refs[0], 0, $pos);
// check for DISTINCT keyword
if ($first === 'DISTINCT') {
$this->parts['distinct'] = true;
$this->_sqlParts['distinct'] = true;
$refs[0] = substr($refs[0], ++$pos);
}
......@@ -575,7 +632,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
continue;
}
$terms = Doctrine_Tokenizer::sqlExplode($reference, ' ');
$terms = $this->_tokenizer->sqlExplode($reference, ' ');
$pos = strpos($terms[0], '(');
......@@ -592,16 +649,16 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
$tableAlias = $this->getTableAlias($componentAlias);
$index = count($this->aggregateMap);
$index = count($this->_aggregateAliasMap);
$sqlAlias = $this->_conn->quoteIdentifier($tableAlias . '__' . $index);
$this->parts['select'][] = $expression . ' AS ' . $sqlAlias;
$this->_sqlParts['select'][] = $expression . ' AS ' . $sqlAlias;
$this->aggregateMap[$alias] = $sqlAlias;
$this->_aggregateAliasMap[$alias] = $sqlAlias;
$this->_expressionMap[$alias][0] = $expression;
$this->_aliasMap[$componentAlias]['agg'][$index] = $alias;
$this->_queryComponents[$componentAlias]['agg'][$index] = $alias;
$this->_neededTables[] = $tableAlias;
} else {
......@@ -611,8 +668,8 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
$componentAlias = $e[0];
$field = $e[1];
} else {
reset($this->_aliasMap);
$componentAlias = key($this->_aliasMap);
reset($this->_queryComponents);
$componentAlias = key($this->_queryComponents);
$field = $e[0];
}
......@@ -629,7 +686,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
*
* 1. Converts all DQL functions to their native SQL equivalents
* 2. Converts all component references to their table alias equivalents
* 3. Converts all column aliases to actual column names
* 3. Converts all field names to actual column names
* 4. Quotes all identifiers
* 5. Parses nested clauses and subqueries recursively
*
......@@ -645,7 +702,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
return $clause;
}
$terms = Doctrine_Tokenizer::clauseExplode($clause, array(' ', '+', '-', '*', '/'));
$terms = $this->_tokenizer->clauseExplode($clause, array(' ', '+', '-', '*', '/'));
$str = '';
foreach ($terms as $term) {
......@@ -659,7 +716,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
$args = array();
// parse args
foreach (Doctrine_Tokenizer::sqlExplode($argStr, ',') as $expr) {
foreach ($this->_tokenizer->sqlExplode($argStr, ',') as $expr) {
$args[] = $this->parseClause($expr);
}
......@@ -671,12 +728,12 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
}
$term[0] = $expr;
} else {
$trimmed = trim(Doctrine_Tokenizer::bracketTrim($term[0]));
$trimmed = trim($this->_tokenizer->bracketTrim($term[0]));
// check for possible subqueries
if (substr($trimmed, 0, 4) == 'FROM' || substr($trimmed, 0, 6) == 'SELECT') {
// parse subquery
$trimmed = $this->createSubquery()->parseQuery($trimmed)->getQuery();
$trimmed = $this->createSubquery()->parseDqlQuery($trimmed)->getQuery();
} else {
// parse normal clause
$trimmed = $this->parseClause($trimmed);
......@@ -703,11 +760,11 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
$this->load($componentAlias);
// check the existence of the component alias
if ( ! isset($this->_aliasMap[$componentAlias])) {
if ( ! isset($this->_queryComponents[$componentAlias])) {
throw new Doctrine_Query_Exception('Unknown component alias ' . $componentAlias);
}
$table = $this->_aliasMap[$componentAlias]['table'];
$table = $this->_queryComponents[$componentAlias]['table'];
// get the actual field name from alias
$field = $table->getColumnName($field);
......@@ -740,7 +797,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
if ($componentAlias !== false &&
$componentAlias !== null) {
$table = $this->_aliasMap[$componentAlias]['table'];
$table = $this->_queryComponents[$componentAlias]['table'];
// check column existence
if ($table->hasColumn($term[0])) {
......@@ -766,7 +823,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
}
if ( ! $found) {
$term[0] = $this->getAggregateAlias($term[0]);
$term[0] = $this->getSqlAggregateAlias($term[0]);
}
}
}
......@@ -789,7 +846,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
*/
public function parseAggregateFunction($expr, $nestedCall = false)
{
$e = Doctrine_Tokenizer::bracketExplode($expr, ' ');
$e = $this->_tokenizer->bracketExplode($expr, ' ');
$func = $e[0];
$pos = strpos($func, '(');
......@@ -803,14 +860,14 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
$args = array();
// parse args
foreach (Doctrine_Tokenizer::bracketExplode($argStr, ',') as $expr) {
foreach ($this->_tokenizer->bracketExplode($argStr, ',') as $expr) {
$args[] = $this->parseAggregateFunction($expr, true);
}
// convert DQL function to its RDBMS specific equivalent
try {
$expr = call_user_func_array(array($this->_conn->expression, $name), $args);
} catch(Doctrine_Expression_Exception $e) {
} catch (Doctrine_Expression_Exception $e) {
throw new Doctrine_Query_Exception('Unknown function ' . $func . '.');
}
......@@ -831,7 +888,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
$alias = substr($expr, 0, strpos($expr, '('));
}
$this->pendingAggregates[] = array($expr, $m[0], $alias);
$this->_pendingAggregates[] = array($expr, $m[0], $alias);
}
return $expr;
......@@ -855,18 +912,18 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
$subquery = $this->createSubquery();
$sql = $subquery->parseQuery($dql, false)->getQuery();
$sql = $subquery->parseDqlQuery($dql, false)->getQuery();
reset($this->_aliasMap);
$componentAlias = key($this->_aliasMap);
reset($this->_queryComponents);
$componentAlias = key($this->_queryComponents);
$tableAlias = $this->getTableAlias($componentAlias);
$sqlAlias = $tableAlias . '__' . count($this->aggregateMap);
$sqlAlias = $tableAlias . '__' . count($this->_aggregateAliasMap);
$this->parts['select'][] = '(' . $sql . ') AS ' . $this->_conn->quoteIdentifier($sqlAlias);
$this->_sqlParts['select'][] = '(' . $sql . ') AS ' . $this->_conn->quoteIdentifier($sqlAlias);
$this->aggregateMap[$alias] = $sqlAlias;
$this->_aliasMap[$componentAlias]['agg'][] = $alias;
$this->_aggregateAliasMap[$alias] = $sqlAlias;
$this->_queryComponents[$componentAlias]['agg'][] = $alias;
}
$this->_pendingSubqueries = array();
}
......@@ -881,7 +938,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
public function processPendingAggregates()
{
// iterate trhough all aggregates
foreach ($this->pendingAggregates as $aggregate) {
foreach ($this->_pendingAggregates as $aggregate) {
list ($expression, $components, $alias) = $aggregate;
$tableAliases = array();
......@@ -900,11 +957,11 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
$componentAlias = implode('.', $e);
// check the existence of the component alias
if ( ! isset($this->_aliasMap[$componentAlias])) {
if ( ! isset($this->_queryComponents[$componentAlias])) {
throw new Doctrine_Query_Exception('Unknown component alias ' . $componentAlias);
}
$table = $this->_aliasMap[$componentAlias]['table'];
$table = $this->_queryComponents[$componentAlias]['table'];
$field = $table->getColumnName($field);
......@@ -913,48 +970,49 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
throw new Doctrine_Query_Exception('Unknown column ' . $field);
}
$tableAlias = $this->getTableAlias($componentAlias);
$sqlTableAlias = $this->getSqlTableAlias($componentAlias);
$tableAliases[$tableAlias] = true;
$tableAliases[$sqlTableAlias] = true;
// build sql expression
$identifier = $this->_conn->quoteIdentifier($tableAlias . '.' . $field);
$identifier = $this->_conn->quoteIdentifier($sqlTableAlias . '.' . $field);
$expression = str_replace($component, $identifier, $expression);
}
}
if (count($tableAliases) !== 1) {
$componentAlias = reset($this->_tableAliases);
$tableAlias = key($this->_tableAliases);
$componentAlias = reset($this->_tableAliasMap);
$tableAlias = key($this->_tableAliasMap);
}
$index = count($this->aggregateMap);
$index = count($this->_aggregateAliasMap);
$sqlAlias = $this->_conn->quoteIdentifier($tableAlias . '__' . $index);
$this->parts['select'][] = $expression . ' AS ' . $sqlAlias;
$this->_sqlParts['select'][] = $expression . ' AS ' . $sqlAlias;
$this->aggregateMap[$alias] = $sqlAlias;
$this->_aggregateAliasMap[$alias] = $sqlAlias;
$this->_expressionMap[$alias][0] = $expression;
$this->_aliasMap[$componentAlias]['agg'][$index] = $alias;
$this->_queryComponents[$componentAlias]['agg'][$index] = $alias;
$this->_neededTables[] = $tableAlias;
}
// reset the state
$this->pendingAggregates = array();
$this->_pendingAggregates = array();
}
/**
* getQueryBase
* _getSqlQueryBase
* returns the base of the generated sql query
* On mysql driver special strategy has to be used for DELETE statements
* (where is this special strategy??)
*
* @return string the base of the generated sql query
*/
public function getQueryBase()
protected function _getSqlQueryBase()
{
switch ($this->type) {
switch ($this->_type) {
case self::DELETE:
$q = 'DELETE FROM ';
break;
......@@ -962,24 +1020,23 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
$q = 'UPDATE ';
break;
case self::SELECT:
$distinct = ($this->parts['distinct']) ? 'DISTINCT ' : '';
$q = 'SELECT ' . $distinct . implode(', ', $this->parts['select']) . ' FROM ';
$distinct = ($this->_sqlParts['distinct']) ? 'DISTINCT ' : '';
$q = 'SELECT ' . $distinct . implode(', ', $this->_sqlParts['select']) . ' FROM ';
break;
}
return $q;
}
/**
* buildFromPart
* _buildSqlFromPart
* builds the from part of the query and returns it
*
* @return string the query sql from part
*/
public function buildFromPart()
protected function _buildSqlFromPart()
{
$q = '';
foreach ($this->parts['from'] as $k => $part) {
foreach ($this->_sqlParts['from'] as $k => $part) {
if ($k === 0) {
$q .= $part;
continue;
......@@ -993,7 +1050,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
if (substr($part, 0, 9) === 'LEFT JOIN') {
$e = explode(' ', $part);
$aliases = array_merge($this->subqueryAliases,
$aliases = array_merge($this->_subqueryAliases,
array_keys($this->_neededTables));
if ( ! in_array($e[3], $aliases) &&
......@@ -1021,7 +1078,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
$q .= ' ' . $part;
}
$this->parts['from'][$k] = $part;
$this->_sqlParts['from'][$k] = $part;
}
return $q;
}
......@@ -1054,40 +1111,6 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
}
/**
* processQueryPart
* parses given query part
*
* @param string $queryPartName the name of the query part
* @param array $queryParts an array containing the query part data
* @return Doctrine_Query this object
* @todo Better description. "parses given query part" ??? Then wheres the difference
* between process/parseQueryPart? I suppose this does something different.
*/
public function processQueryPart($queryPartName, $queryParts)
{
$this->removeQueryPart($queryPartName);
if (is_array($queryParts) && ! empty($queryParts)) {
foreach ($queryParts as $queryPart) {
$parser = $this->getParser($queryPartName);
$sql = $parser->parse($queryPart);
if (isset($sql)) {
if ($queryPartName == 'limit' ||
$queryPartName == 'offset') {
$this->setQueryPart($queryPartName, $sql);
} else {
$this->addQueryPart($queryPartName, $sql);
}
}
}
}
}
/**
* builds the sql query from the given parameters and applies things such as
* column aggregation inheritance and limit subqueries if needed
......@@ -1096,7 +1119,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
* when limit subquery algorithm is used)
* @return string the built sql query
*/
public function getQuery($params = array())
public function getSqlQuery($params = array())
{
if ($this->_state !== self::STATE_DIRTY) {
return $this->_sql;
......@@ -1104,60 +1127,62 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
// reset the state
if ( ! $this->isSubquery()) {
$this->_aliasMap = array();
$this->pendingAggregates = array();
$this->aggregateMap = array();
$this->_queryComponents = array();
$this->_pendingAggregates = array();
$this->_aggregateAliasMap = array();
}
$this->reset();
$this->reset();
// parse the DQL parts
// invoke the preQuery hook
$this->preQuery();
// process the DQL parts => generate the SQL parts.
// this will also populate the $_queryComponents.
foreach ($this->_dqlParts as $queryPartName => $queryParts) {
$this->processQueryPart($queryPartName, $queryParts);
$this->_processDqlQueryPart($queryPartName, $queryParts);
}
$this->_state = self::STATE_CLEAN;
$params = $this->convertEnums($params);
$this->_state = self::STATE_DIRECT;
$params = $this->convertEnums($params);
// invoke the preQuery hook
$this->preQuery();
$this->_state = self::STATE_CLEAN;
if (empty($this->parts['from'])) {
// Proceed with the generated SQL
if (empty($this->_sqlParts['from'])) {
return false;
}
$needsSubQuery = false;
$subquery = '';
$map = reset($this->_aliasMap);
$map = reset($this->_queryComponents);
$table = $map['table'];
$rootAlias = key($this->_aliasMap);
$rootAlias = key($this->_queryComponents);
if ( ! empty($this->parts['limit']) && $this->needsSubquery &&
if ( ! empty($this->_sqlParts['limit']) && $this->_needsSubquery &&
$table->getAttribute(Doctrine::ATTR_QUERY_LIMIT) == Doctrine::LIMIT_RECORDS) {
$this->isLimitSubqueryUsed = true;
$this->_isLimitSubqueryUsed = true;
$needsSubQuery = true;
}
$sql = array();
foreach ($this->_aliasMap as $alias => $map) {
foreach ($this->_queryComponents as $alias => $map) {
$fieldSql = $this->processPendingFields($alias);
if ( ! empty($fieldSql)) {
$sql[] = $fieldSql;
}
}
if ( ! empty($sql)) {
array_unshift($this->parts['select'], implode(', ', $sql));
array_unshift($this->_sqlParts['select'], implode(', ', $sql));
}
$this->_pendingFields = array();
// build the basic query
$q = $this->getQueryBase();
$q .= $this->buildFromPart();
$q = $this->_getSqlQueryBase();
$q .= $this->_buildSqlFromPart();
if ( ! empty($this->parts['set'])) {
$q .= ' SET ' . implode(', ', $this->parts['set']);
if ( ! empty($this->_sqlParts['set'])) {
$q .= ' SET ' . implode(', ', $this->_sqlParts['set']);
}
......@@ -1166,14 +1191,14 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
// apply inheritance to WHERE part
if ( ! empty($string)) {
if (substr($string, 0, 1) === '(' && substr($string, -1) === ')') {
$this->parts['where'][] = $string;
$this->_sqlParts['where'][] = $string;
} else {
$this->parts['where'][] = '(' . $string . ')';
$this->_sqlParts['where'][] = '(' . $string . ')';
}
}
$modifyLimit = true;
if ( ! empty($this->parts['limit']) || ! empty($this->parts['offset'])) {
if ( ! empty($this->_sqlParts['limit']) || ! empty($this->_sqlParts['offset'])) {
if ($needsSubQuery) {
$subquery = $this->getLimitSubquery();
......@@ -1191,32 +1216,32 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
break;
}
$field = $this->getTableAlias($rootAlias) . '.' . $idColumnName;
$field = $this->getSqlTableAlias($rootAlias) . '.' . $idColumnName;
// only append the subquery if it actually contains something
if ($subquery !== '') {
array_unshift($this->parts['where'], $this->_conn->quoteIdentifier($field) . ' IN (' . $subquery . ')');
array_unshift($this->_sqlParts['where'], $this->_conn->quoteIdentifier($field) . ' IN (' . $subquery . ')');
}
$modifyLimit = false;
}
}
$q .= ( ! empty($this->parts['where']))? ' WHERE ' . implode(' AND ', $this->parts['where']) : '';
$q .= ( ! empty($this->parts['groupby']))? ' GROUP BY ' . implode(', ', $this->parts['groupby']) : '';
$q .= ( ! empty($this->parts['having']))? ' HAVING ' . implode(' AND ', $this->parts['having']): '';
$q .= ( ! empty($this->parts['orderby']))? ' ORDER BY ' . implode(', ', $this->parts['orderby']) : '';
$q .= ( ! empty($this->_sqlParts['where']))? ' WHERE ' . implode(' AND ', $this->_sqlParts['where']) : '';
$q .= ( ! empty($this->_sqlParts['groupby']))? ' GROUP BY ' . implode(', ', $this->_sqlParts['groupby']) : '';
$q .= ( ! empty($this->_sqlParts['having']))? ' HAVING ' . implode(' AND ', $this->_sqlParts['having']): '';
$q .= ( ! empty($this->_sqlParts['orderby']))? ' ORDER BY ' . implode(', ', $this->_sqlParts['orderby']) : '';
if ($modifyLimit) {
$q = $this->_conn->modifyLimitQuery($q, $this->parts['limit'], $this->parts['offset']);
$q = $this->_conn->modifyLimitQuery($q, $this->_sqlParts['limit'], $this->_sqlParts['offset']);
}
// return to the previous state
if ( ! empty($string)) {
array_pop($this->parts['where']);
array_pop($this->_sqlParts['where']);
}
if ($needsSubQuery) {
array_shift($this->parts['where']);
array_shift($this->_sqlParts['where']);
}
$this->_sql = $q;
......@@ -1236,9 +1261,9 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
*/
public function getLimitSubquery()
{
$map = reset($this->_aliasMap);
$map = reset($this->_queryComponents);
$table = $map['table'];
$componentAlias = key($this->_aliasMap);
$componentAlias = key($this->_queryComponents);
// get short alias
$alias = $this->getTableAlias($componentAlias);
......@@ -1252,9 +1277,9 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
// pgsql needs the order by fields to be preserved in select clause
if ($driverName == 'pgsql') {
foreach ($this->parts['orderby'] as $part) {
foreach ($this->_sqlParts['orderby'] as $part) {
$part = trim($part);
$e = Doctrine_Tokenizer::bracketExplode($part, ' ');
$e = $this->_tokenizer->bracketExplode($part, ' ');
$part = trim($e[0]);
if (strpos($part, '.') === false) {
......@@ -1276,19 +1301,19 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
if ($driverName == 'mysql' || $driverName == 'pgsql') {
foreach ($this->_expressionMap as $dqlAlias => $expr) {
if (isset($expr[1])) {
$subquery .= ', ' . $expr[0] . ' AS ' . $this->aggregateMap[$dqlAlias];
$subquery .= ', ' . $expr[0] . ' AS ' . $this->_aggregateAliasMap[$dqlAlias];
}
}
}
$subquery .= ' FROM';
foreach ($this->parts['from'] as $part) {
foreach ($this->_sqlParts['from'] as $part) {
// preserve LEFT JOINs only if needed
if (substr($part, 0, 9) === 'LEFT JOIN') {
$e = explode(' ', $part);
if (empty($this->parts['orderby']) && empty($this->parts['where'])) {
if (empty($this->_sqlParts['orderby']) && empty($this->_sqlParts['where'])) {
continue;
}
}
......@@ -1297,16 +1322,16 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
}
// all conditions must be preserved in subquery
$subquery .= ( ! empty($this->parts['where']))? ' WHERE ' . implode(' AND ', $this->parts['where']) : '';
$subquery .= ( ! empty($this->parts['groupby']))? ' GROUP BY ' . implode(', ', $this->parts['groupby']) : '';
$subquery .= ( ! empty($this->parts['having']))? ' HAVING ' . implode(' AND ', $this->parts['having']) : '';
$subquery .= ( ! empty($this->_sqlParts['where']))? ' WHERE ' . implode(' AND ', $this->_sqlParts['where']) : '';
$subquery .= ( ! empty($this->_sqlParts['groupby']))? ' GROUP BY ' . implode(', ', $this->_sqlParts['groupby']) : '';
$subquery .= ( ! empty($this->_sqlParts['having']))? ' HAVING ' . implode(' AND ', $this->_sqlParts['having']) : '';
$subquery .= ( ! empty($this->parts['orderby']))? ' ORDER BY ' . implode(', ', $this->parts['orderby']) : '';
$subquery .= ( ! empty($this->_sqlParts['orderby']))? ' ORDER BY ' . implode(', ', $this->_sqlParts['orderby']) : '';
// add driver specific limit clause
$subquery = $this->_conn->modifyLimitQuery($subquery, $this->parts['limit'], $this->parts['offset']);
$subquery = $this->_conn->modifyLimitQuery($subquery, $this->_sqlParts['limit'], $this->_sqlParts['offset']);
$parts = Doctrine_Tokenizer::quoteExplode($subquery, ' ', "'", "'");
$parts = $this->_tokenizer->quoteExplode($subquery, ' ', "'", "'");
foreach ($parts as $k => $part) {
if (strpos($part, ' ') !== false) {
......@@ -1378,11 +1403,12 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
*/
public function tokenizeQuery($query)
{
$e = Doctrine_Tokenizer::sqlExplode($query, ' ');
$parts = array();
$tokens = $this->_tokenizer->sqlExplode($query, ' ');
foreach ($e as $k => $part) {
$part = trim($part);
switch (strtolower($part)) {
foreach ($tokens as $index => $token) {
$token = trim($token);
switch (strtolower($token)) {
case 'delete':
case 'update':
case 'select':
......@@ -1392,27 +1418,32 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
case 'limit':
case 'offset':
case 'having':
$p = $part;
$parts[$part] = array();
$p = $token;
//$parts[$token] = array();
$parts[$token] = '';
break;
case 'order':
case 'group':
$i = ($k + 1);
if (isset($e[$i]) && strtolower($e[$i]) === 'by') {
$p = $part;
$parts[$part] = array();
$i = ($index + 1);
if (isset($tokens[$i]) && strtolower($tokens[$i]) === 'by') {
$p = $token;
$parts[$token] = '';
//$parts[$token] = array();
} else {
$parts[$p][] = $part;
$parts[$p] .= "$token ";
//$parts[$p][] = $token;
}
break;
case 'by':
continue;
default:
if ( ! isset($p)) {
throw new Doctrine_Query_Exception("Couldn't parse query.");
throw new Doctrine_Query_Tokenizer_Exception(
"Couldn't tokenize query. Encountered invalid token: '$token'.");
}
$parts[$p][] = $part;
$parts[$p] .= "$token ";
//$parts[$p][] = $token;
}
}
return $parts;
......@@ -1429,7 +1460,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
* @throws Doctrine_Query_Exception if some generic parsing error occurs
* @return Doctrine_Query
*/
public function parseQuery($query, $clear = true)
public function parseDqlQuery($query, $clear = true)
{
if ($clear) {
$this->clear();
......@@ -1441,40 +1472,40 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
$parts = $this->tokenizeQuery($query);
foreach ($parts as $k => $part) {
$part = implode(' ', $part);
$k = strtolower($k);
switch ($k) {
foreach ($parts as $partName => $subParts) {
$subParts = trim($subParts);
$partName = strtolower($partName);
switch ($partName) {
case 'create':
$this->type = self::CREATE;
$this->_type = self::CREATE;
break;
case 'insert':
$this->type = self::INSERT;
$this->_type = self::INSERT;
break;
case 'delete':
$this->type = self::DELETE;
$this->_type = self::DELETE;
break;
case 'select':
$this->type = self::SELECT;
$this->parseQueryPart($k, $part);
$this->_type = self::SELECT;
$this->_addDqlQueryPart($partName, $subParts);
break;
case 'update':
$this->type = self::UPDATE;
$k = 'from';
$this->_type = self::UPDATE;
$partName = 'from';
case 'from':
$this->parseQueryPart($k, $part);
$this->_addDqlQueryPart($partName, $subParts);
break;
case 'set':
$this->parseQueryPart($k, $part, true);
$this->_addDqlQueryPart($partName, $subParts, true);
break;
case 'group':
case 'order':
$k .= 'by';
$partName .= 'by';
case 'where':
case 'having':
case 'limit':
case 'offset':
$this->parseQueryPart($k, $part);
$this->_addDqlQueryPart($partName, $subParts);
break;
}
}
......@@ -1487,10 +1518,10 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
*/
public function load($path, $loadFields = true)
{
if (isset($this->_aliasMap[$path])) {
return $this->_aliasMap[$path];
if (isset($this->_queryComponents[$path])) {
return $this->_queryComponents[$path];
}
$e = Doctrine_Tokenizer::quoteExplode($path, ' INDEXBY ');
$e = $this->_tokenizer->quoteExplode($path, ' INDEXBY ');
$mapWith = null;
if (count($e) > 1) {
......@@ -1527,8 +1558,8 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
$prevPath = '';
$fullLength = strlen($fullPath);
if (isset($this->_aliasMap[$e[0]])) {
$table = $this->_aliasMap[$e[0]]['table'];
if (isset($this->_queryComponents[$e[0]])) {
$table = $this->_queryComponents[$e[0]]['table'];
$componentAlias = $e[0];
$prevPath = $parent = array_shift($e);
......@@ -1551,7 +1582,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
}
// if the current alias already exists, skip it
if (isset($this->_aliasMap[$componentAlias])) {
if (isset($this->_queryComponents[$componentAlias])) {
continue;
}
......@@ -1566,12 +1597,12 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
$localTable = $table;
$table = $relation->getTable();
$this->_aliasMap[$componentAlias] = array('table' => $table,
$this->_queryComponents[$componentAlias] = array('table' => $table,
'parent' => $parent,
'relation' => $relation,
'map' => null);
if ( ! $relation->isOneToOne()) {
$this->needsSubquery = true;
$this->_needsSubquery = true;
}
$localAlias = $this->getTableAlias($parent, $table->getTableName());
......@@ -1587,7 +1618,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
$map = $relation->getTable()->inheritanceMap;
if ( ! $loadFields || ! empty($map) || $joinCondition) {
$this->subqueryAliases[] = $foreignAlias;
$this->_subqueryAliases[] = $foreignAlias;
}
if ($relation instanceof Doctrine_Relation_Association) {
......@@ -1596,12 +1627,12 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
$assocTableName = $asf->getTableName();
if ( ! $loadFields || ! empty($map) || $joinCondition) {
$this->subqueryAliases[] = $assocTableName;
$this->_subqueryAliases[] = $assocTableName;
}
$assocPath = $prevPath . '.' . $asf->getComponentName();
$this->_aliasMap[$assocPath] = array('parent' => $prevPath, 'relation' => $relation, 'table' => $asf);
$this->_queryComponents[$assocPath] = array('parent' => $prevPath, 'relation' => $relation, 'table' => $asf);
$assocAlias = $this->getTableAlias($assocPath, $asf->getTableName());
......@@ -1622,7 +1653,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
. $assocAlias . '.' . $relation->getForeign();
}
$this->parts['from'][] = $queryPart;
$this->_sqlParts['from'][] = $queryPart;
$queryPart = $join . $foreignSql;
......@@ -1662,7 +1693,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
$queryPart .= $this->buildInheritanceJoinSql($table->getComponentName(), $componentAlias);
$this->parts['from'][$componentAlias] = $queryPart;
$this->_sqlParts['from'][$componentAlias] = $queryPart;
if ( ! empty($joinCondition)) {
$this->_pendingJoinConditions[$componentAlias] = $joinCondition;
}
......@@ -1678,7 +1709,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
$parent = $prevPath;
}
$table = $this->_aliasMap[$componentAlias]['table'];
$table = $this->_queryComponents[$componentAlias]['table'];
$indexBy = null;
......@@ -1697,9 +1728,9 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
throw new Doctrine_Query_Exception("Couldn't use key mapping. Column " . $indexBy . " does not exist.");
}
$this->_aliasMap[$componentAlias]['map'] = $table->getColumnName($indexBy);
$this->_queryComponents[$componentAlias]['map'] = $table->getColumnName($indexBy);
}
return $this->_aliasMap[$componentAlias];
return $this->_queryComponents[$componentAlias];
}
/**
......@@ -1723,17 +1754,17 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
// quote table name
$queryPart = $this->_conn->quoteIdentifier($tableName);
if ($this->type === self::SELECT) {
if ($this->_type === self::SELECT) {
$queryPart .= ' ' . $this->_conn->quoteIdentifier($tableAlias);
}
$this->_tableAliases[$tableAlias] = $componentAlias;
$this->_tableAliasMap[$tableAlias] = $componentAlias;
$queryPart .= $this->buildInheritanceJoinSql($name, $componentAlias);
$this->parts['from'][] = $queryPart;
$this->_sqlParts['from'][] = $queryPart;
$this->_aliasMap[$componentAlias] = array('table' => $table, 'map' => null);
$this->_queryComponents[$componentAlias] = array('table' => $table, 'map' => null);
return $table;
}
......@@ -1802,14 +1833,15 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
*/
public function count($params = array())
{
$this->getQuery();
// triggers dql parsing/processing
$this->getQuery(); // this is ugly
// initialize temporary variables
$where = $this->parts['where'];
$having = $this->parts['having'];
$groupby = $this->parts['groupby'];
$map = reset($this->_aliasMap);
$componentAlias = key($this->_aliasMap);
$where = $this->_sqlParts['where'];
$having = $this->_sqlParts['having'];
$groupby = $this->_sqlParts['groupby'];
$map = reset($this->_queryComponents);
$componentAlias = key($this->_queryComponents);
$table = $map['table'];
// build the query base
......@@ -1817,13 +1849,13 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
. '.' . implode(',', (array) $table->getIdentifier())
. ') AS num_results';
foreach ($this->parts['select'] as $field) {
foreach ($this->_sqlParts['select'] as $field) {
if (strpos($field, '(') !== false) {
$q .= ', ' . $field;
}
}
$q .= ' FROM ' . $this->buildFromPart();
$q .= ' FROM ' . $this->_buildSqlFromPart();
// append column aggregation inheritance (if needed)
$string = $this->applyInheritance();
......@@ -1870,8 +1902,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria
*/
public function query($query, $params = array(), $hydrationMode = null)
{
$this->parseQuery($query);
$this->parseDqlQuery($query);
return $this->execute($params, $hydrationMode);
}
......
......@@ -29,6 +29,7 @@ Doctrine::autoload('Doctrine_Hydrate');
* @since 1.0
* @version $Revision: 1393 $
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @todo See {@link Doctrine_Query}
*/
abstract class Doctrine_Query_Abstract
{
......@@ -62,101 +63,79 @@ abstract class Doctrine_Query_Abstract
const CREATE = 4;
/** @todo document the query states (and the transitions between them). */
/**
* A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts.
*/
const STATE_CLEAN = 1;
/**
* A query object is in state DIRTY when it has DQL parts that have not yet been
* parsed/processed.
*/
const STATE_DIRTY = 2;
/**
* A query is in DIRECT state when ... ?
*/
const STATE_DIRECT = 3;
const STATE_LOCKED = 4;
/**
* A query object is on LOCKED state when ... ?
*/
const STATE_LOCKED = 4;
protected $_tableAliases = array();
/**
* @var array Table alias map. Keys are SQL aliases and values DQL aliases.
*/
protected $_tableAliasMap = array();
/**
* @var Doctrine_View The view object used by this query, if any.
*/
protected $_view;
/**
* @var integer $_state The current state of this query.
*/
protected $_state = Doctrine_Query::STATE_CLEAN;
/**
* @var array $params query input parameters
* @var array $params The parameters of this query.
*/
protected $_params = array('where' => array(),
'set' => array(),
'having' => array());
/* Caching properties */
/**
* @var Doctrine_Cache_Interface The cache driver used for caching result sets.
*/
protected $_resultCache;
/**
* @var array
*/
protected $_cache;
* @var boolean $_expireResultCache A boolean value that indicates whether or not
* expire the result cache.
*/
protected $_expireResultCache = false;
protected $_resultCacheTTL;
/**
* @var boolean $_expireCache A boolean value that indicates whether or not to force cache expiration.
/**
* @var Doctrine_Cache_Interface The cache driver used for caching queries.
*/
protected $_expireCache = false;
protected $_timeToLive;
protected $_queryCache;
protected $_expireQueryCache = false;
protected $_queryCacheTTL;
/**
* @var Doctrine_Connection The connection used by this query object.
*/
protected $_conn;
/**
* @var array The DQL keywords.
*/
protected static $_keywords = array('ALL',
'AND',
'ANY',
'AS',
'ASC',
'AVG',
'BETWEEN',
'BIT_LENGTH',
'BY',
'CHARACTER_LENGTH',
'CHAR_LENGTH',
'CURRENT_DATE',
'CURRENT_TIME',
'CURRENT_TIMESTAMP',
'DELETE',
'DESC',
'DISTINCT',
'EMPTY',
'EXISTS',
'FALSE',
'FETCH',
'FROM',
'GROUP',
'HAVING',
'IN',
'INDEXBY',
'INNER',
'IS',
'JOIN',
'LEFT',
'LIKE',
'LOWER',
'MEMBER',
'MOD',
'NEW',
'NOT',
'NULL',
'OBJECT',
'OF',
'OR',
'ORDER',
'OUTER',
'POSITION',
'SELECT',
'SOME',
'TRIM',
'TRUE',
'UNKNOWN',
'UPDATE',
'WHERE');
/**
* @var array $parts The SQL query string parts. Filled during the DQL parsing process.
*/
protected $parts = array(
* @var array $_sqlParts The SQL query string parts. Filled during the DQL parsing process.
*/
protected $_sqlParts = array(
'select' => array(),
'distinct' => false,
'forUpdate' => false,
......@@ -171,10 +150,30 @@ abstract class Doctrine_Query_Abstract
'offset' => false,
);
/**
* @var array $_dqlParts an array containing all DQL query parts
*/
protected $_dqlParts = array(
'from' => array(),
'select' => array(),
'forUpdate' => false,
'set' => array(),
'join' => array(),
'where' => array(),
'groupby' => array(),
'having' => array(),
'orderby' => array(),
'limit' => array(),
'offset' => array(),
);
/**
* @var array $_aliasMap two dimensional array containing the map for query aliases
* Main keys are component aliases
* @var array $_queryComponents Two dimensional array containing the components of this query,
* informations about their relations and other related information.
* The components are constructed during query parsing.
*
* Keys are component aliases and values the following:
*
* table table object associated with given alias
*
......@@ -187,23 +186,33 @@ abstract class Doctrine_Query_Abstract
* map the name of the column / aggregate value this
* component is mapped to a collection
*/
protected $_aliasMap = array();
protected $_queryComponents = array();
/**
* @var integer $type the query type
*
* @see Doctrine_Query::* constants
*/
protected $type = self::SELECT;
protected $_type = self::SELECT;
/**
* @var Doctrine_Hydrator The hydrator object used to hydrate query results.
*/
protected $_hydrator;
/**
* @var Doctrine_Query_Tokenizer The tokenizer that is used during the query parsing process.
*/
protected $_tokenizer;
/**
* @var Doctrine_Query_Parser The parser that is used for query parsing.
*/
protected $_parser;
/**
* @var array $_tableAliasSeeds A simple array keys representing table aliases and values
* as table alias seeds. The seeds are used for generating short table
* table alias seeds. The seeds are used for generating short table
* aliases.
*/
protected $_tableAliasSeeds = array();
......@@ -212,29 +221,23 @@ abstract class Doctrine_Query_Abstract
* @var array $_options an array of options
*/
protected $_options = array(
'fetchMode' => Doctrine::FETCH_RECORD,
'parserCache' => false,
'resultSetCache' => false,
'fetchMode' => Doctrine::FETCH_RECORD
);
/**
* @var array $aggregateMap an array containing all aggregate aliases, keys as dql aliases
* and values as sql aliases
*/
protected $aggregateMap = array();
protected $pendingAggregates = array();
protected $inheritanceApplied = false;
/**
* @var array $_enumParams an array containing the keys of the parameters that should be enumerated
*/
protected $_enumParams = array();
protected $isLimitSubqueryUsed = false;
protected $_isLimitSubqueryUsed = false;
protected $_sqlBuilder;
/**
*
*
*/
public function __construct(Doctrine_Connection $connection = null,
Doctrine_Hydrator_Abstract $hydrator = null)
{
......@@ -246,6 +249,8 @@ abstract class Doctrine_Query_Abstract
}
$this->_conn = $connection;
$this->_hydrator = $hydrator;
$this->_tokenizer = new Doctrine_Query_Tokenizer();
$this->_sqlBuilder = new Doctrine_Query_SqlBuilder($this->_tokenizer);
}
/**
......@@ -269,10 +274,23 @@ abstract class Doctrine_Query_Abstract
*
* @param string $tableAlias the table alias to be checked
* @return boolean true if this object has given alias, otherwise false
* @deprecated
*/
public function hasTableAlias($sqlTableAlias)
{
return $this->hasSqlTableAlias($sqlTableAlias);
}
/**
* hasSqlTableAlias
* whether or not this object has given tableAlias
*
* @param string $tableAlias the table alias to be checked
* @return boolean true if this object has given alias, otherwise false
*/
public function hasTableAlias($tableAlias)
public function hasSqlTableAlias($sqlTableAlias)
{
return (isset($this->_tableAliases[$tableAlias]));
return (isset($this->_tableAliasMap[$sqlTableAlias]));
}
/**
......@@ -280,10 +298,22 @@ abstract class Doctrine_Query_Abstract
* returns all table aliases
*
* @return array table aliases as an array
* @deprecated
*/
public function getTableAliases()
{
return $this->_tableAliases;
return $this->getTableAliasMap();
}
/**
* getTableAliasMap
* returns all table aliases
*
* @return array table aliases as an array
*/
public function getTableAliasMap()
{
return $this->_tableAliasMap;
}
/**
......@@ -292,16 +322,30 @@ abstract class Doctrine_Query_Abstract
*
* @param string $name the name of the query part to be set
* @param string $part query part string
* @throws Doctrine_Hydrate_Exception if trying to set unknown query part
* @return Doctrine_Hydrate this object
* @throws Doctrine_Query_Exception if trying to set unknown query part
* @return Doctrine_Query_Abstract this object
* @deprecated
*/
public function getQueryPart($part)
{
if ( ! isset($this->parts[$part])) {
throw new Doctrine_Hydrate_Exception('Unknown query part ' . $part);
return $this->getSqlQueryPart($part);
}
/**
* getSqlQueryPart
* gets an SQL query part from the SQL query part array
*
* @param string $name the name of the query part to be set
* @param string $part query part string
* @throws Doctrine_Query_Exception if trying to set unknown query part
* @return Doctrine_Hydrate this object
*/
public function getSqlQueryPart($part)
{
if ( ! isset($this->_sqlParts[$part])) {
throw new Doctrine_Query_Exception('Unknown SQL query part ' . $part);
}
return $this->parts[$part];
return $this->_sqlParts[$part];
}
/**
......@@ -310,23 +354,38 @@ abstract class Doctrine_Query_Abstract
*
* @param string $name the name of the query part to be set
* @param string $part query part string
* @throws Doctrine_Hydrate_Exception if trying to set unknown query part
* @throws Doctrine_Query_Exception if trying to set unknown query part
* @return Doctrine_Hydrate this object
* @deprecated
*/
public function setQueryPart($name, $part)
{
if ( ! isset($this->parts[$name])) {
throw new Doctrine_Hydrate_Exception('Unknown query part ' . $name);
return $this->setSqlQueryPart($name, $part);
}
/**
* setSqlQueryPart
* sets an SQL query part in the SQL query part array
*
* @param string $name the name of the query part to be set
* @param string $part query part string
* @throws Doctrine_Query_Exception if trying to set unknown query part
* @return Doctrine_Hydrate this object
*/
public function setSqlQueryPart($name, $part)
{
if ( ! isset($this->_sqlParts[$name])) {
throw new Doctrine_Query_Exception('Unknown query part ' . $name);
}
if ($name !== 'limit' && $name !== 'offset') {
if (is_array($part)) {
$this->parts[$name] = $part;
$this->_sqlParts[$name] = $part;
} else {
$this->parts[$name] = array($part);
$this->_sqlParts[$name] = array($part);
}
} else {
$this->parts[$name] = $part;
$this->_sqlParts[$name] = $part;
}
return $this;
......@@ -338,18 +397,33 @@ abstract class Doctrine_Query_Abstract
*
* @param string $name the name of the query part to be added
* @param string $part query part string
* @throws Doctrine_Hydrate_Exception if trying to add unknown query part
* @throws Doctrine_Query_Exception if trying to add unknown query part
* @return Doctrine_Hydrate this object
* @deprecated
*/
public function addQueryPart($name, $part)
{
if ( ! isset($this->parts[$name])) {
throw new Doctrine_Hydrate_Exception('Unknown query part ' . $name);
return $this->addSqlQueryPart($name, $part);
}
/**
* addSqlQueryPart
* adds an SQL query part to the SQL query part array
*
* @param string $name the name of the query part to be added
* @param string $part query part string
* @throws Doctrine_Query_Exception if trying to add unknown query part
* @return Doctrine_Hydrate this object
*/
public function addSqlQueryPart($name, $part)
{
if ( ! isset($this->_sqlParts[$name])) {
throw new Doctrine_Query_Exception('Unknown query part ' . $name);
}
if (is_array($part)) {
$this->parts[$name] = array_merge($this->parts[$name], $part);
$this->_sqlParts[$name] = array_merge($this->_sqlParts[$name], $part);
} else {
$this->parts[$name][] = $part;
$this->_sqlParts[$name][] = $part;
}
return $this;
}
......@@ -359,19 +433,35 @@ abstract class Doctrine_Query_Abstract
* removes a query part from the query part array
*
* @param string $name the name of the query part to be removed
* @throws Doctrine_Hydrate_Exception if trying to remove unknown query part
* @throws Doctrine_Query_Exception if trying to remove unknown query part
* @return Doctrine_Hydrate this object
* @deprecated
*/
public function removeQueryPart($name)
{
if ( ! isset($this->parts[$name])) {
throw new Doctrine_Hydrate_Exception('Unknown query part ' . $name);
}
return $this->removeSqlQueryPart($name);
}
/**
* removeSqlQueryPart
* removes a query part from the query part array
*
* @param string $name the name of the query part to be removed
* @throws Doctrine_Query_Exception if trying to remove unknown query part
* @return Doctrine_Hydrate this object
*/
public function removeSqlQueryPart($name)
{
try {
if ( ! isset($this->_sqlParts[$name])) {
throw new Doctrine_Query_Exception('Unknown query part ' . $name);
}}
catch (Exception $e) {echo $e->getTraceAsString(); echo "<br /><br /><br />";}
if ($name == 'limit' || $name == 'offset') {
$this->parts[$name] = false;
$this->_sqlParts[$name] = false;
} else {
$this->parts[$name] = array();
$this->_sqlParts[$name] = array();
}
return $this;
}
......@@ -407,7 +497,7 @@ abstract class Doctrine_Query_Abstract
*/
public function isLimitSubqueryUsed()
{
return $this->isLimitSubqueryUsed;
return $this->_isLimitSubqueryUsed;
}
/**
......@@ -439,8 +529,8 @@ abstract class Doctrine_Query_Abstract
// get the inheritance maps
$array = array();
foreach ($this->_aliasMap as $componentAlias => $data) {
$tableAlias = $this->getTableAlias($componentAlias);
foreach ($this->_queryComponents as $componentAlias => $data) {
$tableAlias = $this->getSqlTableAlias($componentAlias);
$array[$tableAlias][] = $data['table']->inheritanceMap;
}
......@@ -453,7 +543,7 @@ abstract class Doctrine_Query_Abstract
$a = array();
// don't use table aliases if the query isn't a select query
if ($this->type !== Doctrine_Query::SELECT) {
if ($this->_type !== Doctrine_Query::SELECT) {
$tableAlias = '';
} else {
$tableAlias .= '.';
......@@ -499,17 +589,35 @@ abstract class Doctrine_Query_Abstract
* @param string $componentAlias the alias for the query component to search table alias for
* @param string $tableName the table name from which the table alias is being created
* @return string the generated / fetched short alias
* @deprecated
*/
public function getTableAlias($componentAlias, $tableName = null)
{
$alias = array_search($componentAlias, $this->_tableAliases);
return $this->getSqlTableAlias($componentAlias, $tableName);
}
/**
* getSqlTableAlias
* some database such as Oracle need the identifier lengths to be < ~30 chars
* hence Doctrine creates as short identifier aliases as possible
*
* this method is used for the creation of short table aliases, its also
* smart enough to check if an alias already exists for given component (componentAlias)
*
* @param string $componentAlias the alias for the query component to search table alias for
* @param string $tableName the table name from which the table alias is being created
* @return string the generated / fetched short alias
*/
public function getSqlTableAlias($componentAlias, $tableName = null)
{
$alias = array_search($componentAlias, $this->_tableAliasMap);
if ($alias !== false) {
return $alias;
}
if ($tableName === null) {
throw new Doctrine_Hydrate_Exception("Couldn't get short alias for " . $componentAlias);
throw new Doctrine_Query_Exception("Couldn't get short alias for " . $componentAlias);
}
return $this->generateTableAlias($componentAlias, $tableName);
......@@ -521,13 +629,26 @@ abstract class Doctrine_Query_Abstract
*
* @param string $tableAlias table alias from which to generate the new alias from
* @return string the created table alias
* @deprecated
*/
public function generateNewTableAlias($tableAlias)
public function generateNewTableAlias($oldAlias)
{
if (isset($this->_tableAliases[$tableAlias])) {
return $this->generateNewSqlTableAlias($oldAlias);
}
/**
* generateNewSqlTableAlias
* generates a new alias from given table alias
*
* @param string $tableAlias table alias from which to generate the new alias from
* @return string the created table alias
*/
public function generateNewSqlTableAlias($oldAlias)
{
if (isset($this->_tableAliasMap[$oldAlias])) {
// generate a new alias
$name = substr($tableAlias, 0, 1);
$i = ((int) substr($tableAlias, 1));
$name = substr($oldAlias, 0, 1);
$i = ((int) substr($oldAlias, 1));
if ($i == 0) {
$i = 1;
......@@ -538,7 +659,7 @@ abstract class Doctrine_Query_Abstract
return $name . $newIndex;
}
return $tableAlias;
return $oldAlias;
}
/**
......@@ -547,13 +668,26 @@ abstract class Doctrine_Query_Abstract
*
* @param string $tableAlias table alias that identifies the alias seed
* @return integer table alias seed
* @deprecated
*/
public function getTableAliasSeed($sqlTableAlias)
{
return $this->getSqlTableAliasSeed($sqlTableAlias);
}
/**
* getSqlTableAliasSeed
* returns the alias seed for given table alias
*
* @param string $tableAlias table alias that identifies the alias seed
* @return integer table alias seed
*/
public function getTableAliasSeed($tableAlias)
public function getSqlTableAliasSeed($sqlTableAlias)
{
if ( ! isset($this->_tableAliasSeeds[$tableAlias])) {
if ( ! isset($this->_tableAliasSeeds[$sqlTableAlias])) {
return 0;
}
return $this->_tableAliasSeeds[$tableAlias];
return $this->_tableAliasSeeds[$sqlTableAlias];
}
/**
......@@ -565,7 +699,7 @@ abstract class Doctrine_Query_Abstract
*/
public function hasAliasDeclaration($componentAlias)
{
return isset($this->_aliasMap[$componentAlias]);
return isset($this->_queryComponents[$componentAlias]);
}
/**
......@@ -574,14 +708,27 @@ abstract class Doctrine_Query_Abstract
*
* @param string $componentAlias the component alias the retrieve the declaration from
* @return array the alias declaration
* @deprecated
*/
public function getAliasDeclaration($componentAlias)
{
if ( ! isset($this->_aliasMap[$componentAlias])) {
throw new Doctrine_Hydrate_Exception('Unknown component alias ' . $componentAlias);
return $this->getQueryComponent($componentAlias);
}
/**
* getQueryComponent
* get the declaration for given component alias
*
* @param string $componentAlias the component alias the retrieve the declaration from
* @return array the alias declaration
*/
public function getQueryComponent($componentAlias)
{
if ( ! isset($this->_queryComponents[$componentAlias])) {
throw new Doctrine_Query_Exception('Unknown component alias ' . $componentAlias);
}
return $this->_aliasMap[$componentAlias];
return $this->_queryComponents[$componentAlias];
}
/**
......@@ -597,8 +744,8 @@ abstract class Doctrine_Query_Abstract
*/
public function copyAliases(Doctrine_Query_Abstract $query)
{
$this->_tableAliases = $query->_tableAliases;
$this->_aliasMap = $query->_aliasMap;
$this->_tableAliasMap = $query->_tableAliasMap;
$this->_queryComponents = $query->_queryComponents;
$this->_tableAliasSeeds = $query->_tableAliasSeeds;
return $this;
}
......@@ -611,12 +758,12 @@ abstract class Doctrine_Query_Abstract
*/
public function getRootAlias()
{
if ( ! $this->_aliasMap) {
if ( ! $this->_queryComponents) {
$this->getSql();
}
reset($this->_aliasMap);
reset($this->_queryComponents);
return key($this->_aliasMap);
return key($this->_queryComponents);
}
/**
......@@ -627,7 +774,7 @@ abstract class Doctrine_Query_Abstract
*/
public function getRootDeclaration()
{
$map = reset($this->_aliasMap);
$map = reset($this->_queryComponents);
return $map;
}
......@@ -639,10 +786,10 @@ abstract class Doctrine_Query_Abstract
*/
public function getRoot()
{
$map = reset($this->_aliasMap);
$map = reset($this->_queryComponents);
if ( ! isset($map['table'])) {
throw new Doctrine_Hydrate_Exception('Root component not initialized.');
throw new Doctrine_Query_Exception('Root component not initialized.');
}
return $map['table'];
......@@ -656,8 +803,23 @@ abstract class Doctrine_Query_Abstract
* @param string $componentAlias the component alias to be associated with generated table alias
* @param string $tableName the table name from which to generate the table alias
* @return string the generated table alias
* @deprecated
*/
public function generateTableAlias($componentAlias, $tableName)
{
return $this->generateSqlTableAlias($componentAlias, $tableName);
}
/**
* generateSqlTableAlias
* generates a table alias from given table name and associates
* it with given component alias
*
* @param string $componentAlias the component alias to be associated with generated table alias
* @param string $tableName the table name from which to generate the table alias
* @return string the generated table alias
*/
public function generateSqlTableAlias($componentAlias, $tableName)
{
$char = strtolower(substr($tableName, 0, 1));
......@@ -667,14 +829,14 @@ abstract class Doctrine_Query_Abstract
$this->_tableAliasSeeds[$alias] = 1;
}
while (isset($this->_tableAliases[$alias])) {
while (isset($this->_tableAliasMap[$alias])) {
if ( ! isset($this->_tableAliasSeeds[$alias])) {
$this->_tableAliasSeeds[$alias] = 1;
}
$alias = $char . ++$this->_tableAliasSeeds[$alias];
}
$this->_tableAliases[$alias] = $componentAlias;
$this->_tableAliasMap[$alias] = $componentAlias;
return $alias;
}
......@@ -683,15 +845,15 @@ abstract class Doctrine_Query_Abstract
* getComponentAlias
* get component alias associated with given table alias
*
* @param string $tableAlias the table alias that identifies the component alias
* @param string $sqlTableAlias the SQL table alias that identifies the component alias
* @return string component alias
*/
public function getComponentAlias($tableAlias)
public function getComponentAlias($sqlTableAlias)
{
if ( ! isset($this->_tableAliases[$tableAlias])) {
throw new Doctrine_Hydrate_Exception('Unknown table alias ' . $tableAlias);
if ( ! isset($this->_tableAliasMap[$sqlTableAlias])) {
throw new Doctrine_Query_Exception('Unknown table alias ' . $sqlTableAlias);
}
return $this->_tableAliases[$tableAlias];
return $this->_tableAliasMap[$sqlTableAlias];
}
/**
......@@ -700,12 +862,12 @@ abstract class Doctrine_Query_Abstract
* @param array $params
* @return void
*/
public function _execute($params)
protected function _execute($params)
{
$params = $this->_conn->convertBooleans($params);
if ( ! $this->_view) {
$query = $this->getQuery($params);
$query = $this->getSqlQuery($params);
} else {
$query = $this->_view->getSelectSql();
}
......@@ -713,12 +875,11 @@ abstract class Doctrine_Query_Abstract
$params = $this->convertEnums($params);
if ($this->isLimitSubqueryUsed() &&
$this->_conn->getAttribute(Doctrine::ATTR_DRIVER_NAME) !== 'mysql') {
$this->_conn->getAttribute(Doctrine::ATTR_DRIVER_NAME) !== 'mysql') {
$params = array_merge($params, $params);
}
if ($this->type !== self::SELECT) {
if ($this->_type !== self::SELECT) {
return $this->_conn->exec($query, $params);
}
//echo $query . "<br /><br />";
......@@ -739,32 +900,32 @@ abstract class Doctrine_Query_Abstract
$this->_params['where'],
$this->_params['having'],
$params);
if ($this->_cache) {
$cacheDriver = $this->getCacheDriver();
if ($this->_resultCache) {
$cacheDriver = $this->getResultCacheDriver();
$dql = $this->getDql();
$dql = $this->getDql();
// calculate hash for dql query
$hash = md5($dql . var_export($params, true));
$cached = ($this->_expireCache) ? false : $cacheDriver->fetch($hash);
$cached = ($this->_expireResultCache) ? false : $cacheDriver->fetch($hash);
if ($cached === false) {
// cache miss
$stmt = $this->_execute($params);
$this->_hydrator->setQueryComponents($this->_aliasMap);
$array = $this->_hydrator->hydrateResultSet($stmt, $this->_tableAliases,
$this->_hydrator->setQueryComponents($this->_queryComponents);
$array = $this->_hydrator->hydrateResultSet($stmt, $this->_tableAliasMap,
Doctrine::HYDRATE_ARRAY);
$cached = $this->getCachedForm($array);
$cacheDriver->save($hash, $cached, $this->_timeToLive);
$cacheDriver->save($hash, $cached, $this->_resultCacheTTL);
} else {
$cached = unserialize($cached);
$this->_tableAliases = $cached[2];
$this->_tableAliasMap = $cached[2];
$array = $cached[0];
$map = array();
$map = array();
foreach ($cached[1] as $k => $v) {
$e = explode('.', $v[0]);
if (count($e) === 1) {
......@@ -778,7 +939,7 @@ abstract class Doctrine_Query_Abstract
$map[$k]['agg'] = $v[1];
}
}
$this->_aliasMap = $map;
$this->_queryComponents = $map;
}
} else {
$stmt = $this->_execute($params);
......@@ -787,8 +948,8 @@ abstract class Doctrine_Query_Abstract
return $stmt;
}
$this->_hydrator->setQueryComponents($this->_aliasMap);
$array = $this->_hydrator->hydrateResultSet($stmt, $this->_tableAliases, $hydrationMode);
$this->_hydrator->setQueryComponents($this->_queryComponents);
$array = $this->_hydrator->hydrateResultSet($stmt, $this->_tableAliasMap, $hydrationMode);
}
return $array;
}
......@@ -815,7 +976,7 @@ abstract class Doctrine_Query_Abstract
}
}
return serialize(array($resultSet, $map, $this->getTableAliases()));
return serialize(array($resultSet, $map, $this->getTableAliasMap()));
}
/**
......@@ -827,7 +988,7 @@ abstract class Doctrine_Query_Abstract
*/
public function addSelect($select)
{
return $this->parseQueryPart('select', $select, true);
return $this->_addDqlQueryPart('select', $select, true);
}
/**
......@@ -837,11 +998,24 @@ abstract class Doctrine_Query_Abstract
* @param string $componentAlias the alias for the query component associated with given tableAlias
* @param string $tableAlias the table alias to be added
* @return Doctrine_Hydrate
* @deprecated
*/
public function addTableAlias($tableAlias, $componentAlias)
{
$this->_tableAliases[$tableAlias] = $componentAlias;
return $this->addSqlTableAlias($tableAlias, $componentAlias);
}
/**
* addSqlTableAlias
* adds an SQL table alias and associates it a component alias
*
* @param string $componentAlias the alias for the query component associated with given tableAlias
* @param string $tableAlias the table alias to be added
* @return Doctrine_Query_Abstract
*/
public function addSqlTableAlias($sqlTableAlias, $componentAlias)
{
$this->_tableAliasMap[$sqlTableAlias] = $componentAlias;
return $this;
}
......@@ -854,7 +1028,7 @@ abstract class Doctrine_Query_Abstract
*/
public function addFrom($from)
{
return $this->parseQueryPart('from', $from, true);
return $this->_addDqlQueryPart('from', $from, true);
}
/**
......@@ -872,7 +1046,7 @@ abstract class Doctrine_Query_Abstract
} else {
$this->_params['where'][] = $params;
}
return $this->parseQueryPart('where', $where, true);
return $this->_addDqlQueryPart('where', $where, true);
}
/**
......@@ -902,7 +1076,7 @@ abstract class Doctrine_Query_Abstract
$where = $expr . ($not === true ? ' NOT ':'') . ' IN (' . implode(', ', $a) . ')';
return $this->parseQueryPart('where', $where, true);
return $this->_addDqlQueryPart('where', $where, true);
}
/**
......@@ -913,7 +1087,6 @@ abstract class Doctrine_Query_Abstract
* @param mixed $params an array of parameters or a simple scalar
* @return Doctrine_Query
*/
public function whereNotIn($expr, $params = array())
{
return $this->whereIn($expr, $params, true);
......@@ -928,7 +1101,7 @@ abstract class Doctrine_Query_Abstract
*/
public function addGroupBy($groupby)
{
return $this->parseQueryPart('groupby', $groupby, true);
return $this->_addDqlQueryPart('groupby', $groupby, true);
}
/**
......@@ -946,7 +1119,7 @@ abstract class Doctrine_Query_Abstract
} else {
$this->_params['having'][] = $params;
}
return $this->parseQueryPart('having', $having, true);
return $this->_addDqlQueryPart('having', $having, true);
}
/**
......@@ -958,7 +1131,7 @@ abstract class Doctrine_Query_Abstract
*/
public function addOrderBy($orderby)
{
return $this->parseQueryPart('orderby', $orderby, true);
return $this->_addDqlQueryPart('orderby', $orderby, true);
}
/**
......@@ -970,7 +1143,7 @@ abstract class Doctrine_Query_Abstract
*/
public function select($select)
{
return $this->parseQueryPart('select', $select);
return $this->_addDqlQueryPart('select', $select);
}
/**
......@@ -982,8 +1155,7 @@ abstract class Doctrine_Query_Abstract
*/
public function distinct($flag = true)
{
$this->parts['distinct'] = (bool) $flag;
$this->_sqlParts['distinct'] = (bool) $flag;
return $this;
}
......@@ -996,8 +1168,7 @@ abstract class Doctrine_Query_Abstract
*/
public function forUpdate($flag = true)
{
$this->parts[self::FOR_UPDATE] = (bool) $flag;
$this->_sqlParts[self::FOR_UPDATE] = (bool) $flag;
return $this;
}
......@@ -1009,8 +1180,7 @@ abstract class Doctrine_Query_Abstract
*/
public function delete()
{
$this->type = self::DELETE;
$this->_type = self::DELETE;
return $this;
}
......@@ -1023,9 +1193,8 @@ abstract class Doctrine_Query_Abstract
*/
public function update($update)
{
$this->type = self::UPDATE;
return $this->parseQueryPart('from', $update);
$this->_type = self::UPDATE;
return $this->_addDqlQueryPart('from', $update);
}
/**
......@@ -1050,7 +1219,7 @@ abstract class Doctrine_Query_Abstract
$this->_params['set'][] = $params;
}
}
return $this->parseQueryPart('set', $key . ' = ' . $value, true);
return $this->_addDqlQueryPart('set', $key . ' = ' . $value, true);
}
}
......@@ -1063,7 +1232,7 @@ abstract class Doctrine_Query_Abstract
*/
public function from($from)
{
return $this->parseQueryPart('from', $from);
return $this->_addDqlQueryPart('from', $from);
}
/**
......@@ -1075,7 +1244,7 @@ abstract class Doctrine_Query_Abstract
*/
public function innerJoin($join)
{
return $this->parseQueryPart('from', 'INNER JOIN ' . $join, true);
return $this->_addDqlQueryPart('from', 'INNER JOIN ' . $join, true);
}
/**
......@@ -1087,7 +1256,7 @@ abstract class Doctrine_Query_Abstract
*/
public function leftJoin($join)
{
return $this->parseQueryPart('from', 'LEFT JOIN ' . $join, true);
return $this->_addDqlQueryPart('from', 'LEFT JOIN ' . $join, true);
}
/**
......@@ -1099,7 +1268,7 @@ abstract class Doctrine_Query_Abstract
*/
public function groupBy($groupby)
{
return $this->parseQueryPart('groupby', $groupby);
return $this->_addDqlQueryPart('groupby', $groupby);
}
/**
......@@ -1119,7 +1288,7 @@ abstract class Doctrine_Query_Abstract
$this->_params['where'][] = $params;
}
return $this->parseQueryPart('where', $where);
return $this->_addDqlQueryPart('where', $where);
}
/**
......@@ -1139,7 +1308,7 @@ abstract class Doctrine_Query_Abstract
$this->_params['having'][] = $params;
}
return $this->parseQueryPart('having', $having);
return $this->_addDqlQueryPart('having', $having);
}
/**
......@@ -1151,7 +1320,7 @@ abstract class Doctrine_Query_Abstract
*/
public function orderBy($orderby)
{
return $this->parseQueryPart('orderby', $orderby);
return $this->_addDqlQueryPart('orderby', $orderby);
}
/**
......@@ -1163,7 +1332,7 @@ abstract class Doctrine_Query_Abstract
*/
public function limit($limit)
{
return $this->parseQueryPart('limit', $limit);
return $this->_addDqlQueryPart('limit', $limit);
}
/**
......@@ -1175,18 +1344,18 @@ abstract class Doctrine_Query_Abstract
*/
public function offset($offset)
{
return $this->parseQueryPart('offset', $offset);
return $this->_addDqlQueryPart('offset', $offset);
}
/**
* getSql
* return the sql associated with this object
* shortcut for {@link getSqlQuery()}.
*
* @return string sql query string
*/
public function getSql()
{
return $this->getQuery();
return $this->getSqlQuery();
}
/**
......@@ -1197,7 +1366,7 @@ abstract class Doctrine_Query_Abstract
*/
protected function clear()
{
$this->parts = array(
$this->_sqlParts = array(
'select' => array(),
'distinct' => false,
'forUpdate' => false,
......@@ -1211,7 +1380,6 @@ abstract class Doctrine_Query_Abstract
'limit' => false,
'offset' => false,
);
$this->inheritanceApplied = false;
}
public function setHydrationMode($hydrationMode)
......@@ -1220,38 +1388,60 @@ abstract class Doctrine_Query_Abstract
return $this;
}
/**
* @deprecated
*/
public function getAliasMap()
{
return $this->_aliasMap;
return $this->_queryComponents;
}
/**
* Gets the components of this query.
*/
public function getQueryComponents()
{
return $this->_queryComponents;
}
/**
* Return the SQL parts.
*
* @return array The parts
* @deprecated
*/
public function getParts()
{
return $this->parts;
return $this->getSqlParts();
}
/**
* Return the SQL parts.
*
* @return array The parts
*/
public function getSqlParts()
{
return $this->_sqlParts;
}
/**
* getType
*
* returns the type of this query object
* by default the type is Doctrine_Hydrate::SELECT but if update() or delete()
* are being called the type is Doctrine_Hydrate::UPDATE and Doctrine_Hydrate::DELETE,
* by default the type is Doctrine_Query_Abstract::SELECT but if update() or delete()
* are being called the type is Doctrine_Query_Abstract::UPDATE and Doctrine_Query_Abstract::DELETE,
* respectively
*
* @see Doctrine_Hydrate::SELECT
* @see Doctrine_Hydrate::UPDATE
* @see Doctrine_Hydrate::DELETE
* @see Doctrine_Query_Abstract::SELECT
* @see Doctrine_Query_Abstract::UPDATE
* @see Doctrine_Query_Abstract::DELETE
*
* @return integer return the query type
*/
public function getType()
{
return $this->type;
return $this->_type;
}
/**
......@@ -1260,16 +1450,41 @@ abstract class Doctrine_Query_Abstract
* @param Doctrine_Cache_Interface|bool $driver cache driver
* @param integer $timeToLive how long the cache entry is valid
* @return Doctrine_Hydrate this object
* @deprecated Use useResultCache()
*/
public function useCache($driver = true, $timeToLive = null)
{
return $this->useResultCache($driver, $timeToLive);
}
/**
* useResultCache
*
* @param Doctrine_Cache_Interface|bool $driver cache driver
* @param integer $timeToLive how long the cache entry is valid
* @return Doctrine_Hydrate this object
*/
public function useResultCache($driver = true, $timeToLive = null)
{
if($driver !== null && $driver !== true && ! ($driver instanceOf Doctrine_Cache_Interface)){
$msg = 'First argument should be instance of Doctrine_Cache_Interface or null.';
throw new Doctrine_Hydrate_Exception($msg);
throw new Doctrine_Query_Exception($msg);
}
$this->_cache = $driver;
$this->_resultCache = $driver;
return $this->setCacheLifeSpan($timeToLive);
return $this->setResultCacheLifeSpan($timeToLive);
}
/**
* useQueryCache
*
* @param Doctrine_Cache_Interface|bool $driver cache driver
* @param integer $timeToLive how long the cache entry is valid
* @return Doctrine_Hydrate this object
*/
public function useQueryCache($driver = null, $timeToLive = null)
{
throw new Doctrine_Query_Exception("Not yet implemented.");
}
/**
......@@ -1277,11 +1492,34 @@ abstract class Doctrine_Query_Abstract
*
* @param boolean $expire whether or not to force cache expiration
* @return Doctrine_Hydrate this object
* @deprecated Use expireResultCache()
*/
public function expireCache($expire = true)
{
$this->_expireCache = true;
return $this->expireResultCache($expire);
}
/**
* expireCache
*
* @param boolean $expire whether or not to force cache expiration
* @return Doctrine_Hydrate this object
*/
public function expireResultCache($expire = true)
{
$this->_expireResultCache = true;
return $this;
}
/**
* expireQueryCache
*
* @param boolean $expire whether or not to force cache expiration
* @return Doctrine_Hydrate this object
*/
public function expireQueryCache($expire = true)
{
$this->_expireQueryCache = true;
return $this;
}
......@@ -1290,13 +1528,41 @@ abstract class Doctrine_Query_Abstract
*
* @param integer $timeToLive how long the cache entry is valid
* @return Doctrine_Hydrate this object
* @deprecated Use setResultCacheLifeSpan()
*/
public function setCacheLifeSpan($timeToLive)
{
return $this->setResultCacheLifeSpan($timeToLive);
}
/**
* setResultCacheLifeSpan
*
* @param integer $timeToLive how long the cache entry is valid
* @return Doctrine_Hydrate this object
*/
public function setResultCacheLifeSpan($timeToLive)
{
if ($timeToLive !== null) {
$timeToLive = (int) $timeToLive;
}
$this->_timeToLive = $timeToLive;
$this->_resultCacheTTL = $timeToLive;
return $this;
}
/**
* setQueryCacheLifeSpan
*
* @param integer $timeToLive how long the cache entry is valid
* @return Doctrine_Hydrate this object
*/
public function setQueryCacheLifeSpan($timeToLive)
{
if ($timeToLive !== null) {
$timeToLive = (int) $timeToLive;
}
$this->_queryCacheTTL = $timeToLive;
return $this;
}
......@@ -1306,13 +1572,40 @@ abstract class Doctrine_Query_Abstract
* returns the cache driver associated with this object
*
* @return Doctrine_Cache_Interface|boolean|null cache driver
* @deprecated Use getResultCacheDriver()
*/
public function getCacheDriver()
{
if ($this->_cache instanceof Doctrine_Cache_Interface) {
return $this->_cache;
return $this->getResultCacheDriver();
}
/**
* getResultCacheDriver
* returns the cache driver used for caching result sets
*
* @return Doctrine_Cache_Interface|boolean|null cache driver
*/
public function getResultCacheDriver()
{
if ($this->_resultCache instanceof Doctrine_Cache_Interface) {
return $this->_resultCache;
} else {
return $this->_conn->getCacheDriver();
return $this->_conn->getResultCacheDriver();
}
}
/**
* getQueryCacheDriver
* returns the cache driver used for caching queries
*
* @return Doctrine_Cache_Interface|boolean|null cache driver
*/
public function getQueryCacheDriver()
{
if ($this->_queryCache instanceof Doctrine_Cache_Interface) {
return $this->_queryCache;
} else {
return $this->_conn->getQueryCacheDriver();
}
}
......@@ -1325,23 +1618,113 @@ abstract class Doctrine_Query_Abstract
{
return $this->_conn;
}
/**
* Adds a DQL part to the internal parts collection.
*
* @param string $queryPartName The name of the query part.
* @param string $queryPart The actual query part to add.
* @param boolean $append Whether to append $queryPart to already existing
* parts under the same $queryPartName. Defaults to FALSE
* (previously added parts with the same name get overridden).
*/
protected function _addDqlQueryPart($queryPartName, $queryPart, $append = false)
{
if ($append) {
$this->_dqlParts[$queryPartName][] = $queryPart;
} else {
$this->_dqlParts[$queryPartName] = array($queryPart);
}
$this->_state = Doctrine_Query::STATE_DIRTY;
return $this;
}
/**
* parseQueryPart
* parses given DQL query part
* _processDqlQueryPart
* parses given query part
*
* @param string $queryPartName the name of the query part
* @param string $queryPart query part to be parsed
* @param boolean $append whether or not to append the query part to its stack
* if false is given, this method will overwrite
* the given query part stack with $queryPart
* @param array $queryParts an array containing the query part data
* @return Doctrine_Query this object
* @todo Better description. "parses given query part" ??? Then wheres the difference
* between process/parseQueryPart? I suppose this does something different.
*/
abstract public function parseQueryPart($queryPartName, $queryPart, $append = false);
protected function _processDqlQueryPart($queryPartName, $queryParts)
{
$this->removeSqlQueryPart($queryPartName);
if (is_array($queryParts) && ! empty($queryParts)) {
foreach ($queryParts as $queryPart) {
$parser = $this->_getParser($queryPartName);
$sql = $parser->parse($queryPart);
if (isset($sql)) {
if ($queryPartName == 'limit' || $queryPartName == 'offset') {
$this->setSqlQueryPart($queryPartName, $sql);
} else {
$this->addSqlQueryPart($queryPartName, $sql);
}
}
}
}
}
/**
* _getParser
* parser lazy-loader
*
* @throws Doctrine_Query_Exception if unknown parser name given
* @return Doctrine_Query_Part
* @todo Doc/Description: What is the parameter for? Which parsers are available?
*/
protected function _getParser($name)
{
if ( ! isset($this->_parsers[$name])) {
$class = 'Doctrine_Query_' . ucwords(strtolower($name));
Doctrine::autoload($class);
if ( ! class_exists($class)) {
throw new Doctrine_Query_Exception('Unknown parser ' . $name);
}
$this->_parsers[$name] = new $class($this);
}
return $this->_parsers[$name];
}
/**
* Gets the SQL query that corresponds to this query object.
* The returned SQL syntax depends on the connection driver that is used
* by this query object at the time of this method call.
*
* @param array $params
*/
abstract public function getSqlQuery($params = array());
/**
* parseDqlQuery
* parses a dql query
*
* @param string $query query to be parsed
* @return Doctrine_Query_Abstract this object
*/
abstract public function getQuery($params = array());
abstract public function parseDqlQuery($query);
/**
* @deprecated
*/
public function parseQuery($query)
{
return $this->parseDqlQuery($query);
}
/**
* @deprecated
*/
public function getQuery($params = array())
{
return $this->getSqlQuery($params);
}
}
......@@ -34,13 +34,13 @@ class Doctrine_Query_From extends Doctrine_Query_Part
{
/**
* DQL FROM PARSER
* parses the from part of the query string
* parses the FROM part of the query string
*
* @param string $str
* @return void
*/
public function parse($str)
{
{
$str = trim($str);
$parts = Doctrine_Tokenizer::bracketExplode($str, 'JOIN');
......@@ -54,7 +54,6 @@ class Doctrine_Query_From extends Doctrine_Query_Part
break;
}
$last = '';
foreach ($parts as $k => $part) {
......@@ -64,7 +63,7 @@ class Doctrine_Query_From extends Doctrine_Query_Part
continue;
}
$e = explode(' ', $part);
$e = explode(' ', $part);
if (end($e) == 'INNER' || end($e) == 'LEFT') {
$last = array_pop($e);
......@@ -79,7 +78,7 @@ class Doctrine_Query_From extends Doctrine_Query_Part
if ($operator) {
$e[0] = array_shift($e2) . $operator . implode('.', $e2);
}
$table = $this->query->load(implode(' ', $e));
}
......
<?php
/*
* $Id: Query.php 1296 2007-04-26 17:42:03Z zYne $
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
......@@ -27,8 +27,9 @@
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.com
* @since 1.0
* @version $Revision: 1296 $
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @version $Revision$
*/
class Doctrine_Query_Parser
{ }
\ No newline at end of file
{
}
\ No newline at end of file
......@@ -22,6 +22,12 @@ Doctrine::autoload('Doctrine_Query_Abstract');
/**
* Doctrine_RawSql
*
* Doctrine_RawSql is an implementation of Doctrine_Query_Abstract that skips the entire
* DQL parsing procedure. The "DQL" that is passed to a RawSql query object for execution
* is considered to be plain SQL and will be used "as is". The only query part that is special
* in a RawSql query is the SELECT part, which has a special syntax that provides Doctrine
* with the necessary information to properly hydrate the query results.
*
* @package Doctrine
* @subpackage RawSql
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
......@@ -36,59 +42,84 @@ class Doctrine_RawSql extends Doctrine_Query_Abstract
* @var array $fields
*/
private $fields = array();
/**
* @deprecated
*/
public function parseQueryPart($queryPartName, $queryPart, $append = false)
{
return $this->parseDqlQueryPart($queryPartName, $queryPart, $append);
}
/**
* parseQueryPart
* parses given query part
* parseDqlQueryPart
* parses given DQL query part. Overrides Doctrine_Query_Abstract::parseDqlQueryPart().
* This implementation does no parsing at all, except of the SELECT portion of the query
* which is special in RawSql queries. The entire remaining parts are used "as is", so
* the user of the RawSql query is responsible for writing SQL that is portable between
* different DBMS.
*
* @param string $queryPartName the name of the query part
* @param string $queryPart query part to be parsed
* @param boolean $append whether or not to append the query part to its stack
* if false is given, this method will overwrite
* if false is given, this method will overwrite
* the given query part stack with $queryPart
* @return Doctrine_Query this object
*/
public function parseQueryPart($queryPartName, $queryPart, $append = false)
public function parseDqlQueryPart($queryPartName, $queryPart, $append = false)
{
if ($queryPartName == 'select') {
$this->parseSelectFields($queryPart);
return $this;
}
if( ! isset($this->parts[$queryPartName])) {
$this->parts[$queryPartName] = array();
}
if (! $append) {
$this->parts[$queryPartName] = array($queryPart);
}else {
$this->parts[$queryPartName][] = $queryPart;
}
return $this;
$this->_parseSelectFields($queryPart);
return $this;
}
if ( ! isset($this->parts[$queryPartName])) {
$this->_sqlParts[$queryPartName] = array();
}
if ( ! $append) {
$this->_sqlParts[$queryPartName] = array($queryPart);
} else {
$this->_sqlParts[$queryPartName][] = $queryPart;
}
return $this;
}
/**
* Adds a DQL query part. Overrides Doctrine_Query_Abstract::_addDqlQueryPart().
* This implementation for RawSql parses the new parts right away, generating the SQL.
*/
protected function _addDqlQueryPart($queryPartName, $queryPart, $append = false)
{
return $this->parseQueryPart($queryPartName, $queryPart, $append);
}
/**
* Add select parts to fields
* Add select parts to fields.
*
* @param $queryPart sting The name of the querypart
*/
private function parseSelectFields($queryPart){
private function _parseSelectFields($queryPart){
preg_match_all('/{([^}{]*)}/U', $queryPart, $m);
$this->fields = $m[1];
$this->parts['select'] = array();
$this->_sqlParts['select'] = array();
}
/**
* parseQuery
* parses an sql query and adds the parts to internal array
* parseDqlQuery
* parses an sql query and adds the parts to internal array.
* Overrides Doctrine_Query_Abstract::parseDqlQuery().
* This implementation simply tokenizes the provided query string and uses them
* as SQL parts right away.
*
* @param string $query query to be parsed
* @return Doctrine_RawSql this object
* @param string $query query to be parsed
* @return Doctrine_RawSql this object
*/
public function parseQuery($query)
public function parseDqlQuery($query)
{
$this->parseSelectFields($query);
$this->_parseSelectFields($query);
$this->clear();
$tokens = Doctrine_Tokenizer::sqlExplode($query,' ');
$tokens = $this->_tokenizer->sqlExplode($query, ' ');
$parts = array();
foreach ($tokens as $key => $part) {
......@@ -131,20 +162,20 @@ class Doctrine_RawSql extends Doctrine_Query_Abstract
}
}
$this->parts = $parts;
$this->parts['select'] = array();
$this->_sqlParts = $parts;
$this->_sqlParts['select'] = array();
return $this;
}
/**
* getQuery
* builds the sql query from the given query parts
* getSqlQuery
* builds the sql query.
*
* @return string the built sql query
*/
public function getQuery($params = array())
{
public function getSqlQuery($params = array())
{
$select = array();
foreach ($this->fields as $field) {
......@@ -153,10 +184,10 @@ class Doctrine_RawSql extends Doctrine_Query_Abstract
throw new Doctrine_RawSql_Exception('All selected fields in Sql query must be in format tableAlias.fieldName');
}
// try to auto-add component
if ( ! $this->hasTableAlias($e[0])) {
if ( ! $this->hasSqlTableAlias($e[0])) {
try {
$this->addComponent($e[0], ucwords($e[0]));
} catch(Doctrine_Exception $exception) {
} catch (Doctrine_Exception $exception) {
throw new Doctrine_RawSql_Exception('The associated component for table alias ' . $e[0] . ' couldn\'t be found.');
}
}
......@@ -164,7 +195,7 @@ class Doctrine_RawSql extends Doctrine_Query_Abstract
$componentAlias = $this->getComponentAlias($e[0]);
if ($e[1] == '*') {
foreach ($this->_aliasMap[$componentAlias]['table']->getColumnNames() as $name) {
foreach ($this->_queryComponents[$componentAlias]['table']->getColumnNames() as $name) {
$field = $e[0] . '.' . $name;
$select[$componentAlias][$field] = $field . ' AS ' . $e[0] . '__' . $name;
......@@ -177,21 +208,21 @@ class Doctrine_RawSql extends Doctrine_Query_Abstract
// force-add all primary key fields
foreach ($this->getTableAliases() as $tableAlias => $componentAlias) {
$map = $this->_aliasMap[$componentAlias];
foreach ($this->getTableAliasMap() as $tableAlias => $componentAlias) {
$map = $this->_queryComponents[$componentAlias];
foreach ((array) $map['table']->getIdentifier() as $key) {
$field = $tableAlias . '.' . $key;
if ( ! isset($this->parts['select'][$field])) {
if ( ! isset($this->_sqlParts['select'][$field])) {
$select[$componentAlias][$field] = $field . ' AS ' . $tableAlias . '__' . $key;
}
}
}
// first add the fields of the root component
reset($this->_aliasMap);
$componentAlias = key($this->_aliasMap);
reset($this->_queryComponents);
$componentAlias = key($this->_queryComponents);
$q = 'SELECT ' . implode(', ', $select[$componentAlias]);
unset($select[$componentAlias]);
......@@ -204,21 +235,21 @@ class Doctrine_RawSql extends Doctrine_Query_Abstract
$string = $this->applyInheritance();
if ( ! empty($string)) {
$this->parts['where'][] = $string;
$this->_sqlParts['where'][] = $string;
}
$copy = $this->parts;
$copy = $this->_sqlParts;
unset($copy['select']);
$q .= ( ! empty($this->parts['from']))? ' FROM ' . implode(' ', $this->parts['from']) : '';
$q .= ( ! empty($this->parts['where']))? ' WHERE ' . implode(' AND ', $this->parts['where']) : '';
$q .= ( ! empty($this->parts['groupby']))? ' GROUP BY ' . implode(', ', $this->parts['groupby']) : '';
$q .= ( ! empty($this->parts['having']))? ' HAVING ' . implode(' AND ', $this->parts['having']) : '';
$q .= ( ! empty($this->parts['orderby']))? ' ORDER BY ' . implode(', ', $this->parts['orderby']) : '';
$q .= ( ! empty($this->parts['limit']))? ' LIMIT ' . implode(' ', $this->parts['limit']) : '';
$q .= ( ! empty($this->parts['offset']))? ' OFFSET ' . implode(' ', $this->parts['offset']) : '';
$q .= ( ! empty($this->_sqlParts['from']))? ' FROM ' . implode(' ', $this->_sqlParts['from']) : '';
$q .= ( ! empty($this->_sqlParts['where']))? ' WHERE ' . implode(' AND ', $this->_sqlParts['where']) : '';
$q .= ( ! empty($this->_sqlParts['groupby']))? ' GROUP BY ' . implode(', ', $this->_sqlParts['groupby']) : '';
$q .= ( ! empty($this->_sqlParts['having']))? ' HAVING ' . implode(' AND ', $this->_sqlParts['having']) : '';
$q .= ( ! empty($this->_sqlParts['orderby']))? ' ORDER BY ' . implode(', ', $this->_sqlParts['orderby']) : '';
$q .= ( ! empty($this->_sqlParts['limit']))? ' LIMIT ' . implode(' ', $this->_sqlParts['limit']) : '';
$q .= ( ! empty($this->_sqlParts['offset']))? ' OFFSET ' . implode(' ', $this->_sqlParts['offset']) : '';
if ( ! empty($string)) {
array_pop($this->parts['where']);
array_pop($this->_sqlParts['where']);
}
return $q;
}
......@@ -255,8 +286,8 @@ class Doctrine_RawSql extends Doctrine_Query_Abstract
$currPath = '';
if (isset($this->_aliasMap[$e[0]])) {
$table = $this->_aliasMap[$e[0]]['table'];
if (isset($this->_queryComponents[$e[0]])) {
$table = $this->_queryComponents[$e[0]]['table'];
$currPath = $parent = array_shift($e);
}
......@@ -281,15 +312,15 @@ class Doctrine_RawSql extends Doctrine_Query_Abstract
->getConnectionForComponent($component);
$table = $conn->getTable($component);
$this->_aliasMap[$componentAlias] = array('table' => $table);
$this->_queryComponents[$componentAlias] = array('table' => $table);
} else {
$relation = $table->getRelation($component);
$this->_aliasMap[$componentAlias] = array('table' => $relation->getTable(),
$this->_queryComponents[$componentAlias] = array('table' => $relation->getTable(),
'parent' => $parent,
'relation' => $relation);
}
$this->addTableAlias($tableAlias, $componentAlias);
$this->addSqlTableAlias($tableAlias, $componentAlias);
$parent = $currPath;
}
......
......@@ -55,7 +55,9 @@ class Doctrine_Relation_Association extends Doctrine_Relation
*/
public function getRelationDql($count, $context = 'record')
{
$table = $this->definition['refTable'];
$component = $this->definition['refTable']->getComponentName();
switch ($context) {
case "record":
$sub = substr(str_repeat("?, ", $count),0,-2);
......@@ -88,7 +90,7 @@ class Doctrine_Relation_Association extends Doctrine_Relation
if (empty($id) || ! $this->definition['table']->getAttribute(Doctrine::ATTR_LOAD_REFERENCES)) {
$coll = new Doctrine_Collection($this->getTable());
} else {
$coll = Doctrine_Query::create()->parseQuery($this->getRelationDql(1))->execute(array($id));
$coll = Doctrine_Query::create()->query($this->getRelationDql(1), array($id));
}
$coll->setReference($record, $this);
return $coll;
......
......@@ -109,7 +109,7 @@ class Doctrine_Tokenizer
*
* parameters:
* $str = email LIKE 'John@example.com'
* $d = ' AND '
* $d = ' LIKE '
*
* would return an array:
* array("email", "LIKE", "'John@example.com'")
......
......@@ -47,16 +47,6 @@ class Doctrine_DataType_Enum_TestCase extends Doctrine_UnitTestCase
$this->assertEqual($test->status, 'open');
$test->save();
try {
$query = new Doctrine_Query($this->connection);
$ret = $query->parseQuery('FROM EnumTest WHERE EnumTest.status = ?')
->execute(array('open'));
$this->assertEqual(count($ret), 1);
} catch (Exception $e) {
$this->fail();
}
try {
$query = new Doctrine_Query($this->connection);
$ret = $query->query("FROM EnumTest WHERE EnumTest.status = 'open'");
......
......@@ -98,7 +98,7 @@ class Doctrine_Query_TestCase extends Doctrine_UnitTestCase
$q = new Doctrine_Query();
$q->from('User u')->leftJoin('u.Phonenumber p');
$q->getQuery();
//Doctrine::dump($q->getCachedForm(array('foo' => 'bar')));
$this->assertEqual($q->parseClause("CONCAT('u.name', u.name)"), "CONCAT('u.name', e.name)");
}
}
......@@ -106,8 +106,6 @@ class MyQuery extends Doctrine_Query
{
public function preQuery()
{
if ($this->getRoot()->getComponentName() == 'User') {
$this->where('u.id = 4');
}
$this->where('u.id = 4');
}
}
......@@ -94,8 +94,8 @@ class Doctrine_RawSql_TestCase extends Doctrine_UnitTestCase
$fields = $query->getFields();
$this->assertEqual($fields, array('entity.name', 'entity.id'));
$coll = $query->execute();
$coll = $query->execute();
$this->assertEqual($coll->count(), 11);
......@@ -125,6 +125,7 @@ class Doctrine_RawSql_TestCase extends Doctrine_UnitTestCase
$coll[5]->Phonenumber[0]->phonenumber;
$this->assertEqual($count, $this->conn->count());
}
public function testAliasesAreSupportedInAddComponent()
{
$query = new Doctrine_RawSql();
......@@ -161,12 +162,14 @@ class Doctrine_RawSql_TestCase extends Doctrine_UnitTestCase
$this->assertTrue(is_numeric($coll[3]->id));
$this->assertTrue(is_numeric($coll[7]->id));
}
public function testConvenienceMethods()
{
$query = new Doctrine_RawSql($this->connection);
$query->select('{entity.name}')->from('entity');
$query->addComponent('entity', 'User');
$coll = $query->execute();
$this->assertEqual($coll->count(), 8);
......@@ -247,7 +250,7 @@ class Doctrine_RawSql_TestCase extends Doctrine_UnitTestCase
$this->assertEqual(count($coll), 3);
}
public function testParseQueryPartShouldAddPartIfNotSelectAndAppend()
{
$query = new Doctrine_Rawsql();
......@@ -258,7 +261,7 @@ class Doctrine_RawSql_TestCase extends Doctrine_UnitTestCase
$this->assertTrue(isset($parts["test"][0]));
$this->assertEqual("test", $parts["test"][0]);
}
public function testParseQueryShouldExtractGroupBy()
{
$query = new Doctrine_RawSql();
......
......@@ -145,7 +145,7 @@ $test->addTestCase($data_types);
// Utility components
$plugins = new GroupTest('Plugin tests: View, Validator, Hook','plugins');
//$utility->addTestCase(new Doctrine_PessimisticLocking_TestCase());
$plugins->addTestCase(new Doctrine_Plugin_TestCase());
//$plugins->addTestCase(new Doctrine_Plugin_TestCase());
$plugins->addTestCase(new Doctrine_View_TestCase());
$plugins->addTestCase(new Doctrine_AuditLog_TestCase());
$plugins->addTestCase(new Doctrine_Validator_TestCase());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment