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

[2.0] Parser work.

This commit is contained in:
romanb 2009-03-21 19:58:52 +00:00
parent 98076e0b0d
commit 618c1281e4
14 changed files with 152 additions and 357 deletions

View File

@ -194,10 +194,10 @@ abstract class AbstractHydrator
if ($this->_isIgnoredName($key)) continue; if ($this->_isIgnoredName($key)) continue;
// Cache general information like the column name <-> field name mapping // Cache general information like the column name <-> field name mapping
$e = explode(\Doctrine\ORM\Query\ParserRule::SQLALIAS_SEPARATOR, $key); $e = explode(\Doctrine\ORM\Query\SqlWalker::SQLALIAS_SEPARATOR, $key);
$columnName = array_pop($e); $columnName = array_pop($e);
$cache[$key]['dqlAlias'] = $this->_tableAliases[ $cache[$key]['dqlAlias'] = $this->_tableAliases[
implode(\Doctrine\ORM\Query\ParserRule::SQLALIAS_SEPARATOR, $e) implode(\Doctrine\ORM\Query\SqlWalker::SQLALIAS_SEPARATOR, $e)
]; ];
$classMetadata = $this->_queryComponents[$cache[$key]['dqlAlias']]['metadata']; $classMetadata = $this->_queryComponents[$cache[$key]['dqlAlias']]['metadata'];
// check whether it's an aggregate value or a regular field // check whether it's an aggregate value or a regular field
@ -264,10 +264,10 @@ abstract class AbstractHydrator
if ($this->_isIgnoredName($key)) continue; if ($this->_isIgnoredName($key)) continue;
// cache general information like the column name <-> field name mapping // cache general information like the column name <-> field name mapping
$e = explode(\Doctrine\ORM\Query\ParserRule::SQLALIAS_SEPARATOR, $key); $e = explode(\Doctrine\ORM\Query\SqlWalker::SQLALIAS_SEPARATOR, $key);
$columnName = array_pop($e); $columnName = array_pop($e);
$cache[$key]['dqlAlias'] = $this->_tableAliases[ $cache[$key]['dqlAlias'] = $this->_tableAliases[
implode(\Doctrine\ORM\Query\ParserRule::SQLALIAS_SEPARATOR, $e) implode(\Doctrine\ORM\Query\SqlWalker::SQLALIAS_SEPARATOR, $e)
]; ];
$classMetadata = $this->_queryComponents[$cache[$key]['dqlAlias']]['metadata']; $classMetadata = $this->_queryComponents[$cache[$key]['dqlAlias']]['metadata'];
// check whether it's an aggregate value or a regular field // check whether it's an aggregate value or a regular field

View File

