1
0
mirror of synced 2024-12-13 06:46:03 +03:00

[2.0] Parser work. Added double-dispatch functionality to AST node classes for use in the SqlWalker to reduce big if/else instanceof checks and for better maintainability. Also its less error-prone in the SqlWalker because its harder to miss a conditional case. Added new extensible DQL function implementation.

This commit is contained in:
romanb 2009-03-23 17:39:33 +00:00
parent 618c1281e4
commit ae5d212271
57 changed files with 957 additions and 301 deletions

View File

@ -38,4 +38,9 @@ class AggregateExpression extends Node
{
return $this->_functionName;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkAggregateExpression($this);
}
}

View File

@ -51,4 +51,9 @@ class ArithmeticExpression extends Node
{
return (bool) $this->_subselect;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkArithmeticExpression($this);
}
}

View File

@ -38,4 +38,9 @@ class ArithmeticFactor extends Node
{
return $this->_nSigned;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkArithmeticFactor($this);
}
}

View File

@ -24,4 +24,9 @@ class ArithmeticTerm extends Node
{
return $this->_factors;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkArithmeticTerm($this);
}
}

View File

@ -49,5 +49,10 @@ class BetweenExpression extends Node
{
return $this->_not;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkBetweenExpression($this);
}
}

View File

@ -1,7 +1,22 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Query\AST;
@ -43,4 +58,9 @@ class ComparisonExpression extends Node
{
return $this->_operator;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkComparisonExpression($this);
}
}

View File

@ -35,4 +35,8 @@ namespace Doctrine\ORM\Query\AST;
*/
class ComparisonOperator extends Node
{
public function dispatch($sqlWalker)
{
;
}
}

View File

@ -24,4 +24,9 @@ class ConditionalExpression extends Node
{
return $this->_conditionalTerms;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkConditionalExpression($this);
}
}

View File

@ -31,4 +31,9 @@ class ConditionalFactor extends Node
{
return $this->_conditionalPrimary;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkConditionalFactor($this);
}
}

View File

@ -45,4 +45,9 @@ class ConditionalPrimary extends Node
{
return (bool) $this->_conditionalExpression;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkConditionalPrimary($this);
}
}

View File

@ -24,4 +24,9 @@ class ConditionalTerm extends Node
{
return $this->_conditionalFactors;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkConditionalTerm($this);
}
}

View File

@ -48,5 +48,10 @@ class DeleteClause extends Node
{
$this->_aliasIdentificationVariable = $alias;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkDeleteClause($this);
}
}

View File

@ -54,4 +54,9 @@ class DeleteStatement extends Node
{
return $this->_whereClause;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkDeleteStatement($this);
}
}

View File

@ -26,9 +26,19 @@ class ExistsExpression extends Node
$this->_not = $bool;
}
public function getNot()
public function isNot()
{
return $this->_not;
}
public function getSubselect()
{
return $this->_subselect;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkExistsExpression($this);
}
}

View File

@ -44,4 +44,9 @@ class FromClause extends Node
{
return $this->_identificationVariableDeclarations;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkFromClause($this);
}
}

View File

@ -1,17 +0,0 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
namespace Doctrine\ORM\Query\AST;
/**
* Description of Function
*
* @author robo
*/
class FunctionNode extends Node
{
private $_name;
}

View File

@ -0,0 +1,59 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
namespace Doctrine\ORM\Query\AST\Functions;
/**
* "CONCAT" "(" StringPrimary "," StringPrimary ")"
*
* @author robo
*/
class ConcatFunction extends FunctionNode
{
private $_firstStringPrimary;
private $_secondStringPriamry;
public function getFirstStringPrimary()
{
return $this->_firstStringPrimary;
}
public function getSecondStringPrimary()
{
return $this->_secondStringPrimary;
}
/**
* @override
*/
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
//TODO: Use platform to get SQL
$sql = 'CONCAT(' .
$sqlWalker->walkStringPrimary($this->_firstStringPrimary)
. ', ' .
$sqlWalker->walkStringPrimary($this->_secondStringPrimary)
. ')';
return $sql;
}
/**
* @override
*/
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$lexer = $parser->getLexer();
$parser->match($lexer->lookahead['value']);
$parser->match('(');
$this->_firstStringPrimary = $parser->_StringPrimary();
$parser->match(',');
$this->_secondStringPrimary = $parser->_StringPrimary();
$parser->match(')');
}
}

View File

@ -0,0 +1,66 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
/**
* Description of Function
*
* @author robo
*/
abstract class FunctionNode extends Node
{
private $_name;
//private $_expressions = array();
public function __construct($name)
{
$this->_name = $name;
}
public function getName()
{
return $this->_name;
}
abstract public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker);
public function dispatch($sqlWalker)
{
return $sqlWalker->walkFunction($this);
}
//abstract public function parse(\Doctrine\ORM\Query\Parser $parser);
/*
public function getExpressions()
{
return $this->_expressions;
}
public function setExpressions(array $expressions)
{
$this->_expressions = $expressions;
}
*/
}

View File

@ -0,0 +1,44 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
namespace Doctrine\ORM\Query\AST\Functions;
/**
* "LOWER" "(" StringPrimary ")"
*
* @author robo
*/
class LowerFunction extends FunctionNode
{
private $_stringPrimary;
public function getStringPrimary()
{
return $this->_stringPrimary;
}
/**
* @override
*/
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
//TODO: Use platform to get SQL
return 'LOWER(' . $sqlWalker->walkStringPrimary($this->_stringPrimary) . ')';
}
/**
* @override
*/
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$lexer = $parser->getLexer();
$parser->match($lexer->lookahead['value']);
$parser->match('(');
$this->_stringPrimary = $parser->_StringPrimary();
$parser->match(')');
}
}

View File

