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-19 12:43:48 +00:00
parent bffd76d704
commit b718cd1a63
19 changed files with 817 additions and 276 deletions

View File

@ -0,0 +1,53 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
namespace Doctrine\ORM\Query\AST;
/**
* Description of BetweenExpression
*
* @author robo
*/
class BetweenExpression extends Node
{
private $_baseExpression;
private $_leftBetweenExpression;
private $_rightBetweenExpression;
private $_not;
public function __construct($baseExpr, $leftExpr, $rightExpr)
{
$this->_baseExpression = $baseExpr;
$this->_leftBetweenExpression = $leftExpr;
$this->_rightBetweenExpression = $rightExpr;
}
public function getBaseExpression()
{
return $this->_baseExpression;
}
public function getLeftBetweenExpression()
{
return $this->_leftBetweenExpression;
}
public function getRightBetweenExpression()
{
return $this->_rightBetweenExpression;
}
public function setNot($bool)
{
$this->_not = $bool;
}
public function getNot()
{
return $this->_not;
}
}

View File

@ -0,0 +1,37 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
namespace Doctrine\ORM\Query\AST;
/**
* DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName [["AS"] AliasIdentificationVariable]
*/
class DeleteClause extends Node
{
private $_abstractSchemaName;
private $_aliasIdentificationVariable;
public function __construct($abstractSchemaName)
{
$this->_abstractSchemaName = $abstractSchemaName;
}
public function getAbstractSchemaName()
{
return $this->_abstractSchemaName;
}
public function getAliasIdentificationVariable()
{
return $this->_aliasIdentificationVariable;
}
public function setAliasIdentificationVariable($alias)
{
$this->_aliasIdentificationVariable = $alias;
}
}

View File

@ -32,11 +32,10 @@ namespace Doctrine\ORM\Query\AST;
*/
class DeleteStatement extends Node
{
protected $_deleteClause;
protected $_whereClause;
private $_deleteClause;
private $_whereClause;
/* Setters */
public function setDeleteClause($deleteClause)
public function __construct($deleteClause)
{
$this->_deleteClause = $deleteClause;
}
@ -46,7 +45,6 @@ class DeleteStatement extends Node
$this->_whereClause = $whereClause;
}
/* Getters */
public function getDeleteClause()
{
return $this->_deleteClause;
@ -56,14 +54,4 @@ class DeleteStatement extends Node
{
return $this->_whereClause;
}
/* REMOVE ME LATER. COPIED METHODS FROM SPLIT OF PRODUCTION INTO "AST" AND "PARSER" */
public function buildSql()
{
// The 1=1 is needed to workaround the affected_rows in MySQL.
// Simple "DELETE FROM table_name" gives 0 affected rows.
return $this->_deleteClause->buildSql() . (($this->_whereClause !== null)
? ' ' . $this->_whereClause->buildSql() : ' WHERE 1 = 1');
}
}

View File

@ -0,0 +1,27 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
namespace Doctrine\ORM\Query\AST;
/**
* Description of HavingClause
*
* @author robo
*/
class HavingClause extends Node
{
private $_conditionalExpression;
public function __construct($conditionalExpression)
{
$this->_conditionalExpression = $conditionalExpression;
}
public function getConditionalExpression()
{
return $this->_conditionalExpression;
}
}

View File

@ -0,0 +1,27 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
namespace Doctrine\ORM\Query\AST;
/**
* OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}*
*
* @author robo
*/
class OrderByClause extends Node
{
private $_orderByItems = array();
public function __construct(array $orderByItems)
{
$this->_orderByItems = $orderByItems;
}
public function getOrderByItems()
{
return $this->_orderByItems;
}
}

View File

@ -0,0 +1,49 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
namespace Doctrine\ORM\Query\AST;
/**
* OrderByItem ::= StateFieldPathExpression ["ASC" | "DESC"]
*
* @author robo
*/
class OrderByItem extends Node
{
private $_pathExpr;
private $_asc;
private $_desc;
public function __construct($pathExpr)
{
$this->_pathExpr = $pathExpr;
}
public function getStateFieldPathExpression()
{
return $this->_pathExpr;
}
public function setAsc($bool)
{
$this->_asc = $bool;
}
public function isAsc()
{
return $this->_asc;
}
public function setDesc($bool)
{
$this->_desc = $bool;
}
public function isDesc()
{
return $this->_desc;
}
}

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$
*/

View File

@ -22,7 +22,7 @@
namespace Doctrine\ORM\Query\AST;
/**
* SimpleSelectClause ::= "SELECT" [DISTINCT"] SimpleSelectExpression
* SimpleSelectClause ::= "SELECT" ["DISTINCT"] SimpleSelectExpression
*
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL

View File

@ -0,0 +1,58 @@
<?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;
/**
* SimpleSelectExpression ::= StateFieldPathExpression | IdentificationVariable
* | (AggregateExpression [["AS"] FieldAliasIdentificationVariable])
*
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://www.doctrine-project.org
* @since 2.0
* @version $Revision$
*/
class SimpleSelectExpression extends Node
{
private $_expression;
private $_fieldIdentificationVariable;
public function __construct($expression)
{
$this->_expression = $expression;
}
public function getExpression()
{
return $this->_expression;
}
public function getFieldIdentificationVariable()
{
return $this->_fieldIdentificationVariable;
}
public function setFieldIdentificationVariable($fieldAlias)
{
$this->_fieldIdentificationVariable = $fieldAlias;
}
}