@ -19,6 +19,10 @@ class InputParameter extends Node
public function __construct($value) public function __construct($value)
{ {
if (strlen($value) == 1) {
throw new \InvalidArgumentException("Invalid parameter format.");
}
$param = substr($value, 1); $param = substr($value, 1);
$this->_isNamed = ! is_numeric($param); $this->_isNamed = ! is_numeric($param);
if ($this->_isNamed) { if ($this->_isNamed) {

View File

@ -62,6 +62,8 @@ abstract class AbstractResult
*/ */
protected $_enumParams; protected $_enumParams;
protected $_defaultQueryComponentAlias;
/** /**
* Cannot be called directly, factory methods handle this job. * Cannot be called directly, factory methods handle this job.
* *
@ -110,6 +112,24 @@ abstract class AbstractResult
return $this->_queryComponents; return $this->_queryComponents;
} }
/**
*
*/
public function getDefaultQueryComponentAlias()
{
return $this->_defaultQueryComponentAlias;
}
/**
*
*
* @param <type> $alias
*/
public function setDefaultQueryComponentAlias($alias)
{
$this->_defaultQueryComponentAlias = $alias;
}
/** /**
* Get the declaration for given component alias. * Get the declaration for given component alias.
* *

View File

@ -222,7 +222,7 @@ class Lexer
'[a-z_][a-z0-9_\\\]*', '[a-z_][a-z0-9_\\\]*',
'(?:[0-9]+(?:[,\.][0-9]+)*)(?:e[+-]?[0-9]+)?', '(?:[0-9]+(?:[,\.][0-9]+)*)(?:e[+-]?[0-9]+)?',
"'(?:[^']|'')*'", "'(?:[^']|'')*'",
'\?[0-9]+|:[a-z][a-z0-9_]+' '\?[1-9]+|:[a-z][a-z0-9_]+'
); );
$regex = '/(' . implode(')|(', $patterns) . ')|\s+|(.)/i'; $regex = '/(' . implode(')|(', $patterns) . ')|\s+|(.)/i';
} }

View File

@ -38,6 +38,8 @@ use Doctrine\ORM\Query\Exec;
*/ */
class Parser class Parser
{ {
const SCALAR_QUERYCOMPONENT_ALIAS = 'dctrn';
/** /**
* The minimum number of tokens read after last detected error before * The minimum number of tokens read after last detected error before
* another error can be reported. * another error can be reported.
@ -85,13 +87,11 @@ class Parser
{ {
$this->_em = $query->getEntityManager(); $this->_em = $query->getEntityManager();
$this->_lexer = new Lexer($query->getDql()); $this->_lexer = new Lexer($query->getDql());
$defaultQueryComponent = ParserRule::DEFAULT_QUERYCOMPONENT;
$this->_parserResult = new ParserResult( $this->_parserResult = new ParserResult(
'', '',
array( // queryComponent array( // queryComponent
$defaultQueryComponent => array( self::SCALAR_QUERYCOMPONENT_ALIAS => array(
'metadata' => null, 'metadata' => null,
'parent' => null, 'parent' => null,
'relation' => null, 'relation' => null,
@ -100,7 +100,7 @@ class Parser
), ),
), ),
array( // tableAliasMap array( // tableAliasMap
$defaultQueryComponent => $defaultQueryComponent, self::SCALAR_QUERYCOMPONENT_ALIAS => self::SCALAR_QUERYCOMPONENT_ALIAS,
) )
); );
@ -215,7 +215,6 @@ class Parser
$token = $this->_lexer->lookahead; $token = $this->_lexer->lookahead;
} }
// Formatting message
$message = 'line 0, col ' . (isset($token['position']) ? $token['position'] : '-1') . ': Error: '; $message = 'line 0, col ' . (isset($token['position']) ? $token['position'] : '-1') . ': Error: ';
if ($expected !== '') { if ($expected !== '') {
@ -272,21 +271,7 @@ class Parser
public function getEntityManager() public function getEntityManager()
{ {
return $this->_em; return $this->_em;
} }
/**
* Retrieve the piece of DQL string given the token position
*
* @param array $token Token that it was processing.
* @return string Piece of DQL string.
*/
/*public function getQueryPiece($token, $previousChars = 10, $nextChars = 10)
{
$start = max(0, $token['position'] - $previousChars);
$end = max($token['position'] + $nextChars, strlen($this->_input));
return substr($this->_input, $start, $end);
}*/
/** /**
* Checks if the next-next (after lookahead) token start a function. * Checks if the next-next (after lookahead) token start a function.
@ -296,7 +281,7 @@ class Parser
private function _isFunction() private function _isFunction()
{ {
$next = $this->_lexer->glimpse(); $next = $this->_lexer->glimpse();
return ($next['value'] === '('); return $next['value'] === '(';
} }
/** /**
@ -360,7 +345,7 @@ class Parser
} }
/** /**
* * Begins a new stack of deferred path expressions.
*/ */
private function _beginDeferredPathExpressionStack() private function _beginDeferredPathExpressionStack()
{ {
@ -368,8 +353,8 @@ class Parser
} }
/** /**
* Processes pending path expressions that were encountered while parsing * Processes the topmost stack of deferred path expressions.
* select expressions. These will be validated to make sure they are indeed * These will be validated to make sure they are indeed
* valid <tt>StateFieldPathExpression</tt>s and additional information * valid <tt>StateFieldPathExpression</tt>s and additional information
* is attached to their AST nodes. * is attached to their AST nodes.
*/ */
@ -451,6 +436,8 @@ class Parser
if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) { if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
$this->match(Lexer::T_IDENTIFIER); $this->match(Lexer::T_IDENTIFIER);
$aliasIdentificationVariable = $this->_lexer->token['value']; $aliasIdentificationVariable = $this->_lexer->token['value'];
} else {
$aliasIdentificationVariable = $abstractSchemaName;
} }
$this->match(Lexer::T_SET); $this->match(Lexer::T_SET);
$updateItems = array(); $updateItems = array();
@ -460,18 +447,17 @@ class Parser
$updateItems[] = $this->_UpdateItem(); $updateItems[] = $this->_UpdateItem();
} }
if ($aliasIdentificationVariable) { $classMetadata = $this->_em->getClassMetadata($abstractSchemaName);
$classMetadata = $this->_em->getClassMetadata($abstractSchemaName); // Building queryComponent
// Building queryComponent $queryComponent = array(
$queryComponent = array( 'metadata' => $classMetadata,
'metadata' => $classMetadata, 'parent' => null,
'parent' => null, 'relation' => null,
'relation' => null, 'map' => null,
'map' => null, 'scalar' => null,
'scalar' => null, );
); $this->_parserResult->setQueryComponent($aliasIdentificationVariable, $queryComponent);
$this->_parserResult->setQueryComponent($aliasIdentificationVariable, $queryComponent); $this->_parserResult->setDefaultQueryComponentAlias($aliasIdentificationVariable);
}
$updateClause = new AST\UpdateClause($abstractSchemaName, $updateItems); $updateClause = new AST\UpdateClause($abstractSchemaName, $updateItems);
$updateClause->setAliasIdentificationVariable($aliasIdentificationVariable); $updateClause->setAliasIdentificationVariable($aliasIdentificationVariable);
@ -490,6 +476,8 @@ class Parser
$this->match(Lexer::T_IDENTIFIER); $this->match(Lexer::T_IDENTIFIER);
$identVariable = $this->_lexer->token['value']; $identVariable = $this->_lexer->token['value'];
$this->match('.'); $this->match('.');
} else {
$identVariable = $this->_parserResult->getDefaultQueryComponentAlias();
} }
$this->match(Lexer::T_IDENTIFIER); $this->match(Lexer::T_IDENTIFIER);
$field = $this->_lexer->token['value']; $field = $this->_lexer->token['value'];
@ -551,18 +539,22 @@ class Parser
if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) { if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
$this->match(Lexer::T_IDENTIFIER); $this->match(Lexer::T_IDENTIFIER);
$deleteClause->setAliasIdentificationVariable($this->_lexer->token['value']); $deleteClause->setAliasIdentificationVariable($this->_lexer->token['value']);
} else {
$classMetadata = $this->_em->getClassMetadata($deleteClause->getAbstractSchemaName()); $deleteClause->setAliasIdentificationVariable($deleteClause->getAbstractSchemaName());
// Building queryComponent
$queryComponent = array(
'metadata' => $classMetadata,
'parent' => null,
'relation' => null,
'map' => null,
'scalar' => null,
);
$this->_parserResult->setQueryComponent($this->_lexer->token['value'], $queryComponent);
} }
$classMetadata = $this->_em->getClassMetadata($deleteClause->getAbstractSchemaName());
$queryComponent = array(
'metadata' => $classMetadata,
'parent' => null,
'relation' => null,
'map' => null,
'scalar' => null,
);
$this->_parserResult->setQueryComponent($deleteClause->getAliasIdentificationVariable(),
$queryComponent);
$this->_parserResult->setDefaultQueryComponentAlias($deleteClause->getAliasIdentificationVariable());
return $deleteClause; return $deleteClause;
} }
@ -599,6 +591,9 @@ class Parser
$this->match(Lexer::T_FROM); $this->match(Lexer::T_FROM);
$identificationVariableDeclarations = array(); $identificationVariableDeclarations = array();
$identificationVariableDeclarations[] = $this->_IdentificationVariableDeclaration(); $identificationVariableDeclarations[] = $this->_IdentificationVariableDeclaration();
$this->_parserResult->setDefaultQueryComponentAlias(
$identificationVariableDeclarations[0]->getRangeVariableDeclaration()->getAbstractSchemaName()
);
while ($this->_lexer->isNextToken(',')) { while ($this->_lexer->isNextToken(',')) {
$this->match(','); $this->match(',');
$identificationVariableDeclarations[] = $this->_IdentificationVariableDeclaration(); $identificationVariableDeclarations[] = $this->_IdentificationVariableDeclaration();
@ -1345,6 +1340,7 @@ class Parser
$this->match(','); $this->match(',');
$literals[] = $this->_Literal(); $literals[] = $this->_Literal();
} }
$inExpression->setLiterals($literals);
} }
$this->match(')'); $this->match(')');

