From ae6d80daabc9647d9ceec450c8b9b4eaff86c648 Mon Sep 17 00:00:00 2001 From: Carnage Date: Fri, 9 Feb 2018 09:48:44 +0000 Subject: [PATCH 1/3] Adds sql generation test --- .../Doctrine/Tests/ORM/Query/DeleteSqlGenerationTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/Query/DeleteSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/DeleteSqlGenerationTest.php index 8218339df..1f1a7332d 100644 --- a/tests/Doctrine/Tests/ORM/Query/DeleteSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/DeleteSqlGenerationTest.php @@ -36,6 +36,14 @@ class DeleteSqlGenerationTest extends OrmTestCase } } + public function testSupportsDeleteWithoutWhereAndAlias() : void + { + $this->assertSqlGeneration( + 'DELETE FROM Doctrine\Tests\Models\CMS\CmsUser', + 'DELETE FROM cms_users' + ); + } + public function testSupportsDeleteWithoutWhereAndFrom() { $this->assertSqlGeneration( From f36470941c6d6f62d91632a7745e3638ec9150bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Cobucci?= Date: Sun, 18 Feb 2018 23:19:46 +0100 Subject: [PATCH 2/3] Fix BC-break on delete queries with nasty workaround The `v2.5.x` series of the ORM allowed to have DELETE DQLs without using an alias, even though it didn't follow the grammar rules of the parser. We fixed that issue on `v2.6.0` however that was a BC-breaking change and lots of people were relying on this faulty behaviour. This workaround fixes the BC-break, without even trying to be elegant. In `v2.7.0.` we should raise a deprecation notice to notify people that we'll drop that "feature" in `v3.0`. --- lib/Doctrine/ORM/Query/Parser.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index e726aa062..e8c8104bd 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -22,6 +22,7 @@ namespace Doctrine\ORM\Query; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Query; use Doctrine\ORM\Query\AST\Functions; +use function strpos; /** * An LL(*) recursive-descent parser for the context-free grammar of the Doctrine Query Language. @@ -1280,7 +1281,9 @@ class Parser $this->match(Lexer::T_AS); } - $aliasIdentificationVariable = $this->AliasIdentificationVariable(); + $aliasIdentificationVariable = $this->lexer->isNextToken(Lexer::T_IDENTIFIER) + ? $this->AliasIdentificationVariable() + : 'alias_should_have_been_set'; $deleteClause->aliasIdentificationVariable = $aliasIdentificationVariable; $class = $this->em->getClassMetadata($deleteClause->abstractSchemaName); From fc943b70f629b40e08f6dc121b6466c0b5c5154f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Cobucci?= Date: Sun, 18 Feb 2018 23:26:18 +0100 Subject: [PATCH 3/3] Use early-returns to improve readability of the Parser --- lib/Doctrine/ORM/Query/Parser.php | 53 ++++++++++++++++--------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index e8c8104bd..48b13df64 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -304,21 +304,24 @@ class Parser $lookaheadType = $this->lexer->lookahead['type']; // 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 ($lookaheadType === $token) { + $this->lexer->moveNext(); + return; + } - // If parameter is keyword (200+) must be exact match - if ($token > Lexer::T_IDENTIFIER) { - $this->syntaxError($this->lexer->getLiteral($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 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)); - } + // 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(); @@ -959,20 +962,20 @@ 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; + return $this->lexer->token['value']; } - return $schemaName; + if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER)) { + $this->match(Lexer::T_IDENTIFIER); + + return $this->lexer->token['value']; + } + + $this->match(Lexer::T_ALIASED_NAME); + + [$namespaceAlias, $simpleClassName] = explode(':', $this->lexer->token['value']); + + return $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; } /**