Compare commits

...

13 Commits

Author SHA1 Message Date
Grégoire Paris
6307b4fa7d Merge pull request #8012 from sgehrig/bug/#8011-ordering-with-arithmetic-expression
Bug/#8011 ordering with arithmetic expression
2025-06-24 19:50:46 +02:00
Alexander M. Turek
5d21bb158b Fix calls to Application::add() (#12006) 2025-06-18 08:58:26 +02:00
Stefan Gehrig
067ad51b3f fixes sqlite sql inconsistency 2025-03-17 08:48:30 +01:00
Stefan Gehrig
00c77213fb fixes codesniffer violation 2025-03-15 09:42:21 +01:00
Stefan Gehrig
c68b8f90b3 adds a test for postgres that uses a HIDDEN result variable for ordering based on arithmetic expression 2025-03-05 09:28:52 +01:00
Stefan Gehrig
aa4f9ce9e9 CS fix based on PHP_CodeSniffer report 2025-03-05 09:22:57 +01:00
Stefan Gehrig
d96fc23327 skips tests when running on postgres 2025-02-27 10:30:21 +01:00
Stefan Gehrig
ec6d1b9f72 fixes whitespace
Signed-off-by: Stefan Gehrig <stefan.gehrig.hn@googlemail.com>
2025-01-07 08:51:19 +01:00
Stefan Gehrig
d809fed52a fixes code sniffer complaints
Signed-off-by: Stefan Gehrig <stefan.gehrig.hn@googlemail.com>
2025-01-07 08:48:42 +01:00
Stefan Gehrig
0e4786dfa8 adds testcases for order by items enclosed in ((...)) (double brackets - just one bracket does not work)
just one bracket (...) gives

Exception : [Doctrine\ORM\Query\QueryException] [Syntax Error] line 0, col xx: Error: Expected Doctrine\ORM\Query\Lexer::T_IDENTIFIER, got '('
2025-01-03 10:45:08 +01:00
Stefan Gehrig
c429262f02 adds detection of literals/result variables at the beginning of an order by item with arithmetic expression
Not sure whether this covers the whole problem regarding complex expressions in order by items but it fixes the provided test cases
2025-01-03 10:45:07 +01:00
Stefan Gehrig
f4fdcbcdcb adds more test cases 2025-01-03 10:44:17 +01:00
Stefan Gehrig
b0806469d5 adds test case for GH issue #8011 2025-01-03 10:44:17 +01:00
15 changed files with 311 additions and 16 deletions

View File

@@ -11,7 +11,7 @@ parameters:
# Fallback logic for DBAL 2
-
message: '/Application::add\(\) expects Symfony\\Component\\Console\\Command\\Command/'
message: '/Parameter #2 \$command of static method Doctrine\\ORM\\Tools\\Console\\ConsoleRunner\:\:addCommandToApplication\(\) expects Symfony\\Component\\Console\\Command\\Command/'
path: src/Tools/Console/ConsoleRunner.php
- '/^Class Doctrine\\DBAL\\Platforms\\SQLAnywherePlatform not found\.$/'

View File

@@ -9,7 +9,7 @@ parameters:
# Fallback logic for DBAL 2
-
message: '/Application::add\(\) expects Symfony\\Component\\Console\\Command\\Command/'
message: '/Parameter #2 \$command of static method Doctrine\\ORM\\Tools\\Console\\ConsoleRunner\:\:addCommandToApplication\(\) expects Symfony\\Component\\Console\\Command\\Command/'
path: src/Tools/Console/ConsoleRunner.php
- '/^Class Doctrine\\DBAL\\Platforms\\SQLAnywherePlatform not found\.$/'

View File

@@ -1554,7 +1554,7 @@ class Parser
assert($this->lexer->lookahead !== null);
switch (true) {
case $this->isMathOperator($peek):
case $this->isMathOperator($peek) || $this->isMathOperator($glimpse):
$expr = $this->SimpleArithmeticExpression();
break;

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Tools\Console;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use function method_exists;
/**
* Forward compatibility with Symfony Console 7.4
*
* @internal
*/
trait ApplicationCompatibility
{
private static function addCommandToApplication(Application $application, Command $command): ?Command
{
if (method_exists(Application::class, 'addCommand')) {
// @phpstan-ignore method.notFound (This method will be added in Symfony 7.4)
return $application->addCommand($command);
}
return $application->add($command);
}
}

View File

@@ -23,6 +23,8 @@ use function class_exists;
*/
final class ConsoleRunner
{
use ApplicationCompatibility;
/**
* Create a Symfony Console HelperSet
*
@@ -91,7 +93,7 @@ final class ConsoleRunner
$connectionProvider = new ConnectionFromManagerProvider($entityManagerProvider);
if (class_exists(DBALConsole\Command\ImportCommand::class)) {
$cli->add(new DBALConsole\Command\ImportCommand());
self::addCommandToApplication($cli, new DBALConsole\Command\ImportCommand());
}
$cli->addCommands(

View File

@@ -0,0 +1,238 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\Tests\Models\Company\CompanyEmployee;
use Doctrine\Tests\OrmFunctionalTestCase;
use function count;
/**
* Functional tests for ordering with arithmetic expression.
*
* @group GH8011
*/
class GH8011Test extends OrmFunctionalTestCase
{
protected function setUp(): void
{
$this->useModelSet('company');
parent::setUp();
$this->generateFixture();
}
private function skipIfPostgres(string $test): void
{
$platform = $this->_em->getConnection()->getDatabasePlatform();
if ($platform instanceof PostgreSQLPlatform) {
self::markTestSkipped(
'The ' . $test . ' test does not work on postgresql (see https://github.com/doctrine/orm/pull/8012).'
);
}
}
public function testOrderWithArithmeticExpressionWithSingleValuedPathExpression(): void
{
$dql = 'SELECT p ' .
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
'ORDER BY p.id + p.id ASC';
/** @var CompanyEmployee[] $result */
$result = $this->_em->createQuery($dql)->getResult();
$this->assertEquals(2, count($result));
$this->assertEquals('Benjamin E.', $result[0]->getName());
$this->assertEquals('Guilherme B.', $result[1]->getName());
}
public function testOrderWithArithmeticExpressionWithLiteralAndSingleValuedPathExpression(): void
{
$dql = 'SELECT p ' .
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
'ORDER BY 1 + p.id ASC';
/** @var CompanyEmployee[] $result */
$result = $this->_em->createQuery($dql)->getResult();
$this->assertEquals(2, count($result));
$this->assertEquals('Benjamin E.', $result[0]->getName());
$this->assertEquals('Guilherme B.', $result[1]->getName());
}
public function testOrderWithArithmeticExpressionWithLiteralAndSingleValuedPathExpression2(): void
{
$dql = 'SELECT p ' .
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
'ORDER BY ((1 + p.id)) ASC';
/** @var CompanyEmployee[] $result */
$result = $this->_em->createQuery($dql)->getResult();
$this->assertEquals(2, count($result));
$this->assertEquals('Benjamin E.', $result[0]->getName());
$this->assertEquals('Guilherme B.', $result[1]->getName());
}
public function testOrderWithArithmeticExpressionWithSingleValuedPathExpressionAndLiteral(): void
{
$dql = 'SELECT p ' .
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
'ORDER BY p.id + 1 ASC';
/** @var CompanyEmployee[] $result */
$result = $this->_em->createQuery($dql)->getResult();
$this->assertEquals(2, count($result));
$this->assertEquals('Benjamin E.', $result[0]->getName());
$this->assertEquals('Guilherme B.', $result[1]->getName());
}
public function testOrderWithArithmeticExpressionWithResultVariableAndLiteral(): void
{
$this->skipIfPostgres(__FUNCTION__);
$dql = 'SELECT p, p.salary AS HIDDEN s ' .
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
'ORDER BY s + 1 DESC';
/** @var CompanyEmployee[] $result */
$result = $this->_em->createQuery($dql)->getResult();
$this->assertEquals(2, count($result));
$this->assertEquals('Guilherme B.', $result[0]->getName());
$this->assertEquals('Benjamin E.', $result[1]->getName());
}
public function testOrderWithArithmeticExpressionWithResultVariableAndLiteral2(): void
{
$this->skipIfPostgres(__FUNCTION__);
$dql = 'SELECT p, p.salary AS HIDDEN s ' .
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
'ORDER BY ((s + 1)) DESC';
/** @var CompanyEmployee[] $result */
$result = $this->_em->createQuery($dql)->getResult();
$this->assertEquals(2, count($result));
$this->assertEquals('Guilherme B.', $result[0]->getName());
$this->assertEquals('Benjamin E.', $result[1]->getName());
}
public function testOrderWithArithmeticExpressionWithLiteralAndResultVariable(): void
{
$this->skipIfPostgres(__FUNCTION__);
$dql = 'SELECT p, p.salary AS HIDDEN s ' .
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
'ORDER BY 1 + s DESC';
/** @var CompanyEmployee[] $result */
$result = $this->_em->createQuery($dql)->getResult();
$this->assertEquals(2, count($result));
$this->assertEquals('Guilherme B.', $result[0]->getName());
$this->assertEquals('Benjamin E.', $result[1]->getName());
}
public function testOrderWithArithmeticExpressionWithLiteralAndResultVariable2(): void
{
$this->skipIfPostgres(__FUNCTION__);
$dql = 'SELECT p, p.salary AS HIDDEN s ' .
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
'ORDER BY ((1 + s)) DESC';
/** @var CompanyEmployee[] $result */
$result = $this->_em->createQuery($dql)->getResult();
$this->assertEquals(2, count($result));
$this->assertEquals('Guilherme B.', $result[0]->getName());
$this->assertEquals('Benjamin E.', $result[1]->getName());
}
public function testOrderWithArithmeticExpressionWithResultVariableAndSingleValuedPathExpression(): void
{
$this->skipIfPostgres(__FUNCTION__);
$dql = 'SELECT p, p.salary AS HIDDEN s ' .
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
'ORDER BY s + p.id DESC';
/** @var CompanyEmployee[] $result */
$result = $this->_em->createQuery($dql)->getResult();
$this->assertEquals(2, count($result));
$this->assertEquals('Guilherme B.', $result[0]->getName());
$this->assertEquals('Benjamin E.', $result[1]->getName());
}
public function testOrderWithArithmeticExpressionWithResultVariableAndSingleValuedPathExpression2(): void
{
$this->skipIfPostgres(__FUNCTION__);
$dql = 'SELECT p, p.salary AS HIDDEN s ' .
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
'ORDER BY ((s + p.id)) DESC';
/** @var CompanyEmployee[] $result */
$result = $this->_em->createQuery($dql)->getResult();
$this->assertEquals(2, count($result));
$this->assertEquals('Guilherme B.', $result[0]->getName());
$this->assertEquals('Benjamin E.', $result[1]->getName());
}
public function testOrderWithArithmeticExpressionWithSingleValuedPathExpressionAndResultVariable(): void
{
$this->skipIfPostgres(__FUNCTION__);
$dql = 'SELECT p, p.salary AS HIDDEN s ' .
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
'ORDER BY p.id + s DESC';
/** @var CompanyEmployee[] $result */
$result = $this->_em->createQuery($dql)->getResult();
$this->assertEquals(2, count($result));
$this->assertEquals('Guilherme B.', $result[0]->getName());
$this->assertEquals('Benjamin E.', $result[1]->getName());
}
public function testOrderWithArithmeticExpressionWithLiteralAndResultVariableUsingHiddenResultVariable(): void
{
$dql = 'SELECT p, 1 + p.salary AS HIDDEN _order ' .
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
'ORDER BY _order DESC';
/** @var CompanyEmployee[] $result */
$result = $this->_em->createQuery($dql)->getResult();
$this->assertEquals(2, count($result));
$this->assertEquals('Guilherme B.', $result[0]->getName());
$this->assertEquals('Benjamin E.', $result[1]->getName());
}
public function generateFixture(): void
{
$person1 = new CompanyEmployee();
$person1->setName('Benjamin E.');
$person1->setDepartment('IT');
$person1->setSalary(200000);
$person2 = new CompanyEmployee();
$person2->setName('Guilherme B.');
$person2->setDepartment('IT2');
$person2->setSalary(400000);
$this->_em->persist($person1);
$this->_em->persist($person2);
$this->_em->flush();
$this->_em->clear();
}
}

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\Tests\ORM\Tools\Console\Command;
use Doctrine\ORM\Tools\Console\ApplicationCompatibility;
use Doctrine\ORM\Tools\Console\Command\ClearCache\CollectionRegionCommand;
use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider;
use Doctrine\Tests\Models\Cache\State;
@@ -14,6 +15,8 @@ use Symfony\Component\Console\Tester\CommandTester;
/** @group DDC-2183 */
class ClearCacheCollectionRegionCommandTest extends OrmFunctionalTestCase
{
use ApplicationCompatibility;
/** @var Application */
private $application;
@@ -29,7 +32,7 @@ class ClearCacheCollectionRegionCommandTest extends OrmFunctionalTestCase
$this->command = new CollectionRegionCommand(new SingleManagerProvider($this->_em));
$this->application = new Application();
$this->application->add($this->command);
self::addCommandToApplication($this->application, $this->command);
}
public function testClearAllRegion(): void

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\Tests\ORM\Tools\Console\Command;
use Doctrine\ORM\Tools\Console\ApplicationCompatibility;
use Doctrine\ORM\Tools\Console\Command\ClearCache\EntityRegionCommand;
use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider;
use Doctrine\Tests\Models\Cache\Country;
@@ -17,6 +18,8 @@ use function trim;
/** @group DDC-2183 */
class ClearCacheEntityRegionCommandTest extends OrmFunctionalTestCase
{
use ApplicationCompatibility;
/** @var Application */
private $application;
@@ -32,7 +35,7 @@ class ClearCacheEntityRegionCommandTest extends OrmFunctionalTestCase
$this->command = new EntityRegionCommand(new SingleManagerProvider($this->_em));
$this->application = new Application();
$this->application->add($this->command);
self::addCommandToApplication($this->application, $this->command);
}
public function testClearAllRegion(): void

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\Tests\ORM\Tools\Console\Command;
use Doctrine\ORM\Tools\Console\ApplicationCompatibility;
use Doctrine\ORM\Tools\Console\Command\ClearCache\QueryRegionCommand;
use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider;
use Doctrine\Tests\OrmFunctionalTestCase;
@@ -13,6 +14,8 @@ use Symfony\Component\Console\Tester\CommandTester;
/** @group DDC-2183 */
class ClearCacheQueryRegionCommandTest extends OrmFunctionalTestCase
{
use ApplicationCompatibility;
/** @var Application */
private $application;
@@ -28,7 +31,7 @@ class ClearCacheQueryRegionCommandTest extends OrmFunctionalTestCase
$this->command = new QueryRegionCommand(new SingleManagerProvider($this->_em));
$this->application = new Application();
$this->application->add($this->command);
self::addCommandToApplication($this->application, $this->command);
}
public function testClearAllRegion(): void

View File

@@ -7,6 +7,7 @@ namespace Doctrine\Tests\ORM\Tools\Console\Command;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Tools\Console\ApplicationCompatibility;
use Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand;
use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider;
use Doctrine\Tests\DoctrineTestCase;
@@ -18,6 +19,8 @@ use function array_merge;
class EnsureProductionSettingsCommandTest extends DoctrineTestCase
{
use ApplicationCompatibility;
public function testExecute(): void
{
$em = $this->createMock(EntityManagerInterface::class);
@@ -103,7 +106,7 @@ class EnsureProductionSettingsCommandTest extends DoctrineTestCase
array $input = []
): int {
$application = new Application();
$application->add(new EnsureProductionSettingsCommand(new SingleManagerProvider($em)));
self::addCommandToApplication($application, new EnsureProductionSettingsCommand(new SingleManagerProvider($em)));
$command = $application->find('orm:ensure-production-settings');
$tester = new CommandTester($command);

View File

@@ -7,6 +7,7 @@ namespace Doctrine\Tests\ORM\Tools\Console\Command;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Tools\Console\ApplicationCompatibility;
use Doctrine\ORM\Tools\Console\Command\GenerateRepositoriesCommand;
use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider;
use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper;
@@ -35,6 +36,8 @@ use const DIRECTORY_SEPARATOR;
class GenerateRepositoriesCommandTest extends OrmFunctionalTestCase
{
use ApplicationCompatibility;
/** @var Application */
private $application;
@@ -53,7 +56,7 @@ class GenerateRepositoriesCommandTest extends OrmFunctionalTestCase
$metadataDriver->addPaths([__DIR__ . '/../../../../Models/DDC3231/']);
$this->application = new Application();
$this->application->add(new GenerateRepositoriesCommand(new SingleManagerProvider($this->_em)));
self::addCommandToApplication($this->application, new GenerateRepositoriesCommand(new SingleManagerProvider($this->_em)));
}
public function tearDown(): void
@@ -163,7 +166,7 @@ class GenerateRepositoriesCommandTest extends OrmFunctionalTestCase
$application = new Application();
$application->setHelperSet(new HelperSet(['em' => new EntityManagerHelper($em)]));
$application->add(new GenerateRepositoriesCommand());
self::addCommandToApplication($application, new GenerateRepositoriesCommand());
$command = $application->find('orm:generate-repositories');
$tester = new CommandTester($command);

View File

@@ -7,6 +7,7 @@ namespace Doctrine\Tests\ORM\Tools\Console\Command;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\ORM\Tools\Console\ApplicationCompatibility;
use Doctrine\ORM\Tools\Console\Command\InfoCommand;
use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider;
use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper;
@@ -20,6 +21,8 @@ use Symfony\Component\Console\Tester\CommandTester;
class InfoCommandTest extends OrmFunctionalTestCase
{
use ApplicationCompatibility;
/** @var Application */
private $application;
@@ -35,7 +38,7 @@ class InfoCommandTest extends OrmFunctionalTestCase
$this->application = new Application();
$this->application->add(new InfoCommand(new SingleManagerProvider($this->_em)));
self::addCommandToApplication($this->application, new InfoCommand(new SingleManagerProvider($this->_em)));
$this->command = $this->application->find('orm:info');
$this->tester = new CommandTester($this->command);
@@ -66,7 +69,7 @@ class InfoCommandTest extends OrmFunctionalTestCase
$application = new Application();
$application->setHelperSet(new HelperSet(['em' => new EntityManagerHelper($em)]));
$application->add(new InfoCommand());
self::addCommandToApplication($application, new InfoCommand());
$command = $application->find('orm:info');
$tester = new CommandTester($command);
@@ -105,7 +108,7 @@ class InfoCommandTest extends OrmFunctionalTestCase
$application = new Application();
$application->setHelperSet(new HelperSet(['em' => new EntityManagerHelper($em)]));
$application->add(new InfoCommand());
self::addCommandToApplication($application, new InfoCommand());
$command = $application->find('orm:info');
$tester = new CommandTester($command);

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\Tests\ORM\Tools\Console\Command;
use Doctrine\ORM\Tools\Console\ApplicationCompatibility;
use Doctrine\ORM\Tools\Console\Command\MappingDescribeCommand;
use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider;
use Doctrine\Tests\Models\Cache\AttractionInfo;
@@ -18,6 +19,8 @@ use Symfony\Component\Console\Tester\CommandTester;
*/
class MappingDescribeCommandTest extends OrmFunctionalTestCase
{
use ApplicationCompatibility;
/** @var Application */
private $application;
@@ -32,7 +35,7 @@ class MappingDescribeCommandTest extends OrmFunctionalTestCase
parent::setUp();
$this->application = new Application();
$this->application->add(new MappingDescribeCommand(new SingleManagerProvider($this->_em)));
self::addCommandToApplication($this->application, new MappingDescribeCommand(new SingleManagerProvider($this->_em)));
$this->command = $this->application->find('orm:mapping:describe');
$this->tester = new CommandTester($this->command);

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\Tests\ORM\Tools\Console\Command;
use Doctrine\ORM\Tools\Console\ApplicationCompatibility;
use Doctrine\ORM\Tools\Console\Command\RunDqlCommand;
use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider;
use Doctrine\Tests\Models\Generic\DateTimeModel;
@@ -20,6 +21,8 @@ use function trim;
*/
class RunDqlCommandTest extends OrmFunctionalTestCase
{
use ApplicationCompatibility;
/** @var Application */
private $application;
@@ -38,7 +41,7 @@ class RunDqlCommandTest extends OrmFunctionalTestCase
$this->command = new RunDqlCommand(new SingleManagerProvider($this->_em));
$this->application = new Application();
$this->application->add($this->command);
self::addCommandToApplication($this->application, $this->command);
$this->tester = new CommandTester($this->command);
}

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Doctrine\Tests\ORM\Tools\Console\Command;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\ORM\Tools\Console\ApplicationCompatibility;
use Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand;
use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider;
use Doctrine\Tests\OrmFunctionalTestCase;
@@ -19,6 +20,8 @@ use Symfony\Component\Console\Tester\CommandTester;
*/
class ValidateSchemaCommandTest extends OrmFunctionalTestCase
{
use ApplicationCompatibility;
/** @var ValidateSchemaCommand */
private $command;
@@ -34,7 +37,7 @@ class ValidateSchemaCommandTest extends OrmFunctionalTestCase
}
$application = new Application();
$application->add(new ValidateSchemaCommand(new SingleManagerProvider($this->_em)));
self::addCommandToApplication($application, new ValidateSchemaCommand(new SingleManagerProvider($this->_em)));
$this->command = $application->find('orm:validate-schema');
$this->tester = new CommandTester($this->command);