View File

@ -1,79 +0,0 @@
<?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.phpdoctrine.org>.
*/
/**
* Production variables holder
*
* @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
* @since 2.0
* @version $Revision$
*/
class Doctrine_ORM_Query_ParserDataHolder
{
protected static $_instance;
protected $_data;
protected function __construct()
{
$this->free();
}
public static function create()
{
if ( ! isset(self::$_instance)) {
self::$_instance = new self;
}
return self::$_instance;
}
public function free()
{
$this->_data = array();
}
public function set($offset, $value)
{
$this->_data[$offset] = $value;
}
public function get($offset)
{
return isset($this->_data[$offset]) ? $this->_data[$offset] : null;
}
public function has($offset)
{
return isset($this->_data[$offset]);
}
public function remove($offset)
{
if ($this->has($offset)) {
$this->_data[$offset] = null;
unset($this->_data[$offset]);
}
}
}

View File

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

View File

@ -1,193 +0,0 @@
<?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.phpdoctrine.org>.
*/
namespace Doctrine\ORM\Query;
/**
* An abstract base class for the productions of the Doctrine Query Language
* context-free grammar.
*
* @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
* @since 2.0
* @version $Revision$
*/
abstract class ParserRule
{
/**
* @nodoc
*/
const SQLALIAS_SEPARATOR = '__';
/**
* @nodoc
*/
const DEFAULT_QUERYCOMPONENT = 'dctrn';
/**
* Parser object
*
* @var Doctrine_ORM_Query_Parser
*/
protected $_parser;
/**
* The EntityManager.
*
* @var EntityManager
*/
protected $_em;
/**
* The Parser Data Holder.
*
* @var ParserDataHolder
*/
protected $_dataHolder;
/**
* Creates a new production object.
*
* @param Doctrine_ORM_Query_Parser $parser a parser object
*/
public function __construct(Doctrine_ORM_Query_Parser $parser)
{
$this->_parser = $parser;
$this->_em = $this->_parser->getEntityManager();
$this->_dataHolder = Doctrine_ORM_Query_ParserDataHolder::create();
}
protected function _isNextToken($token)
{
$la = $this->_parser->lookahead;
return ($la['type'] === $token || $la['value'] === $token);
}
protected function _isFunction()
{
$la = $this->_parser->lookahead;
$next = $this->_parser->getScanner()->peek();
return ($la['type'] === Doctrine_ORM_Query_Token::T_IDENTIFIER && $next['value'] === '(');
}
protected function _isSubselect()
{
$la = $this->_parser->lookahead;
$next = $this->_parser->getScanner()->peek();
return ($la['value'] === '(' && $next['type'] === Doctrine_ORM_Query_Token::T_SELECT);
}
/**
* Executes the grammar rule using the specified parameters.
*
* @param string $RuleName BNF Grammar Rule name
* @param array $paramHolder Production parameter holder
* @return Doctrine_ORM_Query_AST The constructed subtree during parsing.
*/
public function parse($ruleName)
{
echo $ruleName . PHP_EOL;
return $this->_getGrammarRule($ruleName)->syntax();
// Syntax check
/*if ( ! $this->_dataHolder->has('syntaxCheck') || $this->_dataHolder->get('syntaxCheck') === true) {
//echo "Processing syntax checks of " . $RuleName . "...\n";
$ASTNode = $BNFGrammarRule->syntax();
if ($ASTNode !== null) {
//echo "Returning Grammar Rule class: " . (is_object($ASTNode) ? get_class($ASTNode) : $ASTNode) . "...\n";
return $ASTNode;
}
}*/
// Semantical check
/*if ( ! $this->_dataHolder->has('semanticalCheck') || $this->_dataHolder->get('semanticalCheck') === true) {
echo "Processing semantical checks of " . $RuleName . "...\n";
$return = $BNFGrammarRule->semantical();
if ($return !== null) {
echo "Returning Grammar Rule class: " . (is_object($return) ? get_class($return) : $return) . "...\n";
return $return;
}
}*/
return $BNFGrammarRule;
}
/**
* Returns a grammar rule object with the given name.
*
* @param string $name grammar rule name
* @return Doctrine_ORM_Query_ParserRule
*/
protected function _getGrammarRule($name)
{
$class = 'Doctrine_ORM_Query_Parser_' . $name;
//echo $class . "\r\n";
//TODO: This expensive check is not necessary. Should be removed at the end.
// "new $class" will throw an error anyway if the class is not found.
if ( ! class_exists($class)) {
throw \Doctrine\Common\DoctrineException::updateMe(
"Unknown Grammar Rule '$name'. Could not find related compiler class."
);
}
return new $class($this->_parser);
}
/**
* Creates an AST node with the given name.
*
* @param string $AstName AST node name
* @return Doctrine_ORM_Query_AST
*/
public function AST($AstName)
{
$class = 'Doctrine_ORM_Query_AST_' . $AstName;
return new $class($this->_parser->getParserResult());
}
/**
* @nodoc
*/
abstract public function syntax();
/**
* @nodoc
*/
public function semantical()
{
}
public function getParser()
{
return $this->_parser;
}
public function getDataHolder()
{
return $this->_dataHolder;
}
}