@ -0,0 +1,69 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
namespace Doctrine\ORM\Query\AST\Functions;
/**
* "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")"
*
* @author robo
*/
class SubstringFunction extends FunctionNode
{
private $_stringPrimary;
private $_firstSimpleArithmeticExpression;
private $_secondSimpleArithmeticExpression;
public function geStringPrimary()
{
return $this->_stringPrimary;
}
public function getSecondSimpleArithmeticExpression()
{
return $this->_secondSimpleArithmeticExpression;
}
public function getFirstSimpleArithmeticExpression()
{
return $this->_firstSimpleArithmeticExpression;
}
/**
* @override
*/
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
//TODO: Use platform to get SQL
$sql = 'SUBSTRING(' .
$sqlWalker->walkStringPrimary($this->_stringPrimary)
. ', ' .
$sqlWalker->walkSimpleArithmeticExpression($this->_firstSimpleArithmeticExpression)
. ', ' .
$sqlWalker->walkSimpleArithmeticExpression($this->_secondSimpleArithmeticExpression)
. ')';
return $sql;
}
/**
* @override
*/
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$lexer = $parser->getLexer();
$parser->match($lexer->lookahead['value']);
$parser->match('(');
$this->_stringPrimary = $parser->_StringPrimary();
$parser->match(',');
$this->_firstSimpleArithmeticExpression = $parser->_SimpleArithmeticExpression();
$parser->match(',');
$this->_secondSimpleArithmeticExpression = $parser->_SimpleArithmeticExpression();
$parser->match(')');
}
}

View File

@ -0,0 +1,118 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\Lexer;
/**
* "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")"
*
* @author robo
*/
class TrimFunction extends FunctionNode
{
private $_leading;
private $_trailing;
private $_both;
private $_trimChar;
private $_stringPrimary;
public function getStringPrimary()
{
return $this->_stringPrimary;
}
public function isLeading()
{
return $this->_leading;
}
public function setLeading($bool)
{
$this->_leading = $bool;
}
public function isTrailing()
{
return $this->_trailing;
}
public function setTrailing($bool)
{
$this->_trailing = $bool;
}
public function isBoth()
{
return $this->_both;
}
public function setBoth($bool)
{
$this->_both = $bool;
}
public function getTrimChar()
{
return $this->_trimChar;
}
public function setTrimChar($trimChar)
{
$this->_trimChar = $trimChar;
}
/**
* @override
*/
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
$sql = 'TRIM(';
if ($this->_leading) $sql .= 'LEADING ';
else if ($this->_trailing) $sql .= 'TRAILING ';
else if ($this->_both) $sql .= 'BOTH ';
if ($this->_trimChar) $sql .= $this->_trimChar . ' '; //TODO: quote()
$sql .= 'FROM ' . $sqlWalker->walkStringPrimary($this->_stringPrimary);
$sql .= ')';
return $sql;
}
/**
* @override
*/
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$lexer = $parser->getLexer();
$parser->match($lexer->lookahead['value']);
$parser->match('(');
if (strcasecmp('leading', $lexer->lookahead['value']) === 0) {
$parser->match($lexer->lookahead['value']);
$this->_leading = true;
} else if (strcasecmp('trailing', $lexer->lookahead['value']) === 0) {
$parser->match($lexer->lookahead['value']);
$this->_trailing = true;
} else if (strcasecmp('both', $lexer->lookahead['value']) === 0) {
$parser->match($lexer->lookahead['value']);
$this->_both = true;
}
if ($lexer->isNextToken(Lexer::T_STRING)) {
$parser->match(Lexer::T_STRING);
$this->_trimChar = $lexer->token['value'];
}
if ($this->_leading || $this->_trailing || $this->_both || $this->_trimChar) {
$parser->match(Lexer::T_FROM);
}
$this->_stringPrimary = $parser->_StringPrimary();
$parser->match(')');
}
}

View File

@ -0,0 +1,44 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
namespace Doctrine\ORM\Query\AST\Functions;
/**
* "UPPER" "(" StringPrimary ")"
*
* @author robo
*/
class UpperFunction extends FunctionNode
{
private $_stringPrimary;
public function getStringPrimary()
{
return $this->_stringPrimary;
}
/**
* @override
*/
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
//TODO: Use platform to get SQL
return 'UPPER(' . $sqlWalker->walkStringPrimary($this->_stringPrimary) . ')';
}
/**
* @override
*/
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$lexer = $parser->getLexer();
$parser->match($lexer->lookahead['value']);
$parser->match('(');
$this->_stringPrimary = $parser->_StringPrimary();
$parser->match(')');
}
}

View File

@ -24,4 +24,9 @@ class GroupByClause extends Node
{
return $this->_groupByItems;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkGroupByClause($this);
}
}

View File

@ -24,4 +24,9 @@ class HavingClause extends Node
{
return $this->_conditionalExpression;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkHavingClause($this);
}
}

View File

@ -63,16 +63,8 @@ class IdentificationVariableDeclaration extends Node
return $this->_joinVariableDeclarations;
}
/* REMOVE ME LATER. COPIED METHODS FROM SPLIT OF PRODUCTION INTO "AST" AND "PARSER" */
public function buildSql()
public function dispatch($sqlWalker)
{
$str = $this->_rangeVariableDeclaration->buildSql();
for ($i = 0, $l = count($this->_joinVariableDeclarations); $i < $l; $i++) {
$str .= ' ' . $this->_joinVariableDeclarations[$i]->buildSql();
}
return $str;
return $sqlWalker->walkIdentificationVariableDeclaration($this);
}
}

View File

@ -57,5 +57,10 @@ class InExpression extends Node
{
return $this->_pathExpression;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkInExpression($this);
}
}

View File

@ -44,4 +44,9 @@ class IndexBy extends Node
{
return $this->_simpleStateFieldPathExpression;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkIndexBy($this);
}
}

View File

@ -51,4 +51,9 @@ class InputParameter extends Node
{
return $this->_position;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkInputParameter($this);
}
}

