Some extra refinement over patch
This commit is contained in:
parent
b7bd42638d
commit
443259f629
@ -49,68 +49,70 @@ class Lexer extends \Doctrine\Common\Lexer
|
||||
const T_PLUS = 17;
|
||||
const T_OPEN_CURLY_BRACE = 18;
|
||||
const T_CLOSE_CURLY_BRACE = 19;
|
||||
const T_ALIASED_NAME = 20;
|
||||
const T_FULLY_QUALIFIED_NAME = 21;
|
||||
|
||||
// All tokens that are also identifiers should be >= 100
|
||||
const T_IDENTIFIER = 100;
|
||||
const T_ALL = 101;
|
||||
const T_AND = 102;
|
||||
const T_ANY = 103;
|
||||
const T_AS = 104;
|
||||
const T_ASC = 105;
|
||||
const T_AVG = 106;
|
||||
const T_BETWEEN = 107;
|
||||
const T_BOTH = 108;
|
||||
const T_BY = 109;
|
||||
const T_CASE = 110;
|
||||
const T_COALESCE = 111;
|
||||
const T_COUNT = 112;
|
||||
const T_DELETE = 113;
|
||||
const T_DESC = 114;
|
||||
const T_DISTINCT = 115;
|
||||
const T_ELSE = 116;
|
||||
const T_EMPTY = 117;
|
||||
const T_END = 118;
|
||||
const T_ESCAPE = 119;
|
||||
const T_EXISTS = 120;
|
||||
const T_FALSE = 121;
|
||||
const T_FROM = 122;
|
||||
const T_GROUP = 123;
|
||||
const T_HAVING = 124;
|
||||
const T_HIDDEN = 125;
|
||||
const T_IN = 126;
|
||||
const T_INDEX = 127;
|
||||
const T_INNER = 128;
|
||||
const T_INSTANCE = 129;
|
||||
const T_IS = 130;
|
||||
const T_JOIN = 131;
|
||||
const T_LEADING = 132;
|
||||
const T_LEFT = 133;
|
||||
const T_LIKE = 134;
|
||||
const T_MAX = 135;
|
||||
const T_MEMBER = 136;
|
||||
const T_MIN = 137;
|
||||
const T_NOT = 138;
|
||||
const T_NULL = 139;
|
||||
const T_NULLIF = 140;
|
||||
const T_OF = 141;
|
||||
const T_OR = 142;
|
||||
const T_ORDER = 143;
|
||||
const T_OUTER = 144;
|
||||
const T_SELECT = 145;
|
||||
const T_SET = 146;
|
||||
const T_SOME = 147;
|
||||
const T_SUM = 148;
|
||||
const T_THEN = 149;
|
||||
const T_TRAILING = 150;
|
||||
const T_TRUE = 151;
|
||||
const T_UPDATE = 152;
|
||||
const T_WHEN = 153;
|
||||
const T_WHERE = 154;
|
||||
const T_WITH = 155;
|
||||
const T_PARTIAL = 156;
|
||||
const T_NEW = 157;
|
||||
// All tokens that are identifiers or keywords that could be considered as identifiers should be >= 100
|
||||
const T_ALIASED_NAME = 100;
|
||||
const T_FULLY_QUALIFIED_NAME = 101;
|
||||
const T_IDENTIFIER = 102;
|
||||
|
||||
// All keyword tokens should be >= 200
|
||||
const T_ALL = 200;
|
||||
const T_AND = 201;
|
||||
const T_ANY = 202;
|
||||
const T_AS = 203;
|
||||
const T_ASC = 204;
|
||||
const T_AVG = 205;
|
||||
const T_BETWEEN = 206;
|
||||
const T_BOTH = 207;
|
||||
const T_BY = 208;
|
||||
const T_CASE = 209;
|
||||
const T_COALESCE = 210;
|
||||
const T_COUNT = 211;
|
||||
const T_DELETE = 212;
|
||||
const T_DESC = 213;
|
||||
const T_DISTINCT = 214;
|
||||
const T_ELSE = 215;
|
||||
const T_EMPTY = 216;
|
||||
const T_END = 217;
|
||||
const T_ESCAPE = 218;
|
||||
const T_EXISTS = 219;
|
||||
const T_FALSE = 220;
|
||||
const T_FROM = 221;
|
||||
const T_GROUP = 222;
|
||||
const T_HAVING = 223;
|
||||
const T_HIDDEN = 224;
|
||||
const T_IN = 225;
|
||||
const T_INDEX = 226;
|
||||
const T_INNER = 227;
|
||||
const T_INSTANCE = 228;
|
||||
const T_IS = 229;
|
||||
const T_JOIN = 230;
|
||||
const T_LEADING = 231;
|
||||
const T_LEFT = 232;
|
||||
const T_LIKE = 233;
|
||||
const T_MAX = 234;
|
||||
const T_MEMBER = 235;
|
||||
const T_MIN = 236;
|
||||
const T_NEW = 237;
|
||||
const T_NOT = 238;
|
||||
const T_NULL = 239;
|
||||
const T_NULLIF = 240;
|
||||
const T_OF = 241;
|
||||
const T_OR = 242;
|
||||
const T_ORDER = 243;
|
||||
const T_OUTER = 244;
|
||||
const T_PARTIAL = 245;
|
||||
const T_SELECT = 246;
|
||||
const T_SET = 247;
|
||||
const T_SOME = 248;
|
||||
const T_SUM = 249;
|
||||
const T_THEN = 250;
|
||||
const T_TRAILING = 251;
|
||||
const T_TRUE = 252;
|
||||
const T_UPDATE = 253;
|
||||
const T_WHEN = 254;
|
||||
const T_WHERE = 255;
|
||||
const T_WITH = 256;
|
||||
|
||||
/**
|
||||
* Creates a new query scanner object.
|
||||
@ -129,7 +131,7 @@ class Lexer extends \Doctrine\Common\Lexer
|
||||
{
|
||||
return array(
|
||||
'[a-z_][a-z0-9_]*\:[a-z_][a-z0-9_]*(?:\\\[a-z_][a-z0-9_]*)*', // aliased name
|
||||
'[a-z_][a-z0-9_]*(?:\\\[a-z_][a-z0-9_]*)*', // identifier or qualified name
|
||||
'[a-z_\\\][a-z0-9_]*(?:\\\[a-z_][a-z0-9_]*)*', // identifier or qualified name
|
||||
'(?:[0-9]+(?:[\.][0-9]+)*)(?:e[+-]?[0-9]+)?', // numbers
|
||||
"'(?:[^']|'')*'", // quoted strings
|
||||
'\?[0-9]*|:[a-z_][a-z0-9_]*' // parameters
|
||||
@ -167,7 +169,7 @@ class Lexer extends \Doctrine\Common\Lexer
|
||||
return self::T_STRING;
|
||||
|
||||
// Recognize identifiers, aliased or qualified names
|
||||
case (ctype_alpha($value[0]) || $value[0] === '_'):
|
||||
case (ctype_alpha($value[0]) || $value[0] === '_' || $value[0] === '\\'):
|
||||
$name = 'Doctrine\ORM\Query\Lexer::T_' . strtoupper($value);
|
||||
|
||||
if (defined($name)) {
|
||||
@ -181,9 +183,11 @@ class Lexer extends \Doctrine\Common\Lexer
|
||||
if (strpos($value, ':') !== false) {
|
||||
return self::T_ALIASED_NAME;
|
||||
}
|
||||
|
||||
if (strpos($value, '\\') !== false) {
|
||||
return self::T_FULLY_QUALIFIED_NAME;
|
||||
}
|
||||
|
||||
return self::T_IDENTIFIER;
|
||||
|
||||
// Recognize input parameters
|
||||
@ -206,9 +210,6 @@ class Lexer extends \Doctrine\Common\Lexer
|
||||
case ($value === '{'): return self::T_OPEN_CURLY_BRACE;
|
||||
case ($value === '}'): return self::T_CLOSE_CURLY_BRACE;
|
||||
|
||||
case (strrpos($value, ':') !== false):
|
||||
return self::T_ALIASED_NAME;
|
||||
|
||||
// Default
|
||||
default:
|
||||
// Do nothing
|
||||
|
@ -311,14 +311,22 @@ class Parser
|
||||
{
|
||||
$lookaheadType = $this->lexer->lookahead['type'];
|
||||
|
||||
/*
|
||||
* The following condition means:
|
||||
* - If next token matches expectation -> ok.
|
||||
* - If expectation is to get T_IDENTIFIER and we find one of the tokens that are reserved words *but* would work as identifiers as well (e. g. "FROM", DDC-505) -> ok.
|
||||
* - Else fail.
|
||||
*/
|
||||
if ($lookaheadType !== $token && ($token !== Lexer::T_IDENTIFIER || $lookaheadType <= Lexer::T_IDENTIFIER)) {
|
||||
$this->syntaxError($this->lexer->getLiteral($token));
|
||||
// Short-circuit on first condition, usually types match
|
||||
if ($lookaheadType !== $token) {
|
||||
// If parameter is not identifier (1-99) must be exact match
|
||||
if ($token < Lexer::T_IDENTIFIER) {
|
||||
$this->syntaxError($this->lexer->getLiteral($token));
|
||||
}
|
||||
|
||||
// If parameter is keyword (200+) must be exact match
|
||||
if ($token > Lexer::T_IDENTIFIER) {
|
||||
$this->syntaxError($this->lexer->getLiteral($token));
|
||||
}
|
||||
|
||||
// If parameter is T_IDENTIFIER, then matches T_IDENTIFIER (100) and keywords (200+)
|
||||
if ($token === Lexer::T_IDENTIFIER && $lookaheadType < Lexer::T_IDENTIFIER) {
|
||||
$this->syntaxError($this->lexer->getLiteral($token));
|
||||
}
|
||||
}
|
||||
|
||||
$this->lexer->moveNext();
|
||||
@ -962,13 +970,17 @@ class Parser
|
||||
{
|
||||
if ($this->lexer->isNextToken(Lexer::T_FULLY_QUALIFIED_NAME)) {
|
||||
$this->match(Lexer::T_FULLY_QUALIFIED_NAME);
|
||||
|
||||
$schemaName = $this->lexer->token['value'];
|
||||
} else if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER)) {
|
||||
$this->match(Lexer::T_IDENTIFIER);
|
||||
|
||||
$schemaName = $this->lexer->token['value'];
|
||||
} else {
|
||||
$this->match(Lexer::T_ALIASED_NAME);
|
||||
|
||||
list($namespaceAlias, $simpleClassName) = explode(':', $this->lexer->token['value']);
|
||||
|
||||
$schemaName = $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
|
||||
}
|
||||
|
||||
@ -1216,8 +1228,10 @@ class Parser
|
||||
public function UpdateClause()
|
||||
{
|
||||
$this->match(Lexer::T_UPDATE);
|
||||
|
||||
$token = $this->lexer->lookahead;
|
||||
$abstractSchemaName = $this->AbstractSchemaName();
|
||||
|
||||
$this->validateAbstractSchemaName($abstractSchemaName);
|
||||
|
||||
if ($this->lexer->isNextToken(Lexer::T_AS)) {
|
||||
@ -1272,7 +1286,9 @@ class Parser
|
||||
|
||||
$token = $this->lexer->lookahead;
|
||||
$abstractSchemaName = $this->AbstractSchemaName();
|
||||
|
||||
$this->validateAbstractSchemaName($abstractSchemaName);
|
||||
|
||||
$deleteClause = new AST\DeleteClause($abstractSchemaName);
|
||||
|
||||
if ($this->lexer->isNextToken(Lexer::T_AS)) {
|
||||
@ -1718,6 +1734,7 @@ class Parser
|
||||
public function RangeVariableDeclaration()
|
||||
{
|
||||
$abstractSchemaName = $this->AbstractSchemaName();
|
||||
|
||||
$this->validateAbstractSchemaName($abstractSchemaName);
|
||||
|
||||
if ($this->lexer->isNextToken(Lexer::T_AS)) {
|
||||
@ -2242,9 +2259,12 @@ class Parser
|
||||
}
|
||||
|
||||
// [["AS"] ["HIDDEN"] AliasResultVariable]
|
||||
$mustHaveAliasResultVariable = false;
|
||||
|
||||
if ($this->lexer->isNextToken(Lexer::T_AS)) {
|
||||
$this->match(Lexer::T_AS);
|
||||
|
||||
$mustHaveAliasResultVariable = true;
|
||||
}
|
||||
|
||||
$hiddenAliasResultVariable = false;
|
||||
@ -2257,7 +2277,7 @@ class Parser
|
||||
|
||||
$aliasResultVariable = null;
|
||||
|
||||
if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER)) {
|
||||
if ($mustHaveAliasResultVariable || $this->lexer->isNextToken(Lexer::T_IDENTIFIER)) {
|
||||
$token = $this->lexer->lookahead;
|
||||
$aliasResultVariable = $this->AliasResultVariable();
|
||||
|
||||
@ -3150,6 +3170,7 @@ class Parser
|
||||
}
|
||||
|
||||
$abstractSchemaName = $this->AbstractSchemaName();
|
||||
|
||||
$this->validateAbstractSchemaName($abstractSchemaName);
|
||||
|
||||
return $abstractSchemaName;
|
||||
|
@ -129,7 +129,7 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
$this->_em->clear();
|
||||
|
||||
$user2 = $this->_em->createQuery('select u from Doctrine\Tests\Models\CMS\CmsUser u where u.id=?1')
|
||||
$user2 = $this->_em->createQuery('select u from \Doctrine\Tests\Models\CMS\CmsUser u where u.id=?1')
|
||||
->setParameter(1, $userId)
|
||||
->getSingleResult();
|
||||
|
||||
|
@ -1046,21 +1046,21 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
/**
|
||||
* @expectedException Doctrine\ORM\Query\QueryException
|
||||
* @expectedExceptionMessage [Semantical Error] line 0, col 11 near 'InvalidClass(u.name)': Error: Class "InvalidClass" is not defined.
|
||||
* @expectedExceptionMessage [Semantical Error] line 0, col 11 near '\InvalidClass(u.name)': Error: Class "\InvalidClass" is not defined.
|
||||
*/
|
||||
public function testInvalidClassException()
|
||||
{
|
||||
$dql = "SELECT new InvalidClass(u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u";
|
||||
$dql = "SELECT new \InvalidClass(u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u";
|
||||
$this->_em->createQuery($dql)->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Doctrine\ORM\Query\QueryException
|
||||
* @expectedExceptionMessage [Semantical Error] line 0, col 11 near 'stdClass(u.name)': Error: Class "stdClass" has not a valid constructor.
|
||||
* @expectedExceptionMessage [Semantical Error] line 0, col 11 near '\stdClass(u.name)': Error: Class "\stdClass" has not a valid constructor.
|
||||
*/
|
||||
public function testInvalidClassConstructorException()
|
||||
{
|
||||
$dql = "SELECT new stdClass(u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u";
|
||||
$dql = "SELECT new \stdClass(u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u";
|
||||
$this->_em->createQuery($dql)->getResult();
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,11 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
|
||||
{
|
||||
$this->setExpectedException('\Doctrine\ORM\Query\QueryException');
|
||||
|
||||
$this->_em->getConfiguration()->setEntityNamespaces(array('Unknown' => 'Unknown', 'CMS' => 'Doctrine\Tests\Models\CMS'));
|
||||
$this->_em->getConfiguration()->setEntityNamespaces(array(
|
||||
'Unknown' => 'Unknown',
|
||||
'CMS' => 'Doctrine\Tests\Models\CMS'
|
||||
));
|
||||
|
||||
$this->parseDql($dql);
|
||||
}
|
||||
|
||||
@ -92,6 +96,7 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
|
||||
{
|
||||
return array(
|
||||
|
||||
array('SELECT \'foo\' AS foo\bar FROM Doctrine\Tests\Models\CMS\CmsUser u'),
|
||||
/* Checks for invalid IdentificationVariables and AliasIdentificationVariables */
|
||||
array('SELECT \foo FROM Doctrine\Tests\Models\CMS\CmsUser \foo'),
|
||||
array('SELECT foo\ FROM Doctrine\Tests\Models\CMS\CmsUser foo\\'),
|
||||
@ -118,7 +123,6 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
|
||||
/* Checks for invalid AliasResultVariable */
|
||||
array('SELECT \'foo\' AS \foo FROM Doctrine\Tests\Models\CMS\CmsUser u'),
|
||||
array('SELECT \'foo\' AS \foo\bar FROM Doctrine\Tests\Models\CMS\CmsUser u'),
|
||||
array('SELECT \'foo\' AS foo\bar FROM Doctrine\Tests\Models\CMS\CmsUser u'),
|
||||
array('SELECT \'foo\' AS foo\ FROM Doctrine\Tests\Models\CMS\CmsUser u'),
|
||||
array('SELECT \'foo\' AS foo\\\\bar FROM Doctrine\Tests\Models\CMS\CmsUser u'),
|
||||
array('SELECT \'foo\' AS foo: FROM Doctrine\Tests\Models\CMS\CmsUser u'),
|
||||
|
@ -24,12 +24,11 @@ class ParserTest extends \Doctrine\Tests\OrmTestCase
|
||||
* @covers Doctrine\ORM\Query\Parser::AbstractSchemaName
|
||||
* @group DDC-3715
|
||||
*/
|
||||
public function testAbstractSchemaNameFailsOnClassnamesWithLeadingBackslash()
|
||||
public function testAbstractSchemaNameSupportsClassnamesWithLeadingBackslash()
|
||||
{
|
||||
$this->setExpectedException('\Doctrine\ORM\Query\QueryException');
|
||||
|
||||
$parser = $this->createParser('\Doctrine\Tests\Models\CMS\CmsUser');
|
||||
$parser->AbstractSchemaName();
|
||||
|
||||
$this->assertEquals('\Doctrine\Tests\Models\CMS\CmsUser', $parser->AbstractSchemaName());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -39,6 +38,7 @@ class ParserTest extends \Doctrine\Tests\OrmTestCase
|
||||
public function testAbstractSchemaNameSupportsIdentifier()
|
||||
{
|
||||
$parser = $this->createParser('stdClass');
|
||||
|
||||
$this->assertEquals('stdClass', $parser->AbstractSchemaName());
|
||||
}
|
||||
|
||||
@ -49,6 +49,7 @@ class ParserTest extends \Doctrine\Tests\OrmTestCase
|
||||
public function testAbstractSchemaNameSupportsNamespaceAlias()
|
||||
{
|
||||
$parser = $this->createParser('CMS:CmsUser');
|
||||
|
||||
$parser->getEntityManager()->getConfiguration()->addEntityNamespace('CMS', 'Doctrine\Tests\Models\CMS');
|
||||
|
||||
$this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $parser->AbstractSchemaName());
|
||||
@ -61,6 +62,7 @@ class ParserTest extends \Doctrine\Tests\OrmTestCase
|
||||
public function testAbstractSchemaNameSupportsNamespaceAliasWithRelativeClassname()
|
||||
{
|
||||
$parser = $this->createParser('Model:CMS\CmsUser');
|
||||
|
||||
$parser->getEntityManager()->getConfiguration()->addEntityNamespace('Model', 'Doctrine\Tests\Models');
|
||||
|
||||
$this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $parser->AbstractSchemaName());
|
||||
@ -74,7 +76,9 @@ class ParserTest extends \Doctrine\Tests\OrmTestCase
|
||||
public function testMatch($expectedToken, $inputString)
|
||||
{
|
||||
$parser = $this->createParser($inputString);
|
||||
|
||||
$parser->match($expectedToken); // throws exception if not matched
|
||||
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
|
||||
@ -88,6 +92,7 @@ class ParserTest extends \Doctrine\Tests\OrmTestCase
|
||||
$this->setExpectedException('\Doctrine\ORM\Query\QueryException');
|
||||
|
||||
$parser = $this->createParser($inputString);
|
||||
|
||||
$parser->match($expectedToken);
|
||||
}
|
||||
|
||||
|
@ -491,11 +491,22 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
{
|
||||
// This also uses FQCNs starting with or without a backslash in the INSTANCE OF parameter
|
||||
$this->assertSqlGeneration(
|
||||
"SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF (Doctrine\Tests\Models\Company\CompanyEmployee, Doctrine\Tests\Models\Company\CompanyManager)",
|
||||
"SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF (Doctrine\Tests\Models\Company\CompanyEmployee, \Doctrine\Tests\Models\Company\CompanyManager)",
|
||||
"SELECT c0_.id AS id_0, c0_.name AS name_1, c0_.discr AS discr_2 FROM company_persons c0_ WHERE c0_.discr IN ('employee', 'manager')"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1194
|
||||
*/
|
||||
public function testSupportsInstanceOfExpressionsInWherePartPrefixedSlash()
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
"SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF \Doctrine\Tests\Models\Company\CompanyEmployee",
|
||||
"SELECT c0_.id AS id_0, c0_.name AS name_1, c0_.discr AS discr_2 FROM company_persons c0_ WHERE c0_.discr IN ('employee')"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1194
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user