From bffd76d704fbdd733dbb7fe394027041fe17c2ae Mon Sep 17 00:00:00 2001 From: romanb Date: Mon, 16 Mar 2009 22:12:38 +0000 Subject: [PATCH] [2.0] Parser work. --- .../ORM/Query/AST/ExistsExpression.php | 34 +++++ lib/Doctrine/ORM/Query/AST/FromClause.php | 25 ---- .../ORM/Query/AST/SimpleSelectClause.php | 58 +++++++++ lib/Doctrine/ORM/Query/AST/Subselect.php | 98 ++++++++++++++ .../ORM/Query/AST/SubselectFromClause.php | 47 +++++++ lib/Doctrine/ORM/Query/Parser.php | 121 +++++++++++++++++- .../ORM/Query/LanguageRecognitionTest.php | 6 +- 7 files changed, 358 insertions(+), 31 deletions(-) create mode 100644 lib/Doctrine/ORM/Query/AST/ExistsExpression.php create mode 100644 lib/Doctrine/ORM/Query/AST/SimpleSelectClause.php create mode 100644 lib/Doctrine/ORM/Query/AST/Subselect.php create mode 100644 lib/Doctrine/ORM/Query/AST/SubselectFromClause.php diff --git a/lib/Doctrine/ORM/Query/AST/ExistsExpression.php b/lib/Doctrine/ORM/Query/AST/ExistsExpression.php new file mode 100644 index 000000000..f532e6f02 --- /dev/null +++ b/lib/Doctrine/ORM/Query/AST/ExistsExpression.php @@ -0,0 +1,34 @@ +_subselect = $subselect; + } + + public function setNot($bool) + { + $this->_not = $bool; + } + + public function getNot() + { + return $this->_not; + } +} + diff --git a/lib/Doctrine/ORM/Query/AST/FromClause.php b/lib/Doctrine/ORM/Query/AST/FromClause.php index 14bbb8563..fff544fd6 100644 --- a/lib/Doctrine/ORM/Query/AST/FromClause.php +++ b/lib/Doctrine/ORM/Query/AST/FromClause.php @@ -44,29 +44,4 @@ class FromClause extends Node { return $this->_identificationVariableDeclarations; } - - /* REMOVE ME LATER. COPIED METHODS FROM SPLIT OF PRODUCTION INTO "AST" AND "PARSER" */ - public function buildSql() - { - //echo "FromClause:\n"; - //for ($i = 0; $i < count($this->_identificationVariableDeclaration);$i++) { - // echo (($this->_identificationVariableDeclaration[$i] instanceof IdentificationVariableDeclaration) - // ? get_class($this->_identificationVariableDeclaration[$i]) - // : get_class($this->_identificationVariableDeclaration[$i])) . "\n"; - //} - - return 'FROM ' . implode(', ', $this->_mapIdentificationVariableDeclarations()); - } - - protected function _mapIdentificationVariableDeclarations() - { - return array_map( - array(&$this, '_mapIdentificationVariableDeclaration'), $this->_identificationVariableDeclarations - ); - } - - protected function _mapIdentificationVariableDeclaration($value) - { - return $value->buildSql(); - } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/AST/SimpleSelectClause.php b/lib/Doctrine/ORM/Query/AST/SimpleSelectClause.php new file mode 100644 index 000000000..3f14b956f --- /dev/null +++ b/lib/Doctrine/ORM/Query/AST/SimpleSelectClause.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SimpleSelectClause ::= "SELECT" [DISTINCT"] SimpleSelectExpression + * + * @author Guilherme Blanco + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + */ +class SimpleSelectClause extends Node +{ + private $_isDistinct = false; + private $_simpleSelectExpression; + + public function __construct($simpleSelectExpression) + { + $this->_simpleSelectExpression = $simpleSelectExpression; + } + + /* Getters */ + public function isDistinct() + { + return $this->_isDistinct; + } + + public function setDistinct($bool) + { + $this->_isDistinct = $bool; + } + + public function getSimpleSelectExpression() + { + return $this->_simpleSelectExpression; + } +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/AST/Subselect.php b/lib/Doctrine/ORM/Query/AST/Subselect.php new file mode 100644 index 000000000..32dbe1e41 --- /dev/null +++ b/lib/Doctrine/ORM/Query/AST/Subselect.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + * + * @author Guilherme Blanco + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + */ +class Subselect extends Node +{ + private $_simpleSelectClause; + private $_subselectFromClause; + private $_whereClause; + private $_groupByClause; + private $_havingClause; + private $_orderByClause; + + public function __construct($simpleSelectClause, $subselectFromClause) + { + $this->_simpleSelectClause = $simpleSelectClause; + $this->_subselectFromClause = $subselectFromClause; + } + + /* Getters */ + public function getSimpleSelectClause() + { + return $this->_simpleSelectClause; + } + + public function getSubselectFromClause() + { + return $this->_subselectFromClause; + } + + public function getWhereClause() + { + return $this->_whereClause; + } + + public function setWhereClause($whereClause) + { + $this->_whereClause = $whereClause; + } + + public function getGroupByClause() + { + return $this->_groupByClause; + } + + public function setGroupByClause($groupByClause) + { + $this->_groupByClause = $groupByClause; + } + + public function getHavingClause() + { + return $this->_havingClause; + } + + public function setHavingClause($havingClause) + { + $this->_havingClause = $havingClause; + } + + public function getOrderByClause() + { + return $this->_orderByClause; + } + + public function setOrderByClause($orderByClause) + { + $this->_orderByClause = $orderByClause; + } +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/AST/SubselectFromClause.php b/lib/Doctrine/ORM/Query/AST/SubselectFromClause.php new file mode 100644 index 000000000..baea127d1 --- /dev/null +++ b/lib/Doctrine/ORM/Query/AST/SubselectFromClause.php @@ -0,0 +1,47 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}* + * + * @author Guilherme Blanco + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + */ +class SubselectFromClause extends Node +{ + private $_identificationVariableDeclarations = array(); + + public function __construct(array $identificationVariableDeclarations) + { + $this->_identificationVariableDeclarations = $identificationVariableDeclarations; + } + + /* Getters */ + public function geSubselectIdentificationVariableDeclarations() + { + return $this->_identificationVariableDeclarations; + } +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 22124ad05..90cc809bd 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -384,7 +384,8 @@ class Parser if (count($parts) == 2) { $expr->setIsSimpleStateFieldPathExpression(true); if ( ! $qComps[$dqlAlias]['metadata']->hasField($parts[1])) { - $this->syntaxError(); + $this->semanticalError('The class ' . $qComps[$dqlAlias]['metadata']->getClassName() + . ' has no simple state field named ' . $parts[1]); } } else { $embeddedClassFieldSeen = false; @@ -417,7 +418,8 @@ class Parser } // Last part MUST be a simple state field if ( ! $qComps[$dqlAlias]['metadata']->hasField($parts[$numParts-1])) { - $this->syntaxError(); + $this->semanticalError('The class ' . $qComps[$dqlAlias]['metadata']->getClassName() + . ' has no simple state field named ' . $parts[$numParts-1]); } } } @@ -796,7 +798,8 @@ class Parser $class = $this->_em->getClassMetadata($assoc->getTargetEntityName()); $assocSeen = true; } else { - $this->syntaxError(); + $this->semanticalError('The class ' . $class->getClassName() . + ' has no field or association named ' . $part); } $parts[] = $part; } @@ -1129,6 +1132,118 @@ class Parser return $inExpression; } + /** + * ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")" + */ + private function _ExistsExpression() + { + $not = false; + if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $not = true; + } + $this->match(Lexer::T_EXISTS); + $this->match('('); + $existsExpression = new AST\ExistsExpression($this->_Subselect()); + $this->match(')'); + $existsExpression->setNot($not); + return $existsExpression; + } + + /** + * Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + */ + private function _Subselect() + { + $subselect = new AST\Subselect($this->_SimpleSelectClause(), $this->_SubselectFromClause()); + + $subselect->setWhereClause( + $this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->_WhereClause() : null + ); + + $subselect->setGroupByClause( + $this->_lexer->isNextToken(Lexer::T_GROUP) ? $this->_GroupByClause() : null + ); + + $subselect->setHavingClause( + $this->_lexer->isNextToken(Lexer::T_HAVING) ? $this->_HavingClause() : null + ); + + $subselect->setOrderByClause( + $this->_lexer->isNextToken(Lexer::T_ORDER) ? $this->_OrderByClause() : null + ); + + return $subselect; + } + + /** + * SimpleSelectClause ::= "SELECT" ["DISTINCT"] SimpleSelectExpression + */ + private function _SimpleSelectClause() + { + $distinct = false; + $this->match(Lexer::T_SELECT); + if ($this->_lexer->isNextToken(Lexer::T_DISTINCT)) { + $this->match(Lexer::T_DISTINCT); + $distinct = true; + } + $simpleSelectClause = new AST\SimpleSelectClause($this->_SimpleSelectExpression()); + $simpleSelectClause->setDistinct($distinct); + return $simpleSelectClause; + } + + /** + * SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}* + */ + private function _SubselectFromClause() + { + $this->match(Lexer::T_FROM); + $identificationVariables = array(); + $identificationVariables[] = $this->_SubselectIdentificationVariableDeclaration(); + while ($this->_lexer->isNextToken(',')) { + $this->match(','); + $identificationVariables[] = $this->_SubselectIdentificationVariableDeclaration(); + } + return new AST\SubselectFromClause($identificationVariables); + } + + /** + * SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration | (AssociationPathExpression ["AS"] AliasIdentificationVariable) + */ + private function _SubselectIdentificationVariableDeclaration() + { + $peek = $this->_lexer->glimpse(); + if ($peek['value'] == '.') { + $subselectIdentificationVarDecl = new AST\SubselectIdentificationVariableDeclaration; + $subselectIdentificationVarDecl->setAssociationPathExpression($this->_AssociationPathExpression()); + $this->match(Lexer::T_AS); + $this->match(Lexer::T_IDENTIFIER); + $subselectIdentificationVarDecl->setAliasIdentificationVariable($this->_lexer->token['value']); + return $subselectIdentificationVarDecl; + } else { + return $this->_IdentificationVariableDeclaration(); + } + } + + /** + * SimpleSelectExpression ::= SingleValuedPathExpression | IdentificationVariable | AggregateExpression + */ + private function _SimpleSelectExpression() + { + if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) { + // SingleValuedPathExpression | IdentificationVariable + $peek = $this->_lexer->glimpse(); + if ($peek['value'] == '.') { + return $this->_PathExpressionInSelect(); + } else { + $this->match($this->_lexer->lookahead['value']); + return $this->_lexer->token['value']; + } + } else { + return $this->_AggregateExpression(); + } + } + /** * Literal ::= string | char | integer | float | boolean | InputParameter */ diff --git a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php index 0347feeb7..f275c4836 100644 --- a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php +++ b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php @@ -107,12 +107,12 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase { $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id NOT IN (1)'); } -/* + public function testExistsExpressionSupportedInWherePart() { - $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE EXISTS (SELECT p.user_id FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.user_id = u.id)'); + $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)');