View File

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

View File

@ -21,6 +21,7 @@
namespace Doctrine\ORM\Query; namespace Doctrine\ORM\Query;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\AST; use Doctrine\ORM\Query\AST;
use Doctrine\Common\DoctrineException; use Doctrine\Common\DoctrineException;
@ -35,6 +36,8 @@ use Doctrine\Common\DoctrineException;
*/ */
class SqlWalker class SqlWalker
{ {
const SQLALIAS_SEPARATOR = '__';
private $_tableAliasCounter = 0; private $_tableAliasCounter = 0;
private $_parserResult; private $_parserResult;
private $_em; private $_em;
@ -50,7 +53,7 @@ class SqlWalker
$this->_parserResult = $parserResult; $this->_parserResult = $parserResult;
$sqlToDqlAliasMap = array(); $sqlToDqlAliasMap = array();
foreach ($parserResult->getQueryComponents() as $dqlAlias => $qComp) { foreach ($parserResult->getQueryComponents() as $dqlAlias => $qComp) {
if ($dqlAlias != 'dctrn') { if ($dqlAlias != Parser::SCALAR_QUERYCOMPONENT_ALIAS) {
$sqlAlias = $this->generateSqlTableAlias($qComp['metadata']->getTableName()); $sqlAlias = $this->generateSqlTableAlias($qComp['metadata']->getTableName());
$sqlToDqlAliasMap[$sqlAlias] = $dqlAlias; $sqlToDqlAliasMap[$sqlAlias] = $dqlAlias;
} }
@ -302,7 +305,9 @@ class SqlWalker
public function walkUpdateStatement(AST\UpdateStatement $AST) public function walkUpdateStatement(AST\UpdateStatement $AST)
{ {
$sql = $this->walkUpdateClause($AST->getUpdateClause());
$sql .= $AST->getWhereClause() ? $this->walkWhereClause($AST->getWhereClause()) : '';
return $sql;
} }
public function walkDeleteStatement(AST\DeleteStatement $AST) public function walkDeleteStatement(AST\DeleteStatement $AST)
@ -323,6 +328,40 @@ class SqlWalker
return $sql; return $sql;
} }
public function walkUpdateClause($updateClause)
{
$sql = 'UPDATE ';
$class = $this->_em->getClassMetadata($updateClause->getAbstractSchemaName());
$sql .= $class->getTableName();
if ($updateClause->getAliasIdentificationVariable()) {
$sql .= ' ' . $this->_dqlToSqlAliasMap[$updateClause->getAliasIdentificationVariable()];
}
$sql .= ' SET ' . implode(', ', array_map(array($this, 'walkUpdateItem'),
$updateClause->getUpdateItems()));
return $sql;
}
public function walkUpdateItem($updateItem)
{
$sql = '';
$dqlAlias = $updateItem->getIdentificationVariable() ?
$updateItem->getIdentificationVariable() :
$this->_parserResult->getDefaultQueryComponentAlias();
$qComp = $this->_parserResult->getQueryComponent($dqlAlias);
$sql .= $this->_dqlToSqlAliasMap[$dqlAlias] . '.'
. $qComp['metadata']->getColumnName($updateItem->getField())
. ' = ';
$newValue = $updateItem->getNewValue();
if ($newValue instanceof AST\InputParameter) {
$sql .= $newValue->isNamed() ? ':' . $newValue->getName() : '?';
} // TODO: else if ...
return $sql;
}
public function walkWhereClause($whereClause) public function walkWhereClause($whereClause)
{ {
$sql = ' WHERE '; $sql = ' WHERE ';
@ -388,12 +427,21 @@ class SqlWalker
if ($inExpr->getSubselect()) { if ($inExpr->getSubselect()) {
$sql .= $this->walkSubselect($inExpr->getSubselect()); $sql .= $this->walkSubselect($inExpr->getSubselect());
} else { } else {
//$sql .= implode(', ', array_map(array($this, 'walkLiteral'), $inExpr->getLiterals())); $sql .= implode(', ', array_map(array($this, 'walkLiteral'), $inExpr->getLiterals()));
} }
$sql .= ')'; $sql .= ')';
return $sql; return $sql;
} }
public function walkLiteral($literal)
{
if ($literal instanceof AST\InputParameter) {
return ($literal->isNamed() ? ':' . $literal->getName() : '?');
} else {
return $literal;
}
}
public function walkBetweenExpression($betweenExpr) public function walkBetweenExpression($betweenExpr)
{ {
$sql = $this->walkArithmeticExpression($betweenExpr->getBaseExpression()); $sql = $this->walkArithmeticExpression($betweenExpr->getBaseExpression());
@ -433,7 +481,7 @@ class SqlWalker
$sql .= ' ' . $compExpr->getOperator() . ' '; $sql .= ' ' . $compExpr->getOperator() . ' ';
if ($compExpr->getRightExpression() instanceof AST\ArithmeticExpression) { if ($compExpr->getRightExpression() instanceof AST\ArithmeticExpression) {
$sql .= $this->walkArithmeticExpression($compExpr->getRightExpression()); $sql .= $this->walkArithmeticExpression($compExpr->getRightExpression());
} } // else...
return $sql; return $sql;
} }

View File

@ -24,9 +24,7 @@ class AllTests
$suite->addTestSuite('Doctrine\Tests\ORM\Query\LanguageRecognitionTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Query\LanguageRecognitionTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Query\LexerTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Query\LexerTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Query\DeleteSqlGenerationTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Query\DeleteSqlGenerationTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Query\UpdateSqlGenerationTest');
/*
$suite->addTestSuite('Orm_Query_UpdateSqlGenerationTest');*/
return $suite; return $suite;
} }

