1
0
mirror of synced 2024-12-13 14:56:01 +03:00

updates to the lexer of the new dql parser

This commit is contained in:
jepso 2008-01-20 16:29:01 +00:00
parent 3fcf1fb818
commit ec4ea31da8
17 changed files with 440 additions and 67 deletions

View File

@ -56,6 +56,20 @@ abstract class Doctrine_Query_Production
return ($la['type'] === $token || $la['value'] === $token);
}
protected function _isFunction()
{
$la = $this->_parser->lookahead;
$next = $this->_parser->getScanner()->peek();
return ($la['type'] === Doctrine_Query_Token::T_IDENTIFIER && $next['value'] === '(');
}
protected function _isSubselect()
{
$la = $this->_parser->lookahead;
$next = $this->_parser->getScanner()->peek();
return ($la['value'] === '(' && $next['type'] === Doctrine_Query_Token::T_SELECT);
}
/**
* Executes a production with specified name and parameters.
*

View File

@ -32,19 +32,11 @@
*/
class Doctrine_Query_Production_ComparisonExpression extends Doctrine_Query_Production
{
private function _isSubquery()
{
$lookahead = $this->_parser->lookahead;
$next = $this->_parser->getScanner()->peek();
return $lookahead['value'] === '(' && $next['type'] === Doctrine_Query_Token::T_SELECT;
}
public function execute(array $params = array())
{
$this->ComparisonOperator();
if ($this->_isSubquery()) {
if ($this->_isSubselect()) {
$this->_parser->match('(');
$this->Subselect();
$this->_parser->match(')');

View File

@ -32,9 +32,52 @@
*/
class Doctrine_Query_Production_ConditionalPrimary extends Doctrine_Query_Production
{
private function _isConditionalExpression()
{
$token = $this->_parser->lookahead;
$parenthesis = 0;
if ($token['value'] === '(') {
$parenthesis++;
}
while ($parenthesis > 0) {
$token = $this->_parser->getScanner()->peek();
if ($token['value'] === '(') {
$parenthesis++;
} elseif ($token['value'] === ')') {
$parenthesis--;
} else {
switch ($token['type']) {
case Doctrine_Query_Token::T_NOT:
case Doctrine_Query_Token::T_AND:
case Doctrine_Query_Token::T_OR:
case Doctrine_Query_Token::T_BETWEEN:
case Doctrine_Query_Token::T_LIKE:
case Doctrine_Query_Token::T_IN:
case Doctrine_Query_Token::T_IS:
case Doctrine_Query_Token::T_EXISTS:
return true;
case Doctrine_Query_Token::T_NONE:
switch ($token['value']) {
case '=':
case '<':
case '>':
return true;
}
break;
}
}
}
return false;
}
public function execute(array $params = array())
{
if ($this->_isNextToken('(')) {
if ($this->_isConditionalExpression()) {
$this->_parser->match('(');
$this->ConditionalExpression();
$this->_parser->match(')');

View File

@ -20,7 +20,7 @@
*/
/**
* DeleteStatement = DeleteClause [WhereClause] [OrderByClause] [LimitClause]
* DeleteStatement = DeleteClause [WhereClause] [OrderByClause] [LimitClause] [OffsetClause]
*
* @package Doctrine
* @subpackage Query
@ -47,5 +47,9 @@ class Doctrine_Query_Production_DeleteStatement extends Doctrine_Query_Productio
if ($this->_isNextToken(Doctrine_Query_Token::T_LIMIT)) {
$this->LimitClause();
}
if ($this->_isNextToken(Doctrine_Query_Token::T_OFFSET)) {
$this->OffsetClause();
}
}
}

View File

@ -20,7 +20,7 @@
*/
/**
* ExistsExpression = ["NOT"] "EXISTS" "(" Subselect ")"
* ExistsExpression = "EXISTS" "(" Subselect ")"
*
* @package Doctrine
* @subpackage Query
@ -34,10 +34,6 @@ class Doctrine_Query_Production_ExistsExpression extends Doctrine_Query_Producti
{
public function execute(array $params = array())
{
if ($this->_isNextToken(Doctrine_Query_Token::T_NOT)) {
$this->_parser->match(Doctrine_Query_Token::T_NOT);
}
$this->_parser->match(Doctrine_Query_Token::T_EXISTS);
$this->_parser->match('(');

View File

@ -20,7 +20,7 @@
*/
/**
* Join = ["LEFT" ["OUTER"] | "INNER"] "JOIN" PathExpression "AS" identifier
* Join = ["LEFT" | "INNER"] "JOIN" RangeVariableDeclaration [("ON" | "WITH") ConditionalExpression] [IndexBy]
*
* @package Doctrine
* @subpackage Query
@ -36,11 +36,6 @@ class Doctrine_Query_Production_Join extends Doctrine_Query_Production
{
if ($this->_isNextToken(Doctrine_Query_Token::T_LEFT)) {
$this->_parser->match(Doctrine_Query_Token::T_LEFT);
if ($this->_isNextToken(Doctrine_Query_Token::T_OUTER)) {
$this->_parser->match(Doctrine_Query_Token::T_OUTER);
}
} elseif ($this->_isNextToken(Doctrine_Query_Token::T_INNER)) {
$this->_parser->match(Doctrine_Query_Token::T_INNER);
}
@ -49,7 +44,16 @@ class Doctrine_Query_Production_Join extends Doctrine_Query_Production
$this->RangeVariableDeclaration();
$this->_parser->match(Doctrine_Query_Token::T_AS);
$this->_parser->match(Doctrine_Query_Token::T_IDENTIFIER);
if ($this->_isNextToken(Doctrine_Query_Token::T_ON)) {
$this->_parser->match(Doctrine_Query_Token::T_ON);
$this->ConditionalExpression();
} elseif ($this->_isNextToken(Doctrine_Query_Token::T_WITH)) {
$this->_parser->match(Doctrine_Query_Token::T_WITH);
$this->ConditionalExpression();
}
if ($this->_isNextToken(Doctrine_Query_Token::T_INDEX)) {
$this->IndexBy();
}
}
}

View File

@ -0,0 +1,40 @@
<?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>.
*/
/**
* LimitClause = "LIMIT" Expression
*
* @package Doctrine
* @subpackage Query
* @author Janne Vanhala <jpvanhal@cc.hut.fi>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://www.phpdoctrine.org
* @since 1.0
* @version $Revision$
*/
class Doctrine_Query_Production_LimitClause extends Doctrine_Query_Production
{
public function execute(array $params = array())
{
$this->_parser->match(Doctrine_Query_Token::T_LIMIT);
$this->Expression();
}
}

View File

@ -0,0 +1,40 @@
<?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>.
*/
/**
* OffsetClause = "OFFSET" Expression
*
* @package Doctrine
* @subpackage Query
* @author Janne Vanhala <jpvanhal@cc.hut.fi>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://www.phpdoctrine.org
* @since 1.0
* @version $Revision$
*/
class Doctrine_Query_Production_OffsetClause extends Doctrine_Query_Production
{
public function execute(array $params = array())
{
$this->_parser->match(Doctrine_Query_Token::T_OFFSET);
$this->Expression();
}
}

View File

@ -20,7 +20,7 @@
*/
/**
* OrderByItem = PathExpression ["ASC" | "DESC"]
* OrderByItem = Expression ["ASC" | "DESC"]
*
* @package Doctrine
* @subpackage Query
@ -34,7 +34,7 @@ class Doctrine_Query_Production_OrderByItem extends Doctrine_Query_Production
{
public function execute(array $params = array())
{
$this->PathExpression();
$this->Expression();
if ($this->_isNextToken(Doctrine_Query_Token::T_ASC)) {
$this->_parser->match(Doctrine_Query_Token::T_ASC);

View File

@ -37,9 +37,7 @@ class Doctrine_Query_Production_Primary extends Doctrine_Query_Production
{
switch ($this->_parser->lookahead['type']) {
case Doctrine_Query_Token::T_IDENTIFIER:
$nextToken = $this->_parser->getScanner()->peek();
if ($nextToken['value'] === '(') {
if ($this->_isFunction()) {
$this->Function();
} else {
$this->PathExpression();

View File

@ -45,19 +45,11 @@ class Doctrine_Query_Production_SelectExpression extends Doctrine_Query_Producti
return $token['value'] === '*';
}
private function _isSubquery()
{
$lookahead = $this->_parser->lookahead;
$next = $this->_parser->getScanner()->peek();
return $lookahead['value'] === '(' && $next['type'] === Doctrine_Query_Token::T_SELECT;
}
public function execute(array $params = array())
{
if ($this->_isPathExpressionEndingWithAsterisk()) {
$this->PathExpressionEndingWithAsterisk();
} elseif ($this->_isSubquery()) {
} elseif ($this->_isSubselect()) {
$this->_parser->match('(');
$this->Subselect();
$this->_parser->match(')');

View File

@ -38,10 +38,10 @@ class Doctrine_Query_Production_UpdateClause extends Doctrine_Query_Production
$this->RangeVariableDeclaration();
$this->_parser->match(Doctrine_Query_Token::T_SET);
$this->RangeVariableDeclaration();
$this->UpdateItem();
while ($this->_isNextToken(',')) {
$this->_parser->match(',');
$this->RangeVariableDeclaration();
$this->UpdateItem();
}
}
}

View File

@ -0,0 +1,47 @@
<?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>.
*/
/**
* UpdateItem = PathExpression "=" (Expression | "NULL")
*
* @package Doctrine
* @subpackage Query
* @author Janne Vanhala <jpvanhal@cc.hut.fi>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://www.phpdoctrine.org
* @since 1.0
* @version $Revision$
*/
class Doctrine_Query_Production_UpdateItem extends Doctrine_Query_Production
{
public function execute(array $params = array())
{
$this->PathExpression();
$this->_parser->match('=');
if ($this->_isNextToken(Doctrine_Query_Token::T_NULL)) {
$this->_parser->match(Doctrine_Query_Token::T_NULL);
} else {
$this->Expression();
}
}
}

View File

@ -47,7 +47,7 @@ final class Doctrine_Query_Token
const T_BETWEEN = 107;
const T_BY = 108;
const T_COUNT = 109;
const T_DELETE = 100;
const T_DELETE = 110;
const T_DESC = 111;
const T_DISTINCT = 112;
const T_ESCAPE = 113;
@ -56,27 +56,30 @@ final class Doctrine_Query_Token
const T_GROUP = 116;
const T_HAVING = 117;
const T_IN = 118;
const T_INNER = 119;
const T_IS = 120;
const T_JOIN = 121;
const T_LEFT = 122;
const T_LIKE = 123;
const T_LIMIT = 124;
const T_MAX = 125;
const T_MIN = 126;
const T_NOT = 127;
const T_NULL = 128;
const T_OFFSET = 129;
const T_OR = 130;
const T_ORDER = 131;
const T_SELECT = 132;
const T_SET = 133;
const T_SOME = 134;
const T_SUM = 135;
const T_UPDATE = 136;
const T_WHERE = 137;
const T_MOD = 142;
const T_SIZE = 143;
const T_INDEX = 119;
const T_INNER = 120;
const T_IS = 121;
const T_JOIN = 122;
const T_LEFT = 123;
const T_LIKE = 124;
const T_LIMIT = 125;
const T_MAX = 126;
const T_MIN = 127;
const T_MOD = 128;
const T_NOT = 129;
const T_NULL = 130;
const T_OFFSET = 131;
const T_ON = 132;
const T_OR = 133;
const T_ORDER = 134;
const T_SELECT = 135;
const T_SET = 136;
const T_SIZE = 137;
const T_SOME = 138;
const T_SUM = 139;
const T_UPDATE = 140;
const T_WHERE = 141;
const T_WITH = 142;
private function __construct() {}
}

View File

@ -13,9 +13,9 @@
QueryLanguage = SelectStatement | UpdateStatement | DeleteStatement
SelectStatement = [SelectClause] FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] [LimitClause]
UpdateStatement = UpdateClause [WhereClause] [OrderByClause] [LimitClause]
DeleteStatement = DeleteClause [WhereClause] [OrderByClause] [LimitClause]
SelectStatement = [SelectClause] FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] [LimitClause] [OffsetClause]
UpdateStatement = UpdateClause [WhereClause] [OrderByClause] [LimitClause] [OffsetClause]
DeleteStatement = DeleteClause [WhereClause] [OrderByClause] [LimitClause] [OffsetClause]
Subselect = SimpleSelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] [LimitClause] [OffsetClause]
SelectClause = "SELECT" ["ALL" | "DISTINCT"] SelectExpression {"," SelectExpression}
@ -26,7 +26,8 @@ FromClause = "FROM" IdentificationVariableDeclaration {"," IdentificationVa
HavingClause = "HAVING" ConditionalExpression
GroupByClause = "GROUP" "BY" GroupByItem {"," GroupByItem}
OrderByClause = "ORDER" "BY" OrderByItem {"," OrderByItem}
LimitClause = "LIMIT" Expression ["OFFSET" Expression]
LimitClause = "LIMIT" Expression
OffsetClause = "OFFSET" Expression
UpdateClause = "UPDATE" RangeVariableDeclaration "SET" UpdateItem {"," UpdateItem}
OrderByItem = Expression ["ASC" | "DESC"]
@ -67,6 +68,6 @@ ComparisonExpression = ComparisonOperator ( QuantifiedExpression | Expressio
InExpression = ["NOT"] "IN" "(" (Atom {"," Atom} | Subselect) ")"
LikeExpression = ["NOT"] "LIKE" Expression ["ESCAPE" string_literal]
NullComparisonExpression = "IS" ["NOT"] "NULL"
ExistsExpression = ["NOT"] "EXISTS" "(" Subselect ")"
ExistsExpression = "EXISTS" "(" Subselect ")"
Function = identifier "(" [Expression {"," Expression}] ")"

View File

@ -25,6 +25,11 @@ class Doctrine_Query_LanguageRecognition_TestCase extends Doctrine_UnitTestCase
}
}
public function testEmptyQueryString()
{
$this->assertInvalidDql('');
}
public function testPlainFromClauseWithoutAlias()
{
$this->assertValidDql('FROM User');
@ -99,4 +104,180 @@ class Doctrine_Query_LanguageRecognition_TestCase extends Doctrine_UnitTestCase
{
$this->assertValidDql('SELECT u.id FROM User u WHERE 1 IN (1, 2)');
}
public function testUpdateWorksWithOneColumn()
{
$this->assertValidDql("UPDATE User u SET u.name = 'someone'");
}
public function testUpdateWorksWithMultipleColumns()
{
$this->assertValidDql("UPDATE User u SET u.name = 'someone', u.email_id = 5");
}
public function testUpdateSupportsConditions()
{
$this->assertValidDql("UPDATE User u SET u.name = 'someone' WHERE u.id = 5");
}
public function testDeleteAll()
{
$this->assertValidDql('DELETE FROM Entity');
}
public function testDeleteWithCondition()
{
$this->assertValidDql('DELETE FROM Entity WHERE id = 3');
}
public function testDeleteWithLimit()
{
$this->assertValidDql('DELETE FROM Entity LIMIT 20');
}
public function testDeleteWithLimitAndOffset()
{
$this->assertValidDql('DELETE FROM Entity LIMIT 10 OFFSET 20');
}
public function testAdditionExpression()
{
$this->assertValidDql('SELECT u.*, (u.id + u.id) addition FROM User u');
}
public function testSubtractionExpression()
{
$this->assertValidDql('SELECT u.*, (u.id - u.id) subtraction FROM User u');
}
public function testDivisionExpression()
{
$this->assertValidDql('SELECT u.*, (u.id/u.id) division FROM User u');
}
public function testMultiplicationExpression()
{
$this->assertValidDql('SELECT u.*, (u.id * u.id) multiplication FROM User u');
}
public function testNegationExpression()
{
$this->assertValidDql('SELECT u.*, -u.id negation FROM User u');
}
public function testExpressionWithPrecedingPlusSign()
{
$this->assertValidDql('SELECT u.*, +u.id FROM User u');
}
public function testAggregateFunctionInHavingClause()
{
$this->assertValidDql('SELECT u.name FROM User u LEFT JOIN u.Phonenumber p HAVING COUNT(p.id) > 2');
$this->assertValidDql("SELECT u.name FROM User u LEFT JOIN u.Phonenumber p HAVING MAX(u.name) = 'zYne'");
}
public function testMultipleAggregateFunctionsInHavingClause()
{
$this->assertValidDql("SELECT u.name FROM User u LEFT JOIN u.Phonenumber p HAVING MAX(u.name) = 'zYne'");
}
public function testLeftJoin()
{
$this->assertValidDql('FROM User u LEFT JOIN u.Group');
}
public function testJoin()
{
$this->assertValidDql('FROM User u JOIN u.Group');
}
public function testInnerJoin()
{
$this->assertValidDql('FROM User u INNER JOIN u.Group');
}
public function testMultipleLeftJoin()
{
$this->assertValidDql('FROM User u LEFT JOIN u.Group LEFT JOIN u.Phonenumber');
}
public function testMultipleInnerJoin()
{
$this->assertValidDql('SELECT u.name FROM User u INNER JOIN u.Group INNER JOIN u.Phonenumber');
}
public function testMultipleInnerJoin2()
{
$this->assertValidDql('SELECT u.name FROM User u INNER JOIN u.Group, u.Phonenumber');
}
public function testMixingOfJoins()
{
$this->assertValidDql('SELECT u.name, g.name, p.phonenumber FROM User u INNER JOIN u.Group g LEFT JOIN u.Phonenumber p');
}
public function testMixingOfJoins2()
{
$this->assertValidDql('SELECT u.name, g.name, p.phonenumber FROM User u INNER JOIN u.Group.Phonenumber p');
}
public function testOrderBySingleColumn()
{
$this->assertValidDql('SELECT u.name FROM User u ORDER BY u.name');
}
public function testOrderBySingleColumnAscending()
{
$this->assertValidDql('SELECT u.name FROM User u ORDER BY u.name ASC');
}
public function testOrderBySingleColumnDescending()
{
$this->assertValidDql('SELECT u.name FROM User u ORDER BY u.name DESC');
}
public function testOrderByMultipleColumns()
{
$this->assertValidDql('SELECT u.firstname, u.lastname FROM User u ORDER BY u.lastname DESC, u.firstname DESC');
}
public function testOrderByWithFunctionExpression()
{
$this->assertValidDql('SELECT u.name FROM User u ORDER BY COALESCE(u.id, u.name) DESC');
}
public function testSubselectInInExpression()
{
$this->assertValidDql("FROM User u WHERE u.id NOT IN (SELECT u2.id FROM User u2 WHERE u2.name = 'zYne')");
}
public function testSubselectInSelectPart()
{
$this->assertValidDql("SELECT u.name, (SELECT COUNT(p.id) FROM Phonenumber p WHERE p.entity_id = u.id) pcount FROM User u WHERE u.name = 'zYne' LIMIT 1");
}
public function testInputParameter()
{
$this->assertValidDql('FROM User WHERE u.id = ?');
}
public function testNamedInputParameter()
{
$this->assertValidDql('FROM User WHERE u.id = :id');
}
public function testCustomJoinsAndWithKeywordSupported()
{
$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.id FROM User u LEFT JOIN u.Phonenumber p ON p.phonenumber = '123 123'");
}
public function testIndexByClauseWithOneComponent()
{
$this->assertValidDql('FROM Record_City c INDEX BY c.name');
}
}

View File

@ -91,6 +91,24 @@ class Doctrine_Query_Scanner_TestCase extends Doctrine_UnitTestCase
$this->assertEqual("'abc''defg'''", $token['value']);
}
public function testScannerRecognizesInputParameter()
{
$scanner = new Doctrine_Query_Scanner('?');
$token = $scanner->next();
$this->assertEqual(Doctrine_Query_Token::T_INPUT_PARAMETER, $token['type']);
$this->assertEqual('?', $token['value']);
}
public function testScannerRecognizesNamedInputParameter()
{
$scanner = new Doctrine_Query_Scanner(':name');
$token = $scanner->next();
$this->assertEqual(Doctrine_Query_Token::T_INPUT_PARAMETER, $token['type']);
$this->assertEqual(':name', $token['value']);
}
public function testScannerTokenizesASimpleQueryCorrectly()
{
$dql = "SELECT u.* FROM User u WHERE u.name = 'Jack O''Neil'";