View File

@ -89,97 +89,9 @@ class Join extends Node
{
return $this->_conditionalExpression;
}
/* REMOVE ME LATER. COPIED METHODS FROM SPLIT OF PRODUCTION INTO "AST" AND "PARSER" */
public function buildSql()
public function dispatch($sqlWalker)
{
$join = '';
switch ($this->_joinType) {
case self::JOIN_TYPE_LEFT:
$join .= 'LEFT';
break;
case self::JOIN_TYPE_LEFTOUTER:
$join .= 'LEFT OUTER';
break;
case self::JOIN_TYPE_INNER:
default:
$join .= 'INNER';
break;
}
$join .= ' JOIN ' . $this->_joinAssociationPathExpression->buildSql();
$condition = isset($this->_conditionalExpression)
? $this->_conditionalExpression->buildSql() : '';
switch ($this->whereType) {
case self::JOIN_WHERE_ON:
// Nothing to do here... =)
break;
case self::JOIN_WHERE_WITH:
default:
/*
TODO: Refactor to support split!!!
$parserResult = $this->_parser->getParserResult();
// Get the connection for the component
$conn = $this->_em->getConnection();
// We need to build the join conditions. Retrieving AssociationMapping
$queryComponent = $this->_rangeVariableDeclaration->getQueryComponent();
$association = $queryComponent['relation'];
$joinColumns = array();
if ($association->isOneToMany() || $association->isOneToOne()) {
if ($association->isInverseSide()) {
// joinColumns are found on the other (owning) side
$targetClass = $this->_em->getClassMetadata($association->getTargetEntityName());
$joinColumns = $targetClass->getAssociationMapping($association->getMappedByFieldName())
->getTargetToSourceKeyColumns();
} else {
$joinColumns = $association->getSourceToTargetKeyColumns();
}
} else {
//TODO: many-many
}
$relationConditionExpression = '';
// We have an array('localColumn' => 'foreignColumn', ...) here
foreach ($joinColumns as $localColumn => $foreignColumn) {
// leftExpression = rightExpression
// Defining leftExpression
$leftExpression = $conn->quoteIdentifier(
$parserResult->getTableAliasFromComponentAlias($queryComponent['parent']) . '.' . $localColumn
);
// Defining rightExpression
$rightExpression = $conn->quoteIdentifier(
$parserResult->getTableAliasFromComponentAlias(
$this->_rangeVariableDeclaration->getIdentificationVariable()
) . '.' . $foreignColumn
);
// Building the relation
$relationConditionExpression .= (($relationConditionExpression != '') ? ' AND ' : '')
. $leftExpression . ' = ' . $rightExpression;
}
$sql .= ' ON ' . $relationConditionExpression;
$sql .= empty($conditionExpression) ? '' : ' AND (' . $conditionExpression . ')';
*/
break;
}
return $join . ' ON ' . $condition;
return $sqlWalker->walkJoin($this);
}
}

View File

@ -32,4 +32,9 @@ class JoinPathExpression extends Node
{
return $this->_assocField;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkJoinPathExpression($this);
}
}

View File

@ -52,9 +52,8 @@ class JoinVariableDeclaration extends Node
return $this->_indexBy;
}
/* REMOVE ME LATER. COPIED METHODS FROM SPLIT OF PRODUCTION INTO "AST" AND "PARSER" */
public function buildSql()
public function dispatch($sqlWalker)
{
return $this->_join->buildSql() . (isset($this->_indexby) ? $this->_indexby->buildSql() . ' ' : '');
return $sqlWalker->walkJoinVariableDeclaration($this);
}
}

View File

@ -45,4 +45,9 @@ class LikeExpression extends Node
{
return $this->_escapeChar;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkLikeExpression($this);
}
}

View File

@ -16,7 +16,7 @@
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.org>.
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Query\AST;
@ -27,10 +27,11 @@ namespace Doctrine\ORM\Query\AST;
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Janne Vanhala <jpvanhal@cc.hut.fi>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://www.phpdoctrine.org
* @link http://www.doctrine-project.org
* @since 2.0
* @version $Revision$
*/
abstract class Node
{
abstract public function dispatch($sqlWalker);
}

View File

@ -35,5 +35,10 @@ class NullComparisonExpression extends Node
{
return $this->_not;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkNullComparisonExpression($this);
}
}

View File

@ -24,4 +24,9 @@ class OrderByClause extends Node
{
return $this->_orderByItems;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkOrderByClause($this);
}
}

View File

@ -46,4 +46,9 @@ class OrderByItem extends Node
{
return $this->_desc;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkOrderByItem($this);
}
}

View File

@ -16,7 +16,7 @@
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.org>.
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Query\AST;
@ -26,7 +26,7 @@ namespace Doctrine\ORM\Query\AST;
*
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://www.phpdoctrine.org
* @link http://www.doctrine-project.org
* @since 2.0
* @version $Revision$
*/
@ -58,27 +58,9 @@ class RangeVariableDeclaration extends Node
{
return $this->_classMetadata;
}
/* REMOVE ME LATER. COPIED METHODS FROM SPLIT OF PRODUCTION INTO "AST" AND "PARSER" */
public function buildSql()
public function dispatch($sqlWalker)
{
// Retrieving connection
$conn = $this->_parserResult->getEntityManager()->getConnection();
// Component alias
$componentAlias = $this->_aliasIdentificationVariable;
// Retrieving required information
try {
$queryComponent = $this->_parserResult->getQueryComponent($componentAlias);
$classMetadata = $queryComponent['metadata'];
} catch (Doctrine_Exception $e) {
$this->_parser->semanticalError($e->getMessage());
return;
}
return $conn->quoteIdentifier($classMetadata->getTableName()) . ' '
. $conn->quoteIdentifier($this->_parserResult->getTableAliasFromComponentAlias($componentAlias));
return $sqlWalker->walkRangeVariableDeclaration($this);
}
}