View File

@ -218,19 +218,17 @@ class DeleteSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
); );
} }
/*
public function testWithExprAndIn() public function testWithExprAndIn()
{ {
// "WHERE" Expression InExpression
$this->assertSqlGeneration( $this->assertSqlGeneration(
'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id IN ( ?, ?, ?, ? )', 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id IN ( ?1, ?2, ?3, ?4 )',
'DELETE FROM cms_users c0 WHERE c0.id IN (?, ?, ?, ?)' 'DELETE FROM cms_users c0 WHERE c0.id IN (?, ?, ?, ?)'
); );
$this->assertSqlGeneration( $this->assertSqlGeneration(
'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id NOT IN ( ?, ? )', 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id NOT IN ( ?1, ?2 )',
'DELETE FROM cms_users c0 WHERE c0.id NOT IN (?, ?)' 'DELETE FROM cms_users c0 WHERE c0.id NOT IN (?, ?)'
); );
} }
*/
} }

View File

@ -181,13 +181,12 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
public function testSubselectInSelectPart() public function testSubselectInSelectPart()
{ {
// Semantical error: Unknown query component u (probably in subselect)
$this->assertValidDql("SELECT u.name, (SELECT COUNT(p.phonenumber) FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.phonenumber = 1234) pcount FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = 'jon'"); $this->assertValidDql("SELECT u.name, (SELECT COUNT(p.phonenumber) FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.phonenumber = 1234) pcount FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = 'jon'");
} }
public function testPositionalInputParameter() public function testPositionalInputParameter()
{ {
$this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?'); $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1');
} }
public function testNamedInputParameter() public function testNamedInputParameter()