View File

@ -40,7 +40,7 @@ class SubselectFromClause extends Node
}
/* Getters */
public function geSubselectIdentificationVariableDeclarations()
public function getSubselectIdentificationVariableDeclarations()
{
return $this->_identificationVariableDeclarations;
}

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;
/**
* UpdateClause ::= "UPDATE" AbstractSchemaName [["AS"] AliasIdentificationVariable] "SET" UpdateItem {"," UpdateItem}*
*/
class UpdateClause extends Node
{
private $_abstractSchemaName;
private $_aliasIdentificationVariable;
private $_updateItems = array();
public function __construct($abstractSchemaName, array $updateItems)
{
$this->_abstractSchemaName = $abstractSchemaName;
$this->_updateItems = $updateItems;
}
public function getAbstractSchemaName()
{
return $this->_abstractSchemaName;
}
public function getAliasIdentificationVariable()
{
return $this->_aliasIdentificationVariable;
}
public function setAliasIdentificationVariable($alias)
{
$this->_aliasIdentificationVariable = $alias;
}
public function getUpdateItems()
{
return $this->_updateItems;
}
}

View File

@ -0,0 +1,46 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
namespace Doctrine\ORM\Query\AST;
/**
* UpdateItem ::= [IdentificationVariable "."] {StateField | SingleValuedAssociationField} "=" NewValue
*
* @author robo
*/
class UpdateItem extends Node
{
private $_identificationVariable;
private $_field;
private $_newValue;
public function __construct($field, $newValue)
{
$this->_field = $field;
$this->_newValue = $newValue;
}
public function setIdentificationVariable($identVar)
{
$this->_identificationVariable = $identVar;
}
public function getIdentificationVariable()
{
return $this->_identificationVariable;
}
public function getField()
{
return $this->_field;
}
public function getNewValue()
{
return $this->_newValue;
}
}

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,17 +26,16 @@ 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$
*/
class UpdateStatement extends Node
{
protected $_updateClause;
protected $_whereClause;
private $_updateClause;
private $_whereClause;
/* Setters */
public function setUpdateClause($updateClause)
public function __construct($updateClause)
{
$this->_updateClause = $updateClause;
}
@ -46,7 +45,6 @@ class UpdateStatement extends Node
$this->_whereClause = $whereClause;
}
/* Getters */
public function getUpdateClause()
{
return $this->_updateClause;
@ -56,13 +54,4 @@ class UpdateStatement extends Node
{
return $this->_whereClause;
}
/* REMOVE ME LATER. COPIED METHODS FROM SPLIT OF PRODUCTION INTO "AST" AND "PARSER" */
public function buildSql()
{
// The 1=1 is needed to workaround the affected_rows in MySQL.
// Simple "UPDATE table_name SET column_name = value" gives 0 affected rows.
return $this->_updateClause->buildSql() . (($this->_whereClause !== null)
? ' ' . $this->_whereClause->buildSql() : ' WHERE 1 = 1');
}
}

View File

@ -17,7 +17,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;
@ -26,7 +26,7 @@ namespace Doctrine\ORM\Query;
* Doctrine_ORM_Query_AbstractResult
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.com
* @link www.doctrine-project.com
* @since 2.0
* @version $Revision: 1393 $
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>

View File

@ -79,7 +79,7 @@ abstract class AbstractExecutor implements \Serializable
}
} else ...
*/
return new SingleTableDeleteUpdateExecutor($AST);
return new SingleTableDeleteUpdateExecutor($AST, $sqlWalker);
} else {
return new SingleSelectExecutor($AST, $sqlWalker);
}

View File