View File

@ -69,4 +69,9 @@ class SelectClause extends Node
{
return is_object($value) ? $value->buildSql() : $value;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkSelectClause($this);
}
}

View File

@ -52,4 +52,9 @@ class SelectExpression extends Node
{
return $this->_fieldIdentificationVariable;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkSelectExpression($this);
}
}

View File

@ -80,14 +80,8 @@ class SelectStatement extends Node
return $this->_orderByClause;
}
/* REMOVE ME LATER. COPIED METHODS FROM SPLIT OF PRODUCTION INTO "AST" AND "PARSER" */
public function buildSql()
public function dispatch($sqlWalker)
{
return $this->_selectClause->buildSql() . ' ' . $this->_fromClause->buildSql()
. (($this->_whereClause !== null) ? ' ' . $this->_whereClause->buildSql() : '')
. (($this->_groupByClause !== null) ? ' ' . $this->_groupByClause->buildSql() : '')
. (($this->_havingClause !== null) ? ' ' . $this->_havingClause->buildSql() : '')
. (($this->_orderByClause !== null) ? ' ' . $this->_orderByClause->buildSql() : '');
return $sqlWalker->walkSelectStatement($this);
}
}

View File

@ -24,4 +24,9 @@ class SimpleArithmeticExpression extends Node
{
return $this->_terms;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkSimpleArithmeticExpression($this);
}
}

View File

@ -55,4 +55,9 @@ class SimpleSelectClause extends Node
{
return $this->_simpleSelectExpression;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkSimpleSelectClause($this);
}
}

View File

@ -55,4 +55,9 @@ class SimpleSelectExpression extends Node
{
$this->_fieldIdentificationVariable = $fieldAlias;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkSimpleSelectExpression($this);
}
}

View File

@ -51,4 +51,9 @@ class SimpleStateFieldPathExpression extends Node
{
return $this->_simpleStateField;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkSimpleStateFieldPathExpression($this);
}
}

View File

@ -95,4 +95,9 @@ class StateFieldPathExpression extends Node
{
$this->_collectionValuedAssociationFields[$part] = true;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkStateFieldPathExpression($this);
}
}

View File

@ -95,4 +95,9 @@ class Subselect extends Node
{
$this->_orderByClause = $orderByClause;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkSubselect($this);
}
}

View File

@ -44,4 +44,9 @@ class SubselectFromClause extends Node
{
return $this->_identificationVariableDeclarations;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkSubselectFromClause($this);
}
}

View File

@ -40,5 +40,10 @@ class UpdateClause extends Node
{
return $this->_updateItems;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkUpdateClause($this);
}
}

View File

@ -1,7 +1,22 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Query\AST;
@ -42,5 +57,10 @@ class UpdateItem extends Node
{
return $this->_newValue;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkUpdateItem($this);
}
}

View File

@ -54,4 +54,9 @@ class UpdateStatement extends Node
{
return $this->_whereClause;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkUpdateStatement($this);
}
}

View File

@ -24,4 +24,9 @@ class WhereClause extends Node
{
return $this->_conditionalExpression;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkWhereClause($this);
}
}

View File