View File

@ -16,14 +16,16 @@
* *
* This software consists of voluntary contributions made by many individuals * This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see * and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.org>. * <http://www.doctrine-project.org>.
*/ */
namespace Doctrine\Tests\ORM\Query;
require_once __DIR__ . '/../../TestInit.php';
/** /**
* Test case for testing the saving and referencing of query identifiers. * Test case for testing the saving and referencing of query identifiers.
* *
* @package Doctrine
* @subpackage Query
* @author Guilherme Blanco <guilhermeblanco@hotmail.com> * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Janne Vanhala <jpvanhal@cc.hut.fi> * @author Janne Vanhala <jpvanhal@cc.hut.fi>
* @author Konsta Vesterinen <kvesteri@cc.hut.fi> * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
@ -35,34 +37,36 @@
* testcases later since we'll have a lot of them and we might want to have special SQL * testcases later since we'll have a lot of them and we might want to have special SQL
* generation tests for some dbms specific SQL syntaxes. * generation tests for some dbms specific SQL syntaxes.
*/ */
class Orm_Query_UpdateSqlGenerationTest extends Doctrine_OrmTestCase class UpdateSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
{ {
private $_em;
protected function setUp() {
$this->_em = $this->_getTestEntityManager();
}
public function assertSqlGeneration($dqlToBeTested, $sqlToBeConfirmed) public function assertSqlGeneration($dqlToBeTested, $sqlToBeConfirmed)
{ {
try { try {
$entityManager = $this->_em; $query = $this->_em->createQuery($dqlToBeTested);
$query = $entityManager->createQuery($dqlToBeTested);
parent::assertEquals($sqlToBeConfirmed, $query->getSql()); parent::assertEquals($sqlToBeConfirmed, $query->getSql());
$query->free(); $query->free();
} catch (Doctrine_Exception $e) { } catch (\Exception $e) {
$this->fail($e->getMessage()); $this->fail($e->getMessage());
} }
} }
public function testWithoutWhere() public function testWithoutWhere()
{ {
// NO WhereClause
$this->assertSqlGeneration( $this->assertSqlGeneration(
'UPDATE CmsUser u SET name = ?', 'UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = ?1',
'UPDATE cms_user cu SET cu.name = ? WHERE 1 = 1' 'UPDATE cms_users c0 SET c0.name = ?'
); );
$this->assertSqlGeneration( $this->assertSqlGeneration(
'UPDATE CmsUser u SET name = ?, username = ?', 'UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = ?1, u.username = ?2',
'UPDATE cms_user cu SET cu.name = ?, cu.username = ? WHERE 1 = 1' 'UPDATE cms_users c0 SET c0.name = ?, c0.username = ?'
); );
} }
} }