@ -16,11 +16,13 @@
*
* 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\Exec;
use Doctrine\ORM\Query\AST;
/**
* Executor that executes the SQL statements for DQL DELETE/UPDATE statements on classes
* that are mapped to a single table.
@ -28,16 +30,20 @@ namespace Doctrine\ORM\Query\Exec;
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @author Roman Borschel <roman@code-factory.org>
* @version $Revision$
* @link www.phpdoctrine.org
* @link www.doctrine-project.org
* @since 2.0
* @todo This is exactly the same as SingleSelectExecutor. Unify in SingleStatementExecutor.
*/
class SingleTableDeleteUpdateExecutor extends AbstractExecutor
{
public function __construct(\Doctrine\ORM\Query\AST\Node $AST)
public function __construct(AST\Node $AST, $sqlWalker)
{
parent::__construct($AST);
$this->_sqlStatements = $AST->buildSql();
parent::__construct($AST, $sqlWalker);
if ($AST instanceof AST\UpdateStatement) {
$this->_sqlStatements = $sqlWalker->walkUpdateStatement($AST);
} else if ($AST instanceof AST\DeleteStatement) {
$this->_sqlStatements = $sqlWalker->walkDeleteStatement($AST);
}
}
public function execute(\Doctrine\DBAL\Connection $conn, array $params)

View File

@ -326,16 +326,10 @@ class Parser
switch ($this->_lexer->lookahead['type']) {
case Lexer::T_SELECT:
return $this->_SelectStatement();
break;
case Lexer::T_UPDATE:
return $this->_UpdateStatement();
break;
case Lexer::T_DELETE:
return $this->_DeleteStatement();
break;
default:
$this->syntaxError('SELECT, UPDATE or DELETE');
break;
@ -430,7 +424,97 @@ class Parser
*/
private function _UpdateStatement()
{
//TODO
$updateStatement = new AST\UpdateStatement($this->_UpdateClause());
$updateStatement->setWhereClause(
$this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->_WhereClause() : null
);
return $updateStatement;
}
/**
* UpdateClause ::= "UPDATE" AbstractSchemaName [["AS"] AliasIdentificationVariable] "SET" UpdateItem {"," UpdateItem}*
*/
private function _UpdateClause()
{
$this->match(Lexer::T_UPDATE);
$abstractSchemaName = $this->_AbstractSchemaName();
$aliasIdentificationVariable = null;
if ($this->_lexer->isNextToken(Lexer::T_AS)) {
$this->match(Lexer::T_AS);
}
if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
$this->match(Lexer::T_IDENTIFIER);
$aliasIdentificationVariable = $this->_lexer->token['value'];
}
$this->match(Lexer::T_SET);
$updateItems = array();
$updateItems[] = $this->_UpdateItem();
while ($this->_lexer->isNextToken(',')) {
$this->match(',');
$updateItems[] = $this->_UpdateItem();
}
if ($aliasIdentificationVariable) {
$classMetadata = $this->_em->getClassMetadata($abstractSchemaName);
// Building queryComponent
$queryComponent = array(
'metadata' => $classMetadata,
'parent' => null,
'relation' => null,
'map' => null,
'scalar' => null,
);
$this->_parserResult->setQueryComponent($aliasIdentificationVariable, $queryComponent);
}
$updateClause = new AST\UpdateClause($abstractSchemaName, $updateItems);
$updateClause->setAliasIdentificationVariable($aliasIdentificationVariable);
return $updateClause;
}
/**
* UpdateItem ::= [IdentificationVariable "."] {StateField | SingleValuedAssociationField} "=" NewValue
*/
private function _UpdateItem()
{
$peek = $this->_lexer->glimpse();
$identVariable = null;
if ($peek['value'] == '.') {
$this->match(Lexer::T_IDENTIFIER);
$identVariable = $this->_lexer->token['value'];
$this->match('.');
}
$this->match(Lexer::T_IDENTIFIER);
$field = $this->_lexer->token['value'];
$this->match('=');
$newValue = $this->_NewValue();
$updateItem = new AST\UpdateItem($field, $newValue);
$updateItem->setIdentificationVariable($identVariable);
return $updateItem;
}
/**
* NewValue ::= SimpleArithmeticExpression | StringPrimary | DatetimePrimary | BooleanPrimary |
* EnumPrimary | SimpleEntityExpression | "NULL"
*/
private function _NewValue()
{
if ($this->_lexer->isNextToken(Lexer::T_NULL)) {
$this->match(Lexer::T_NULL);
return null;
} else if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
$this->match(Lexer::T_INPUT_PARAMETER);
return new AST\InputParameter($this->_lexer->token['value']);
} else if ($this->_lexer->isNextToken(Lexer::T_STRING)) {
//TODO: Can be StringPrimary or EnumPrimary
return $this->_StringPrimary();
} else {
$this->syntaxError('Not yet implemented.');
//echo "UH OH ...";
}
}
/**
@ -438,7 +522,42 @@ class Parser
*/
private function _DeleteStatement()
{
//TODO
$deleteStatement = new AST\DeleteStatement($this->_DeleteClause());
$deleteStatement->setWhereClause(
$this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->_WhereClause() : null
);
return $deleteStatement;
}
/**
* DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName [["AS"] AliasIdentificationVariable]
*/
private function _DeleteClause()
{
$this->match(Lexer::T_DELETE);
if ($this->_lexer->isNextToken(Lexer::T_FROM)) {
$this->match(Lexer::T_FROM);
}
$deleteClause = new AST\DeleteClause($this->_AbstractSchemaName());
if ($this->_lexer->isNextToken(Lexer::T_AS)) {
$this->match(Lexer::T_AS);
}
if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
$this->match(Lexer::T_IDENTIFIER);
$deleteClause->setAliasIdentificationVariable($this->_lexer->token['value']);
$classMetadata = $this->_em->getClassMetadata($deleteClause->getAbstractSchemaName());
// Building queryComponent
$queryComponent = array(
'metadata' => $classMetadata,
'parent' => null,
'relation' => null,
'map' => null,
'scalar' => null,
);
$this->_parserResult->setQueryComponent($this->_lexer->token['value'], $queryComponent);
}
return $deleteClause;
}
/**
@ -496,17 +615,23 @@ class Parser
if ($peek['value'] != '.' && $this->_lexer->lookahead['type'] === Lexer::T_IDENTIFIER) {
$expression = $this->_IdentificationVariable();
} else if (($isFunction = $this->_isFunction()) !== false || $this->_isSubselect()) {
$expression = $isFunction ? $this->_AggregateExpression() : $this->_Subselect();
if ($isFunction) {
$expression = $this->_AggregateExpression();
} else {
$this->match('(');
$expression = $this->_Subselect();
$this->match(')');
}
if ($this->_lexer->isNextToken(Lexer::T_AS)) {
$this->match(Lexer::T_AS);
$fieldIdentificationVariable = $this->_FieldAliasIdentificationVariable();
} else if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
$fieldIdentificationVariable = $this->_FieldAliasIdentificationVariable();
}
if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
$this->match(Lexer::T_IDENTIFIER);
$fieldIdentificationVariable = $this->_lexer->token['value'];
}
} else {
$expression = $this->_PathExpressionInSelect();
}
return new AST\SelectExpression($expression, $fieldIdentificationVariable);
}
@ -835,14 +960,23 @@ class Parser
// For now we only support a PathExpression here...
$pathExp = $this->_PathExpression();
$this->match(')');
} else if ($this->_lexer->isNextToken(Lexer::T_AVG)) {
} else {
if ($this->_lexer->isNextToken(Lexer::T_AVG)) {
$this->match(Lexer::T_AVG);
$functionName = $this->_lexer->token['value'];
$this->match('(');
//...
} else if ($this->_lexer->isNextToken(Lexer::T_MAX)) {
$this->match(Lexer::T_MAX);
} else if ($this->_lexer->isNextToken(Lexer::T_MIN)) {
$this->match(Lexer::T_MIN);
} else if ($this->_lexer->isNextToken(Lexer::T_SUM)) {
$this->match(Lexer::T_SUM);
} else {
$this->syntaxError('One of: MAX, MIN, AVG, SUM, COUNT');
}
$functionName = $this->_lexer->token['value'];
$this->match('(');
$pathExp = $this->_StateFieldPathExpression();
$this->match(')');
}
return new AST\AggregateExpression($functionName, $pathExp, $isDistinct);
}
@ -863,6 +997,49 @@ class Parser
return new AST\GroupByClause($groupByItems);
}
/**
* HavingClause ::= "HAVING" ConditionalExpression
*/
private function _HavingClause()
{
$this->match(Lexer::T_HAVING);
return new AST\HavingClause($this->_ConditionalExpression());
}
/**
* OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}*
*/
private function _OrderByClause()
{
$this->match(Lexer::T_ORDER);
$this->match(Lexer::T_BY);
$orderByItems = array();
$orderByItems[] = $this->_OrderByItem();
while ($this->_lexer->isNextToken(',')) {
$this->match(',');
$orderByItems[] = $this->_OrderByItem();
}
return new AST\OrderByClause($orderByItems);
}
/**
* OrderByItem ::= StateFieldPathExpression ["ASC" | "DESC"]
*/
private function _OrderByItem()
{
$item = new AST\OrderByItem($this->_StateFieldPathExpression());
if ($this->_lexer->isNextToken(Lexer::T_ASC)) {
$this->match(Lexer::T_ASC);
$item->setAsc(true);
} else if ($this->_lexer->isNextToken(Lexer::T_DESC)) {
$this->match(Lexer::T_DESC);
$item->setDesc(true);
} else {
$item->setDesc(true);
}
return $item;
}
/**
* WhereClause ::= "WHERE" ConditionalExpression
*/
@ -1000,9 +1177,9 @@ class Parser
default:
$this->syntaxError();
}
} else if ($token['value'] == '(') {
return $this->_ComparisonExpression();
} else {
return $this->_ComparisonExpression();
/*} else {
switch ($token['type']) {
case Lexer::T_INTEGER:
// IF it turns out its a ComparisonExpression, then it MUST be ArithmeticExpression
@ -1012,7 +1189,7 @@ class Parser
break;
default:
$this->syntaxError();
}
}*/
}
}
@ -1226,7 +1403,7 @@ class Parser
}
/**
* SimpleSelectExpression ::= SingleValuedPathExpression | IdentificationVariable | AggregateExpression
* SimpleSelectExpression ::= StateFieldPathExpression | IdentificationVariable | (AggregateExpression [["AS"] FieldAliasIdentificationVariable])
*/
private function _SimpleSelectExpression()
{
@ -1234,13 +1411,21 @@ class Parser
// SingleValuedPathExpression | IdentificationVariable
$peek = $this->_lexer->glimpse();
if ($peek['value'] == '.') {
return $this->_PathExpressionInSelect();
return new AST\SimpleSelectExpression($this->_PathExpressionInSelect());
} else {
$this->match($this->_lexer->lookahead['value']);
return $this->_lexer->token['value'];
return new AST\SimpleSelectExpression($this->_lexer->token['value']);
}
} else {
return $this->_AggregateExpression();
$expr = new AST\SimpleSelectExpression($this->_AggregateExpression());
if ($this->_lexer->isNextToken(Lexer::T_AS)) {
$this->match(Lexer::T_AS);
}
if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
$this->match(Lexer::T_IDENTIFIER);
$expr->setFieldIdentificationVariable($this->_lexer->token['value']);
}
return $expr;
}
}
@ -1263,6 +1448,28 @@ class Parser
}
}
/**
* BetweenExpression ::= ArithmeticExpression ["NOT"] "BETWEEN" ArithmeticExpression "AND" ArithmeticExpression
*/
private function _BetweenExpression()
{
$not = false;
$arithExpr1 = $this->_ArithmeticExpression();
if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
$this->match(Lexer::T_NOT);
$not = true;
}
$this->match(Lexer::T_BETWEEN);
$arithExpr2 = $this->_ArithmeticExpression();
$this->match(Lexer::T_AND);
$arithExpr3 = $this->_ArithmeticExpression();
$betweenExpr = new AST\BetweenExpression($arithExpr1, $arithExpr2, $arithExpr3);
$betweenExpr->setNot($not);
return $betweenExpr;
}
/**
* ArithmeticPrimary ::= StateFieldPathExpression | Literal | "(" SimpleArithmeticExpression ")" | Function | AggregateExpression
*/
@ -1274,13 +1481,11 @@ class Parser
$this->match(')');
return $expr;
}
switch ($this->_lexer->lookahead['type']) {
case Lexer::T_IDENTIFIER:
$peek = $this->_lexer->glimpse();
if ($peek['value'] == '(') {
if ($this->_isAggregateFunction($peek['type'])) {
return $this->_AggregateExpression();
}
return $this->_FunctionsReturningStrings();
}
return $this->_StateFieldPathExpression();
@ -1293,8 +1498,16 @@ class Parser
$this->match($this->_lexer->lookahead['value']);
return $this->_lexer->token['value'];
default:
$peek = $this->_lexer->glimpse();
if ($peek['value'] == '(') {
if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) {
return $this->_AggregateExpression();
}
return $this->_FunctionsReturningStrings();
} else {
$this->syntaxError();
}
}
throw \Doctrine\Common\DoctrineException::updateMe("Not yet implemented.");
//TODO...
}
@ -1402,9 +1615,8 @@ class Parser
$escapeChar = null;
if ($this->_lexer->lookahead['type'] === Lexer::T_ESCAPE) {
$this->match(Lexer::T_ESCAPE);
var_dump($this->_lexer->lookahead);
//$this->match(Lexer::T_)
//$escapeChar =
$this->match(Lexer::T_STRING);
$escapeChar = $this->_lexer->token['value'];
}
return new AST\LikeExpression($stringExpr, $stringPattern, $isNot, $escapeChar);
}
@ -1438,9 +1650,11 @@ class Parser
$this->syntaxError("'.' or '('");
}
} else if ($this->_lexer->lookahead['type'] === Lexer::T_STRING) {
//TODO...
$this->match(Lexer::T_STRING);
return $this->_lexer->token['value'];
} else if ($this->_lexer->lookahead['type'] === Lexer::T_INPUT_PARAMETER) {
//TODO...
$this->match(Lexer::T_INPUT_PARAMETER);
return new AST\InputParameter($this->_lexer->token['value']);
} else {
$this->syntaxError('StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression');
}