@ -1,5 +1,4 @@
<?php
/*
* $Id: Cache.php 3938 2008-03-06 19:36:50Z romanb $
*

View File

@ -21,6 +21,7 @@
namespace Doctrine\ORM\Query;
use Doctrine\Common\DoctrineException;
use Doctrine\ORM\Query\AST;
use Doctrine\ORM\Query\Exec;
@ -40,6 +41,32 @@ class Parser
{
const SCALAR_QUERYCOMPONENT_ALIAS = 'dctrn';
/** Maps registered string function names to class names. */
private static $_STRING_FUNCTIONS = array(
'concat' => 'Doctrine\ORM\Query\AST\Functions\ConcatFunction',
'substring' => 'Doctrine\ORM\Query\AST\Functions\SubstringFunction',
'trim' => 'Doctrine\ORM\Query\AST\Functions\TrimFunction',
'lower' => 'Doctrine\ORM\Query\AST\Functions\LowerFunction',
'upper' => 'Doctrine\ORM\Query\AST\Functions\UpperFunction'
);
/** Maps registered numeric function names to class names. */
private static $_NUMERIC_FUNCTIONS = array(
'length' => 'Doctrine\ORM\Query\AST\Functions\LengthFunction',
'locate' => 'Doctrine\ORM\Query\AST\Functions\LocateFunction',
'abs' => 'Doctrine\ORM\Query\AST\Functions\AbsFunction',
'sqrt' => 'Doctrine\ORM\Query\AST\Functions\SqrtFunction',
'mod' => 'Doctrine\ORM\Query\AST\Functions\ModFunction',
'size' => 'Doctrine\ORM\Query\AST\Functions\SizeFunction'
);
/** Maps registered datetime function names to class names. */
private static $_DATETIME_FUNCTIONS = array(
'current_date' => 'Doctrine\ORM\Query\AST\Functions\CurrentDateFunction',
'current_time' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimeFunction',
'current_timestamp' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimestampFunction'
);
/**
* The minimum number of tokens read after last detected error before
* another error can be reported.
@ -713,41 +740,6 @@ class Parser
return $this->_lexer->token['value'];
}
/**
* Special rule that acceps all kinds of path expressions.
*/
/*private function _StateFieldPathExpression()
{
$this->match(Lexer::T_IDENTIFIER);
$parts = array($this->_lexer->token['value']);
while ($this->_lexer->isNextToken('.')) {
$this->match('.');
$this->match(Lexer::T_IDENTIFIER);
$parts[] = $this->_lexer->token['value'];
}
$pathExpression = new AST\StateFieldPathExpression($parts);
return $pathExpression;
}*/
/**
* Special rule that acceps all kinds of path expressions. and defers their
* semantical checking until the FROM part has been parsed completely (joins inclusive).
* Mainly used for path expressions in the SelectExpressions.
*/
/*private function _PathExpressionInSelect()
{
$this->match(Lexer::T_IDENTIFIER);
$parts = array($this->_lexer->token['value']);
while ($this->_lexer->isNextToken('.')) {
$this->match('.');
$this->match(Lexer::T_IDENTIFIER);
$parts[] = $this->_lexer->token['value'];
}
$expr = new AST\StateFieldPathExpression($parts);
$this->_pendingPathExpressionsInSelect[] = $expr;
return $expr;
}*/
/**
* JoinVariableDeclaration ::= Join [IndexBy]
*/
@ -1231,23 +1223,138 @@ class Parser
}
/**
* SIMPLIFIED FROM BNF FOR NOW
* ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression )
* ComparisonExpression ::=
* ArithmeticExpression ComparisonOperator (QuantifiedExpression | ArithmeticExpression) |
* StringExpression ComparisonOperator (StringExpression | QuantifiedExpression) |
* BooleanExpression ("=" | "<>" | "!=") (BooleanExpression | QuantifiedExpression) |
* EnumExpression ("=" | "<>" | "!=") (EnumExpression | QuantifiedExpression) |
* DatetimeExpression ComparisonOperator (DatetimeExpression | QuantifiedExpression) |
* EntityExpression ("=" | "<>") (EntityExpression | QuantifiedExpression)
*/
private function _ComparisonExpression()
{
$leftExpr = $this->_ArithmeticExpression();
$operator = $this->_ComparisonOperator();
if ($this->_lexer->lookahead['type'] === Lexer::T_ALL ||
$this->_lexer->lookahead['type'] === Lexer::T_ANY ||
$this->_lexer->lookahead['type'] === Lexer::T_SOME) {
$rightExpr = $this->_QuantifiedExpression();
} else {
$peek = $this->_lexer->glimpse();
if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
if ($this->_isComparisonOperator($peek)) {
$this->match(Lexer::T_INPUT_PARAMETER);
$leftExpr = new AST\InputParameter($this->_lexer->token['value']);
} else {
$leftExpr = $this->_ArithmeticExpression();
}
$operator = $this->_ComparisonOperator();
$rightExpr = $this->_ArithmeticExpression();
//...
}
else if ($this->_lexer->isNextToken('(') && $peek['type'] == Lexer::T_SELECT) {
$leftExpr = $this->_Subselect();
//...
}
else if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER) && $peek['value'] == '(') {
$peek2 = $this->_peekBeyond(')');
if ($this->_isComparisonOperator($peek2)) {
if ($this->_isStringFunction($this->_lexer->lookahead['value'])) {
$leftExpr = $this->_FunctionsReturningStrings();
$operator = $this->_ComparisonOperator();
if ($this->_lexer->lookahead['type'] === Lexer::T_ALL ||
$this->_lexer->lookahead['type'] === Lexer::T_ANY ||
$this->_lexer->lookahead['type'] === Lexer::T_SOME) {
$rightExpr = $this->_QuantifiedExpression();
} else {
$rightExpr = $this->_StringPrimary();
}
} else if ($this->_isNumericFunction($this->_lexer->lookahead['value'])) {
$leftExpr = $this->_FunctionsReturningNumerics();
$operator = $this->_ComparisonOperator();
if ($this->_lexer->lookahead['type'] === Lexer::T_ALL ||
$this->_lexer->lookahead['type'] === Lexer::T_ANY ||
$this->_lexer->lookahead['type'] === Lexer::T_SOME) {
$rightExpr = $this->_QuantifiedExpression();
} else {
$rightExpr = $this->_ArithmeticExpression();
}
} else {
$leftExpr = $this->_FunctionsReturningDatetime();
$operator = $this->_ComparisonOperator();
if ($this->_lexer->lookahead['type'] === Lexer::T_ALL ||
$this->_lexer->lookahead['type'] === Lexer::T_ANY ||
$this->_lexer->lookahead['type'] === Lexer::T_SOME) {
$rightExpr = $this->_QuantifiedExpression();
} else {
$rightExpr = $this->_DatetimePrimary();
}
}
} else {
$leftExpr = $this->_ArithmeticExpression();
$operator = $this->_ComparisonOperator();
if ($this->_lexer->lookahead['type'] === Lexer::T_ALL ||
$this->_lexer->lookahead['type'] === Lexer::T_ANY ||
$this->_lexer->lookahead['type'] === Lexer::T_SOME) {
$rightExpr = $this->_QuantifiedExpression();
} else {
$rightExpr = $this->_StringExpression();
}
}
}
else if ($this->_isAggregateFunction($this->_lexer->lookahead)) {
$leftExpr = $this->_StringExpression();
$operator = $this->_ComparisonOperator();
if ($this->_lexer->lookahead['type'] === Lexer::T_ALL ||
$this->_lexer->lookahead['type'] === Lexer::T_ANY ||
$this->_lexer->lookahead['type'] === Lexer::T_SOME) {
$rightExpr = $this->_QuantifiedExpression();
} else {
$rightExpr = $this->_StringExpression();
}
}
else {
$leftExpr = $this->_ArithmeticExpression();
$operator = $this->_ComparisonOperator();
if ($this->_lexer->lookahead['type'] === Lexer::T_ALL ||
$this->_lexer->lookahead['type'] === Lexer::T_ANY ||
$this->_lexer->lookahead['type'] === Lexer::T_SOME) {
$rightExpr = $this->_QuantifiedExpression();
} else {
$rightExpr = $this->_ArithmeticExpression();
}
}
return new AST\ComparisonExpression($leftExpr, $operator, $rightExpr);
}
private function _isStringFunction($funcName)
{
return isset(self::$_STRING_FUNCTIONS[strtolower($funcName)]);
}
private function _isNumericFunction($funcName)
{
return isset(self::$_NUMERIC_FUNCTIONS[strtolower($funcName)]);
}
private function _isDatetimeFunction($funcName)
{
return isset(self::$_DATETIME_FUNCTIONS[strtolower($funcName)]);
}
private function _peekBeyond($token)
{
$peek = $this->_lexer->peek();
while ($peek['value'] != $token) {
$peek = $this->_lexer->peek();
}
$peek = $this->_lexer->peek();
$this->_lexer->resetPeek();
return $peek;
}
private function _isComparisonOperator($token)
{
$value = $token['value'];
return $value == '=' || $value == '<' || $value == '<=' || $value == '<>' ||
$value == '>' || $value == '>=' || $value == '!=';
}
/**
* ArithmeticExpression ::= SimpleArithmeticExpression | "(" Subselect ")"
*/
@ -1257,7 +1364,9 @@ class Parser
if ($this->_lexer->lookahead['value'] === '(') {
$peek = $this->_lexer->glimpse();
if ($peek['type'] === Lexer::T_SELECT) {
$this->match('(');
$expr->setSubselect($this->_Subselect());
$this->match(')');
return $expr;
}
}
@ -1526,7 +1635,7 @@ class Parser
case Lexer::T_IDENTIFIER:
$peek = $this->_lexer->glimpse();
if ($peek['value'] == '(') {
return $this->_FunctionsReturningStrings();
return $this->_FunctionsReturningNumerics();
}
return $this->_StateFieldPathExpression();
case Lexer::T_INPUT_PARAMETER:
@ -1548,7 +1657,7 @@ class Parser
$this->syntaxError();
}
}
throw \Doctrine\Common\DoctrineException::updateMe("Not yet implemented2.");
throw DoctrineException::updateMe("Not yet implemented2.");
//TODO...
}
@ -1562,42 +1671,51 @@ class Parser
*/
private function _FunctionsReturningStrings()
{
switch (strtoupper($this->_lexer->lookahead['value'])) {
case 'CONCAT':
break;
case 'SUBSTRING':
break;
case 'TRIM':
//TODO: This is not complete! See BNF
$this->match($this->_lexer->lookahead['value']);
$this->match('(');
$func = $this->_StringPrimary();
$this->match(')');
return $func;
case 'LOWER':
break;
case 'UPPER':
default:
$this->syntaxError('CONCAT, SUBSTRING, TRIM or UPPER');
}
$funcNameLower = strtolower($this->_lexer->lookahead['value']);
$funcClass = self::$_STRING_FUNCTIONS[$funcNameLower];
$function = new $funcClass($funcNameLower);
$function->parse($this);
return $function;
}
/**
* PortableFunctionsReturningNumerics ::=
* "LENGTH" "(" StringPrimary ")" |
* "LOCATE" "(" StringPrimary "," StringPrimary ["," SimpleArithmeticExpression]")" |
* "ABS" "(" SimpleArithmeticExpression ")" |
* "SQRT" "(" SimpleArithmeticExpression ")" |
* "MOD" "(" SimpleArithmeticExpression "," SimpleArithmeticExpression ")" |
* "SIZE" "(" CollectionValuedPathExpression ")"
*/
private function _FunctionsReturningNumerics()
{
$funcNameLower = strtolower($this->_lexer->lookahead['value']);
$funcClass = self::$_NUMERIC_FUNCTIONS[$funcNameLower];
$function = new $funcClass($funcNameLower);
$function->parse($this);
return $function;
}
/**
* PortableFunctionsReturningDateTime ::= "CURRENT_DATE" | "CURRENT_TIME" | "CURRENT_TIMESTAMP"
*/
public function _FunctionsReturningDatetime()
{
$funcNameLower = strtolower($this->_lexer->lookahead['value']);
$funcClass = self::$_DATETIME_FUNCTIONS[$funcNameLower];
$function = new $funcClass($funcNameLower);
$function->parse($this);
return $function;
}
/**
* Checks whether the given token type indicates an aggregate function.
*/
private function _isAggregateFunction($tokenType)
{
switch ($tokenType) {
case Lexer::T_AVG:
case Lexer::T_MIN:
case Lexer::T_MAX:
case Lexer::T_SUM:
case Lexer::T_COUNT:
return true;
default:
return false;
}
return $tokenType == Lexer::T_AVG || $tokenType == Lexer::T_MIN ||
$tokenType == Lexer::T_MAX || $tokenType == Lexer::T_SUM ||
$tokenType == Lexer::T_COUNT;
}
/**
@ -1674,7 +1792,10 @@ class Parser
if ($this->_lexer->lookahead['value'] === '(') {
$peek = $this->_lexer->glimpse();
if ($peek['type'] === Lexer::T_SELECT) {
return $this->_Subselect();
$this->match('(');
$expr = $this->_Subselect();
$this->match(')');
return $expr;
}
}
return $this->_StringPrimary();
@ -1683,7 +1804,7 @@ class Parser
/**
* StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression
*/
private function _StringPrimary()
public function _StringPrimary()
{
if ($this->_lexer->lookahead['type'] === Lexer::T_IDENTIFIER) {
$peek = $this->_lexer->glimpse();
@ -1704,4 +1825,19 @@ class Parser
$this->syntaxError('StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression');
}
}
public static function registerStringFunction($name, $class)
{
self::$_STRING_FUNCTIONS[$name] = $class;
}
public static function registerNumericFunction($name, $class)
{
self::$_NUMERIC_FUNCTIONS[$name] = $class;
}
public static function registerDatetimeFunction($name, $class)
{
self::$_DATETIME_FUNCTIONS[$name] = $class;
}
}