View File

@ -181,27 +181,16 @@ class SqlWalker
}
else if ($selectExpression->getExpression() instanceof AST\AggregateExpression) {
$aggExpr = $selectExpression->getExpression();
if ( ! $selectExpression->getFieldIdentificationVariable()) {
$alias = $this->_scalarAliasCounter++;
} else {
$alias = $selectExpression->getFieldIdentificationVariable();
}
$parts = $aggExpr->getPathExpression()->getParts();
$dqlAlias = $parts[0];
$fieldName = $parts[1];
$qComp = $this->_parserResult->getQueryComponent($dqlAlias);
$columnName = $qComp['metadata']->getColumnName($fieldName);
$sql .= $aggExpr->getFunctionName() . '(';
if ($aggExpr->isDistinct()) $sql .= 'DISTINCT ';
$sql .= $this->_dqlToSqlAliasMap[$dqlAlias] . '.' . $columnName;
$sql .= ') AS dctrn__' . $alias;
$sql .= $this->walkAggregateExpression($aggExpr) . ' AS dctrn__' . $alias;
}
//TODO: else if Subselect
else {
else if ($selectExpression->getExpression() instanceof AST\Subselect) {
$sql .= $this->walkSubselect($selectExpression->getExpression());
} else {
$dqlAlias = $selectExpression->getExpression();
$queryComp = $this->_parserResult->getQueryComponent($dqlAlias);
$class = $queryComp['metadata'];
@ -221,6 +210,80 @@ class SqlWalker
return $sql;
}
public function walkSubselect($subselect)
{
$sql = $this->walkSimpleSelectClause($subselect->getSimpleSelectClause());
$sql .= $this->walkSubselectFromClause($subselect->getSubselectFromClause());
$sql .= $subselect->getWhereClause() ? $this->walkWhereClause($subselect->getWhereClause()) : '';
$sql .= $subselect->getGroupByClause() ? $this->walkGroupByClause($subselect->getGroupByClause()) : '';
//... more clauses
return $sql;
}
public function walkSubselectFromClause($subselectFromClause)
{
$sql = ' FROM ';
$identificationVarDecls = $subselectFromClause->getSubselectIdentificationVariableDeclarations();
$firstIdentificationVarDecl = $identificationVarDecls[0];
$rangeDecl = $firstIdentificationVarDecl->getRangeVariableDeclaration();
$sql .= $rangeDecl->getClassMetadata()->getTableName() . ' '
. $this->_dqlToSqlAliasMap[$rangeDecl->getAliasIdentificationVariable()];
foreach ($firstIdentificationVarDecl->getJoinVariableDeclarations() as $joinVarDecl) {
$sql .= $this->walkJoinVariableDeclaration($joinVarDecl);
}
return $sql;
}
public function walkSimpleSelectClause($simpleSelectClause)
{
$sql = 'SELECT';
if ($simpleSelectClause->isDistinct()) {
$sql .= ' DISTINCT';
}
$sql .= $this->walkSimpleSelectExpression($simpleSelectClause->getSimpleSelectExpression());
return $sql;
}
public function walkSimpleSelectExpression($simpleSelectExpression)
{
$sql = '';
$expr = $simpleSelectExpression->getExpression();
if ($expr instanceof AST\PathExpression) {
//...
} else if ($expr instanceof AST\AggregateExpression) {
if ( ! $simpleSelectExpression->getFieldIdentificationVariable()) {
$alias = $this->_scalarAliasCounter++;
} else {
$alias = $simpleSelectExpression->getFieldIdentificationVariable();
}
$sql .= $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias;
} else {
// $expr is IdentificationVariable
//...
}
return $sql;
}
public function walkAggregateExpression($aggExpression)
{
$sql = '';
$parts = $aggExpression->getPathExpression()->getParts();
$dqlAlias = $parts[0];
$fieldName = $parts[1];
$qComp = $this->_parserResult->getQueryComponent($dqlAlias);
$columnName = $qComp['metadata']->getColumnName($fieldName);
$sql .= $aggExpression->getFunctionName() . '(';
if ($aggExpression->isDistinct()) $sql .= 'DISTINCT ';
$sql .= $this->_dqlToSqlAliasMap[$dqlAlias] . '.' . $columnName;
$sql .= ')';
return $sql;
}
public function walkGroupByClause($groupByClause)
{
return ' GROUP BY '

View File

@ -112,111 +112,41 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
{
$this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE EXISTS (SELECT p.id FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.phonenumber = 1234)');
}
/*
public function testNotExistsExpressionSupportedInWherePart()
{
$this->assertValidDql('SELECT u.* FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE NOT EXISTS (SELECT p.user_id FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.user_id = u.id)');
}
public function testLiteralValueAsInOperatorOperandIsSupported()
{
$this->assertValidDql('SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE 1 IN (1, 2)');
}
public function testUpdateWorksWithOneColumn()
{
$this->assertValidDql("UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = 'someone'");
}
public function testUpdateWorksWithMultipleColumns()
{
$this->assertValidDql("UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = 'someone', u.username = 'some'");
}
public function testUpdateSupportsConditions()
{
$this->assertValidDql("UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = 'someone' WHERE u.id = 5");
}
public function testDeleteAll()
{
$this->assertValidDql('DELETE FROM Doctrine\Tests\Models\CMS\CmsUser');
}
public function testDeleteWithCondition()
{
$this->assertValidDql('DELETE FROM Doctrine\Tests\Models\CMS\CmsUser WHERE id = 3');
}
public function testAdditionExpression()
{
$this->assertValidDql('SELECT u.*, (u.id + u.id) addition FROM Doctrine\Tests\Models\CMS\CmsUser u');
}
public function testSubtractionExpression()
{
$this->assertValidDql('SELECT u.*, (u.id - u.id) subtraction FROM Doctrine\Tests\Models\CMS\CmsUser u');
}
public function testDivisionExpression()
{
$this->assertValidDql('SELECT u.*, (u.id/u.id) division FROM Doctrine\Tests\Models\CMS\CmsUser u');
}
public function testMultiplicationExpression()
{
$this->assertValidDql('SELECT u.*, (u.id * u.id) multiplication FROM Doctrine\Tests\Models\CMS\CmsUser u');
}
public function testNegationExpression()
{
$this->assertValidDql('SELECT u.*, -u.id negation FROM Doctrine\Tests\Models\CMS\CmsUser u');
}
public function testExpressionWithPrecedingPlusSign()
{
$this->assertValidDql('SELECT u.*, +u.id FROM CmsUser u');
$this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE NOT EXISTS (SELECT p.id FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.phonenumber = 1234)');
}
public function testAggregateFunctionInHavingClause()
{
$this->assertValidDql('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.phonenumbers p HAVING COUNT(p.phonenumber) > 2');
$this->assertValidDql("SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.phonenumbers p HAVING MAX(u.name) = 'zYne'");
}
public function testMultipleAggregateFunctionsInHavingClause()
{
$this->assertValidDql("SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.phonenumbers p HAVING MAX(u.name) = 'zYne'");
$this->assertValidDql("SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.phonenumbers p HAVING MAX(u.name) = 'romanb'");
}
public function testLeftJoin()
{
$this->assertValidDql('SELECT u.*, p.* FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.phonenumbers p');
$this->assertValidDql('SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.phonenumbers p');
}
public function testJoin()
{
$this->assertValidDql('SELECT u.* FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.phonenumbers');
$this->assertValidDql('SELECT u,p FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.phonenumbers p');
}
public function testInnerJoin()
{
$this->assertValidDql('SELECT u.*, u.phonenumbers.* FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.phonenumbers');
$this->assertValidDql('SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.phonenumbers p');
}
public function testMultipleLeftJoin()
{
$this->assertValidDql('SELECT u.articles.*, u.phonenumbers.* FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles LEFT JOIN u.phonenumbers');
$this->assertValidDql('SELECT u, a, p FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a LEFT JOIN u.phonenumbers p');
}
public function testMultipleInnerJoin()
{
$this->assertValidDql('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles INNER JOIN u.phonenumbers');
}
public function testMultipleInnerJoin2()
{
$this->assertValidDql('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles, u.phonenumbers');
$this->assertValidDql('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a INNER JOIN u.phonenumbers p');
}
public function testMixingOfJoins()
@ -224,11 +154,6 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
$this->assertValidDql('SELECT u.name, a.topic, p.phonenumber FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a LEFT JOIN u.phonenumbers p');
}
public function testMixingOfJoins2()
{
$this->assertInvalidDql('SELECT u.name, u.articles.topic, c.text FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles.comments c');
}
public function testOrderBySingleColumn()
{
$this->assertValidDql('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY u.name');
@ -249,31 +174,134 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
$this->assertValidDql('SELECT u.name, u.username FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY u.username DESC, u.name DESC');
}
public function testOrderByWithFunctionExpression()
{
$this->assertValidDql('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY COALESCE(u.id, u.name) DESC');
}*/
/*
public function testSubselectInInExpression()
{
$this->assertValidDql("SELECT * FROM CmsUser u WHERE u.id NOT IN (SELECT u2.id FROM CmsUser u2 WHERE u2.name = 'zYne')");
$this->assertValidDql("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id NOT IN (SELECT u2.id FROM Doctrine\Tests\Models\CMS\CmsUser u2 WHERE u2.name = 'zYne')");
}
public function testSubselectInSelectPart()
{
// Semantical error: Unknown query component u (probably in subselect)
$this->assertValidDql("SELECT u.name, (SELECT COUNT(p.phonenumber) FROM CmsPhonenumber p WHERE p.user_id = u.id) pcount FROM CmsUser u WHERE u.name = 'zYne' LIMIT 1");
$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()
{
$this->assertValidDql('SELECT * FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?');
$this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?');
}
public function testNamedInputParameter()
{
$this->assertValidDql('SELECT * FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = :id');
}*/
$this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = :id');
}
public function testJoinConditionsSupported()
{
$this->assertValidDql("SELECT u.name, p FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.phonenumbers p ON p.phonenumber = '123 123'");
}
public function testIndexByClauseWithOneComponent()
{
$this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u INDEX BY u.id');
}
public function testIndexBySupportsJoins()
{
$this->assertValidDql('SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a INDEX BY a.id'); // INDEX BY is now referring to articles
}
public function testIndexBySupportsJoins2()
{
$this->assertValidDql('SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u INDEX BY u.id LEFT JOIN u.phonenumbers p INDEX BY p.phonenumber');
}
public function testBetweenExpressionSupported()
{
$this->assertValidDql("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name BETWEEN 'jepso' AND 'zYne'");
}
public function testNotBetweenExpressionSupported()
{
$this->assertValidDql("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name NOT BETWEEN 'jepso' AND 'zYne'");
}
public function testLikeExpression()
{
$this->assertValidDql("SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name LIKE 'z%'");
}
public function testNotLikeExpression()
{
$this->assertValidDql("SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name NOT LIKE 'z%'");
}
public function testLikeExpressionWithCustomEscapeCharacter()
{
$this->assertValidDql("SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name LIKE 'z|%' ESCAPE '|'");
}
public function testImplicitJoinInWhereOnSingleValuedAssociationPathExpression()
{
// This should be allowed because avatar is a single-value association.
// SQL: SELECT ... FROM forum_user fu INNER JOIN forum_avatar fa ON fu.avatar_id = fa.id WHERE fa.id = ?
$this->assertValidDql("SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u WHERE u.avatar.id = ?");
}
public function testImplicitJoinInWhereOnCollectionValuedPathExpression()
{
// This should be forbidden, because articles is a collection
$this->assertInvalidDql("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.articles.title = ?");
}
public function testInvalidSyntaxIsRejected()
{
$this->assertInvalidDql("FOOBAR CmsUser");
//$this->assertInvalidDql("DELETE FROM Doctrine\Tests\Models\CMS\CmsUser.articles");
//$this->assertInvalidDql("DELETE FROM Doctrine\Tests\Models\CMS\CmsUser cu WHERE cu.articles.id > ?");
$this->assertInvalidDql("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.articles.comments");
// Currently UNDEFINED OFFSET error
$this->assertInvalidDql("SELECT c FROM CmsUser.articles.comments c");
}
public function testUpdateWorksWithOneField()
{
$this->assertValidDql("UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = 'someone'");
}
public function testUpdateWorksWithMultipleFields()
{
$this->assertValidDql("UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = 'someone', u.username = 'some'");
}
public function testUpdateSupportsConditions()
{
$this->assertValidDql("UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = 'someone' WHERE u.id = 5");
}
public function testDeleteAll()
{
$this->assertValidDql('DELETE FROM Doctrine\Tests\Models\CMS\CmsUser');
}
public function testDeleteWithCondition()
{
$this->assertValidDql('DELETE FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = 3');
}
/**
* TODO: Hydration can't deal with this currently but it should be allowed.
* Also, generated SQL looks like: "... FROM cms_user, cms_article ..." which
* may not work on all dbms.
*
* The main use case for this generalized style of join is when a join condition
* does not involve a foreign key relationship that is mapped to an entity relationship.
*/
public function testImplicitJoinWithCartesianProductAndConditionInWhere()
{
$this->assertValidDql("SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsArticle a WHERE u.name = a.topic");
}
/*
public function testCustomJoinsAndWithKeywordSupported()
{
@ -281,37 +309,6 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
$this->assertValidDql('SELECT c.*, c2.*, d.* FROM Record_Country c INNER JOIN c.City c2 WITH c2.id = 2 WHERE c.id = 1');
}
*/
/*
public function testJoinConditionsSupported()
{
$this->assertValidDql("SELECT u.name, p.* FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.phonenumbers p ON p.phonenumber = '123 123'");
}
public function testIndexByClauseWithOneComponent()
{
$this->assertValidDql('SELECT * FROM Doctrine\Tests\Models\CMS\CmsUser u INDEX BY id');
}
public function testIndexBySupportsJoins()
{
$this->assertValidDql('SELECT u.*, u.articles.* FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles INDEX BY id'); // INDEX BY is now referring to articles
}
public function testIndexBySupportsJoins2()
{
$this->assertValidDql('SELECT u.*, p.* FROM Doctrine\Tests\Models\CMS\CmsUser u INDEX BY id LEFT JOIN u.phonenumbers p INDEX BY phonenumber');
}
public function testBetweenExpressionSupported()
{
$this->assertValidDql("SELECT * FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name BETWEEN 'jepso' AND 'zYne'");
}
public function testNotBetweenExpressionSupported()
{
$this->assertValidDql("SELECT * FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name NOT BETWEEN 'jepso' AND 'zYne'");
}
*/
/* public function testAllExpressionWithCorrelatedSubquery()
{
// We need existant classes here, otherwise semantical will always fail
@ -329,62 +326,5 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
// We need existant classes here, otherwise semantical will always fail
$this->assertValidDql('SELECT * FROM Employee e WHERE e.salary > SOME (SELECT m.salary FROM Manager m WHERE m.department = e.department)');
}
*//*
public function testLikeExpression()
{
$this->assertValidDql("SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name LIKE 'z%'");
}
public function testNotLikeExpression()
{
$this->assertValidDql("SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name NOT LIKE 'z%'");
}
public function testLikeExpressionWithCustomEscapeCharacter()
{
$this->assertValidDql("SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name LIKE 'z|%' ESCAPE '|'");
}
*/
/**
* TODO: Hydration can't deal with this currently but it should be allowed.
* Also, generated SQL looks like: "... FROM cms_user, cms_article ..." which
* may not work on all dbms.
*
* The main use case for this generalized style of join is when a join condition
* does not involve a foreign key relationship that is mapped to an entity relationship.
*/
/* public function testImplicitJoinWithCartesianProductAndConditionInWhere()
{
$this->assertValidDql("SELECT u.*, a.* FROM Doctrine\Tests\Models\CMS\CmsUser u, CmsArticle a WHERE u.name = a.topic");
}
public function testImplicitJoinInWhereOnSingleValuedAssociationPathExpression()
{
// This should be allowed because avatar is a single-value association.
// SQL: SELECT ... FROM forum_user fu INNER JOIN forum_avatar fa ON fu.avatar_id = fa.id WHERE fa.id = ?
//$this->assertValidDql("SELECT u.* FROM ForumUser u WHERE u.avatar.id = ?");
}
public function testImplicitJoinInWhereOnCollectionValuedPathExpression()
{
// This should be forbidden, because articles is a collection
$this->assertInvalidDql("SELECT u.* FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.articles.title = ?");
}
public function testInvalidSyntaxIsRejected()
{
$this->assertInvalidDql("FOOBAR CmsUser");
$this->assertInvalidDql("DELETE FROM Doctrine\Tests\Models\CMS\CmsUser.articles");
$this->assertInvalidDql("DELETE FROM Doctrine\Tests\Models\CMS\CmsUser cu WHERE cu.articles.id > ?");
$this->assertInvalidDql("SELECT user FROM Doctrine\Tests\Models\CMS\CmsUser user");
// Error message here is: Relation 'comments' does not exist in component 'CmsUser'
// This means it is intepreted as:
// SELECT u.* FROM CmsUser u JOIN u.articles JOIN u.comments
// This seems wrong. "JOIN u.articles.comments" should not be allowed.
$this->assertInvalidDql("SELECT u.* FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.articles.comments");
// Currently UNDEFINED OFFSET error
//$this->assertInvalidDql("SELECT * FROM CmsUser.articles.comments");
}*/
}