View File

@ -78,7 +78,7 @@ class SqlWalker
public function walkSelectClause($selectClause)
{
return 'SELECT ' . (($selectClause->isDistinct()) ? 'DISTINCT ' : '')
. implode(', ', array_map(array(&$this, 'walkSelectExpression'),
. implode(', ', array_map(array($this, 'walkSelectExpression'),
$selectClause->getSelectExpressions()));
}
@ -98,6 +98,11 @@ class SqlWalker
return $sql;
}
public function walkFunction($function)
{
return $function->getSql($this);
}
/**
* Walks down a JoinVariableDeclaration AST node and creates the corresponding SQL.
*
@ -108,8 +113,7 @@ class SqlWalker
{
$join = $joinVarDecl->getJoin();
$joinType = $join->getJoinType();
if ($joinType == AST\Join::JOIN_TYPE_LEFT ||
$joinType == AST\Join::JOIN_TYPE_LEFTOUTER) {
if ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) {
$sql = ' LEFT JOIN ';
} else {
$sql = ' INNER JOIN ';
@ -177,9 +181,9 @@ class SqlWalker
$sql .= $sqlTableAlias . '.' . $class->getColumnName($fieldName) .
' AS ' . $sqlTableAlias . '__' . $class->getColumnName($fieldName);
} else if ($pathExpression->isSimpleStateFieldAssociationPathExpression()) {
\Doctrine\Common\DoctrineException::updateMe("Not yet implemented.");
throw DoctrineException::updateMe("Not yet implemented.");
} else {
\Doctrine\Common\DoctrineException::updateMe("Encountered invalid PathExpression during SQL construction.");
throw DoctrineException::updateMe("Encountered invalid PathExpression during SQL construction.");
}
}
else if ($selectExpression->getExpression() instanceof AST\AggregateExpression) {
@ -355,9 +359,16 @@ class SqlWalker
. ' = ';
$newValue = $updateItem->getNewValue();
if ($newValue instanceof AST\InputParameter) {
$sql .= $newValue->isNamed() ? ':' . $newValue->getName() : '?';
} // TODO: else if ...
if ($newValue instanceof AST\Node) {
$sql .= $newValue->dispatch($this);
} else if (is_string($newValue)) {
if (strcasecmp($newValue, 'NULL') === 0) {
$sql .= 'NULL';
} else {
$sql .= $newValue; //TODO: quote()
}
}
return $sql;
}
@ -366,14 +377,14 @@ class SqlWalker
{
$sql = ' WHERE ';
$condExpr = $whereClause->getConditionalExpression();
$sql .= implode(' OR ', array_map(array(&$this, 'walkConditionalTerm'),
$sql .= implode(' OR ', array_map(array($this, 'walkConditionalTerm'),
$condExpr->getConditionalTerms()));
return $sql;
}
public function walkConditionalTerm($condTerm)
{
return implode(' AND ', array_map(array(&$this, 'walkConditionalFactor'),
return implode(' AND ', array_map(array($this, 'walkConditionalFactor'),
$condTerm->getConditionalFactors()));
}
@ -383,37 +394,30 @@ class SqlWalker
if ($factor->isNot()) $sql .= 'NOT ';
$primary = $factor->getConditionalPrimary();
if ($primary->isSimpleConditionalExpression()) {
$simpleCond = $primary->getSimpleConditionalExpression();
if ($simpleCond instanceof AST\ComparisonExpression) {
$sql .= $this->walkComparisonExpression($simpleCond);
}
else if ($simpleCond instanceof AST\LikeExpression) {
$sql .= $this->walkLikeExpression($simpleCond);
}
else if ($simpleCond instanceof AST\BetweenExpression) {
$sql .= $this->walkBetweenExpression($simpleCond);
}
else if ($simpleCond instanceof AST\InExpression) {
$sql .= $this->walkInExpression($simpleCond);
} else if ($simpleCond instanceof AST\NullComparisonExpression) {
$sql .= $this->walkNullComparisonExpression($simpleCond);
}
// else if ...
$sql .= $primary->getSimpleConditionalExpression()->dispatch($this);
} else if ($primary->isConditionalExpression()) {
$sql .= '(' . implode(' OR ', array_map(array(&$this, 'walkConditionalTerm'),
$sql .= '(' . implode(' OR ', array_map(array($this, 'walkConditionalTerm'),
$primary->getConditionalExpression()->getConditionalTerms())) . ')';
}
return $sql;
}
public function walkExistsExpression($existsExpr)
{
$sql = '';
if ($existsExpr->isNot()) $sql .= ' NOT';
$sql .= ' EXISTS (' . $this->walkSubselect($existsExpr->getSubselect()) . ')';
return $sql;
}
public function walkNullComparisonExpression($nullCompExpr)
{
$sql = '';
if ($nullCompExpr->getExpression() instanceof AST\InputParameter) {
$inputParam = $nullCompExpr->getExpression();
$sql .= ' ' . ($inputParam->isNamed() ? ':' . $inputParam->getName() : '?');
$innerExpr = $nullCompExpr->getExpression();
if ($innerExpr instanceof AST\InputParameter) {
$sql .= ' ' . ($innerExpr->isNamed() ? ':' . $innerExpr->getName() : '?');
} else {
$sql .= $this->walkPathExpression($nullCompExpr->getExpression());
$sql .= $this->walkPathExpression($innerExpr);
}
$sql .= ' IS' . ($nullCompExpr->isNot() ? ' NOT' : '') . ' NULL';
return $sql;
@ -438,7 +442,7 @@ class SqlWalker
if ($literal instanceof AST\InputParameter) {
return ($literal->isNamed() ? ':' . $literal->getName() : '?');
} else {
return $literal;
return $literal; //TODO: quote() ?
}
}
@ -455,11 +459,12 @@ class SqlWalker
{
$sql = '';
$stringExpr = $likeExpr->getStringExpression();
if ($stringExpr instanceof AST\StateFieldPathExpression) {
$sql .= $this->walkPathExpression($stringExpr);
} //TODO else...
$sql .= $stringExpr->dispatch($this);
if ($likeExpr->isNot()) $sql .= ' NOT';
$sql .= ' LIKE ';
if ($likeExpr->getStringPattern() instanceof AST\InputParameter) {
$inputParam = $likeExpr->getStringPattern();
$sql .= $inputParam->isNamed() ? ':' . $inputParam->getName() : '?';
@ -472,19 +477,39 @@ class SqlWalker
return $sql;
}
public function walkStateFieldPathExpression($stateFieldPathExpression)
{
return $this->walkPathExpression($stateFieldPathExpression);
}
public function walkComparisonExpression($compExpr)
{
$sql = '';
if ($compExpr->getLeftExpression() instanceof AST\ArithmeticExpression) {
$sql .= $this->walkArithmeticExpression($compExpr->getLeftExpression());
} // else...
$leftExpr = $compExpr->getLeftExpression();
$rightExpr = $compExpr->getRightExpression();
if ($leftExpr instanceof AST\Node) {
$sql .= $leftExpr->dispatch($this);
} else {
$sql .= $leftExpr; //TODO: quote()
}
$sql .= ' ' . $compExpr->getOperator() . ' ';
if ($compExpr->getRightExpression() instanceof AST\ArithmeticExpression) {
$sql .= $this->walkArithmeticExpression($compExpr->getRightExpression());
} // else...
if ($rightExpr instanceof AST\Node) {
$sql .= $rightExpr->dispatch($this);
} else {
$sql .= $rightExpr; //TODO: quote()
}
return $sql;
}
public function walkInputParameter($inputParam)
{
return $inputParam->isNamed() ? ':' . $inputParam->getName() : '?';
}
public function walkArithmeticExpression($arithmeticExpr)
{
$sql = '';
@ -501,40 +526,43 @@ class SqlWalker
public function walkArithmeticTerm($term)
{
if (is_string($term)) return $term;
return implode(' ', array_map(array(&$this, 'walkArithmeticFactor'),
return implode(' ', array_map(array($this, 'walkArithmeticFactor'),
$term->getArithmeticFactors()));
}
public function walkStringPrimary($stringPrimary)
{
if (is_string($stringPrimary)) {
return $stringPrimary;
} else {
return $stringPrimary->dispatch($this);
}
}
public function walkArithmeticFactor($factor)
{
if (is_string($factor)) return $factor;
$sql = '';
$primary = $factor->getArithmeticPrimary();
if (is_numeric($primary)) {
$sql .= $primary;
$sql .= $primary; //TODO: quote() ?
} else if (is_string($primary)) {
//TODO: quote string according to platform
$sql .= $primary;
} else if ($primary instanceof AST\StateFieldPathExpression) {
$sql .= $this->walkPathExpression($primary);
} else if ($primary instanceof AST\InputParameter) {
if ($primary->isNamed()) {
$sql .= ':' . $primary->getName();
} else {
$sql .= '?';
}
} else if ($primary instanceof AST\SimpleArithmeticExpression) {
$sql .= '(' . $this->walkSimpleArithmeticExpression($primary) . ')';
} else if ($primary instanceof AST\Node) {
$sql .= $primary->dispatch($this);
}
// else...
return $sql;
}
public function walkSimpleArithmeticExpression($simpleArithmeticExpr)
{
return implode(' ', array_map(array(&$this, 'walkArithmeticTerm'),
return implode(' ', array_map(array($this, 'walkArithmeticTerm'),
$simpleArithmeticExpr->getArithmeticTerms()));
}
@ -558,9 +586,9 @@ class SqlWalker
$sqlTableAlias = $this->_dqlToSqlAliasMap[$dqlAlias];
$sql .= $sqlTableAlias . '.' . $class->getColumnName($fieldName);
} else if ($pathExpr->isSimpleStateFieldAssociationPathExpression()) {
throw \Doctrine\Common\DoctrineException::updateMe("Not yet implemented.");
throw DoctrineException::updateMe("Not yet implemented.");
} else {
throw \Doctrine\Common\DoctrineException::updateMe("Encountered invalid PathExpression during SQL construction.");
throw DoctrineException::updateMe("Encountered invalid PathExpression during SQL construction.");
}
return $sql;
}

View File

@ -31,7 +31,6 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
$query = $this->_em->createQuery($dql);
$query->setDql($dql);
$parserResult = $query->parse();
$this->fail('No syntax errors were detected, when syntax errors were expected');
} catch (\Exception $e) {
//echo $e->getMessage() . PHP_EOL;
@ -90,7 +89,7 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
public function testFunctionalExpressionsSupportedInWherePart()
{
$this->assertValidDql("SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE TRIM(u.name) = 'someone'");
//$this->assertValidDql("SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE TRIM(u.name) = 'someone'");
}
public function testArithmeticExpressionsSupportedInWherePart()

View File

@ -156,6 +156,14 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
'SELECT c0.id AS c0__id, c1.id AS c1__id, c2.phonenumber AS c2__phonenumber, c3.id AS c3__id FROM cms_users c0 INNER JOIN cms_articles c1 ON c0.id = c1.user_id INNER JOIN cms_phonenumbers c2 ON c0.id = c2.user_id INNER JOIN cms_comments c3 ON c1.id = c3.article_id'
);
}
public function testTrimFunction()
{
$this->assertSqlGeneration(
"SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE TRIM(TRAILING ' ' FROM u.name) = 'someone'",
"SELECT c0.name AS c0__name FROM cms_users c0 WHERE TRIM(TRAILING ' ' FROM c0.name) = 'someone'"
);
}
/*public function testFunctionalExpressionsSupportedInWherePart()
{

View File

@ -62,7 +62,6 @@ class UpdateSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
'UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = ?1',
'UPDATE cms_users c0 SET c0.name = ?'
);
$this->assertSqlGeneration(
'UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = ?1, u.username = ?2',
'UPDATE cms_users c0 SET c0.name = ?, c0.username = ?'