mirror of
https://github.com/doctrine/orm.git
synced 2026-04-25 15:38:10 +02:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f58984c43f | |||
| 79c7c5087e | |||
| 12c4560f1d | |||
| 152ebd756c | |||
| 82533af893 | |||
| b988137378 | |||
| ccfc97c32f | |||
| d386b43be3 | |||
| 0970ce7072 | |||
| 624c56be72 | |||
| fbc8e6741e | |||
| 7151db3cb8 | |||
| ac24c11808 | |||
| dd478d8662 | |||
| 0b3cd72609 | |||
| 85034699cb | |||
| d98186e2c4 | |||
| a0ed37954b | |||
| 4875f4c878 |
@@ -1,7 +1,7 @@
|
||||
| [3.0.x][3.0] | [2.16.x][2.16] | [2.15.x][2.15] |
|
||||
| [3.0.x][3.0] | [2.18.x][2.18] | [2.17.x][2.17] |
|
||||
|:----------------:|:----------------:|:----------:|
|
||||
| [![Build status][3.0 image]][3.0] | [![Build status][2.16 image]][2.16] | [![Build status][2.15 image]][2.15] |
|
||||
| [![Coverage Status][3.0 coverage image]][3.0 coverage]| [![Coverage Status][2.16 coverage image]][2.16 coverage] | [![Coverage Status][2.15 coverage image]][2.15 coverage] |
|
||||
| [![Build status][3.0 image]][3.0] | [![Build status][2.18 image]][2.18] | [![Build status][2.17 image]][2.17] |
|
||||
| [![Coverage Status][3.0 coverage image]][3.0 coverage]| [![Coverage Status][2.18 coverage image]][2.18 coverage] | [![Coverage Status][2.17 coverage image]][2.17 coverage] |
|
||||
|
||||
[<h1 align="center">🇺🇦 UKRAINE NEEDS YOUR HELP NOW!</h1>](https://www.doctrine-project.org/stop-war.html)
|
||||
|
||||
@@ -22,11 +22,11 @@ without requiring unnecessary code duplication.
|
||||
[3.0]: https://github.com/doctrine/orm/tree/3.0.x
|
||||
[3.0 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.0.x/graph/badge.svg
|
||||
[3.0 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.0.x
|
||||
[2.16 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.16.x
|
||||
[2.16]: https://github.com/doctrine/orm/tree/2.16.x
|
||||
[2.16 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.16.x/graph/badge.svg
|
||||
[2.16 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.16.x
|
||||
[2.15 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.15.x
|
||||
[2.15]: https://github.com/doctrine/orm/tree/2.15.x
|
||||
[2.15 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.15.x/graph/badge.svg
|
||||
[2.15 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.15.x
|
||||
[2.18 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.18.x
|
||||
[2.18]: https://github.com/doctrine/orm/tree/2.18.x
|
||||
[2.18 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.18.x/graph/badge.svg
|
||||
[2.18 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.18.x
|
||||
[2.17 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.17.x
|
||||
[2.17]: https://github.com/doctrine/orm/tree/2.17.x
|
||||
[2.17 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.17.x/graph/badge.svg
|
||||
[2.17 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.17.x
|
||||
|
||||
+2
-3
@@ -13,6 +13,5 @@ understand the assumptions we make.
|
||||
- [DBAL Security Page](https://www.doctrine-project.org/projects/doctrine-dbal/en/stable/reference/security.html)
|
||||
- [ORM Security Page](https://www.doctrine-project.org/projects/doctrine-orm/en/stable/reference/security.html)
|
||||
|
||||
If you find a Security bug in Doctrine, please report it on Jira and change the
|
||||
Security Level to "Security Issues". It will be visible to Doctrine Core
|
||||
developers and you only.
|
||||
If you find a Security bug in Doctrine, please follow our
|
||||
[Security reporting guidelines](https://www.doctrine-project.org/policies/security.html#reporting).
|
||||
|
||||
@@ -36,71 +36,50 @@ Our entities look like:
|
||||
namespace Bank\Entities;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
|
||||
#[ORM\Entity]
|
||||
class Account
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
private ?int $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", unique=true)
|
||||
*/
|
||||
private string $no;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="Entry", mappedBy="account", cascade={"persist"})
|
||||
*/
|
||||
private array $entries;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private int $maxCredit = 0;
|
||||
|
||||
public function __construct(string $no, int $maxCredit = 0)
|
||||
{
|
||||
$this->no = $no;
|
||||
$this->maxCredit = $maxCredit;
|
||||
$this->entries = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
|
||||
#[ORM\OneToMany(targetEntity: Entry::class, mappedBy: 'account', cascade: ['persist'])]
|
||||
private Collection $entries;
|
||||
|
||||
|
||||
public function __construct(
|
||||
#[ORM\Column(type: 'string', unique: true)]
|
||||
private string $no,
|
||||
|
||||
#[ORM\Column(type: 'integer')]
|
||||
private int $maxCredit = 0,
|
||||
) {
|
||||
$this->entries = new ArrayCollection();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
|
||||
#[ORM\Entity]
|
||||
class Entry
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
private ?int $id;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Account", inversedBy="entries")
|
||||
*/
|
||||
private Account $account;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private int $amount;
|
||||
|
||||
public function __construct(Account $account, int $amount)
|
||||
{
|
||||
$this->account = $account;
|
||||
$this->amount = $amount;
|
||||
|
||||
public function __construct(
|
||||
#[ORM\ManyToOne(targetEntity: Account::class, inversedBy: 'entries')]
|
||||
private Account $account,
|
||||
|
||||
#[ORM\Column(type: 'integer')]
|
||||
private int $amount,
|
||||
) {
|
||||
// more stuff here, from/to whom, stated reason, execution date and such
|
||||
}
|
||||
|
||||
|
||||
public function getAmount(): Amount
|
||||
{
|
||||
return $this->amount;
|
||||
@@ -193,9 +172,8 @@ relation with this method:
|
||||
public function addEntry(int $amount): void
|
||||
{
|
||||
$this->assertAcceptEntryAllowed($amount);
|
||||
|
||||
$e = new Entry($this, $amount);
|
||||
$this->entries[] = $e;
|
||||
|
||||
$this->entries[] = new Entry($this, $amount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,18 +191,18 @@ Now look at the following test-code for our entities:
|
||||
{
|
||||
$account = new Account("123456", maxCredit: 200);
|
||||
$this->assertEquals(0, $account->getBalance());
|
||||
|
||||
|
||||
$account->addEntry(500);
|
||||
$this->assertEquals(500, $account->getBalance());
|
||||
|
||||
|
||||
$account->addEntry(-700);
|
||||
$this->assertEquals(-200, $account->getBalance());
|
||||
}
|
||||
|
||||
|
||||
public function testExceedMaxLimit()
|
||||
{
|
||||
$account = new Account("123456", maxCredit: 200);
|
||||
|
||||
|
||||
$this->expectException(Exception::class);
|
||||
$account->addEntry(-1000);
|
||||
}
|
||||
@@ -285,22 +263,19 @@ entries collection) we want to add an aggregate field called
|
||||
<?php
|
||||
class Account
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
#[ORM\Column(type: 'integer')]
|
||||
private int $balance = 0;
|
||||
|
||||
|
||||
public function getBalance(): int
|
||||
{
|
||||
return $this->balance;
|
||||
}
|
||||
|
||||
|
||||
public function addEntry(int $amount): void
|
||||
{
|
||||
$this->assertAcceptEntryAllowed($amount);
|
||||
|
||||
$e = new Entry($this, $amount);
|
||||
$this->entries[] = $e;
|
||||
|
||||
$this->entries[] = new Entry($this, $amount);
|
||||
$this->balance += $amount;
|
||||
}
|
||||
}
|
||||
@@ -331,13 +306,13 @@ potentially lead to inconsistent state. See this example:
|
||||
// The Account $accId has a balance of 0 and a max credit limit of 200:
|
||||
// request 1 account
|
||||
$account1 = $em->find(Account::class, $accId);
|
||||
|
||||
|
||||
// request 2 account
|
||||
$account2 = $em->find(Account::class, $accId);
|
||||
|
||||
|
||||
$account1->addEntry(-200);
|
||||
$account2->addEntry(-200);
|
||||
|
||||
|
||||
// now request 1 and 2 both flush the changes.
|
||||
|
||||
The aggregate field ``Account::$balance`` is now -200, however the
|
||||
@@ -357,10 +332,8 @@ Optimistic locking is as easy as adding a version column:
|
||||
|
||||
class Account
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\Version
|
||||
*/
|
||||
#[ORM\Column(type: 'integer')]
|
||||
#[ORM\Version]
|
||||
private int $version;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,10 +47,8 @@ A Customer entity
|
||||
use Acme\CustomerModule\Entity\Customer as BaseCustomer;
|
||||
use Acme\InvoiceModule\Model\InvoiceSubjectInterface;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="customer")
|
||||
*/
|
||||
#[ORM\Entity]
|
||||
#[ORM\Table(name: 'customer')]
|
||||
class Customer extends BaseCustomer implements InvoiceSubjectInterface
|
||||
{
|
||||
// In our example, any methods defined in the InvoiceSubjectInterface
|
||||
@@ -69,19 +67,12 @@ An Invoice entity
|
||||
use Doctrine\ORM\Mapping AS ORM;
|
||||
use Acme\InvoiceModule\Model\InvoiceSubjectInterface;
|
||||
|
||||
/**
|
||||
* Represents an Invoice.
|
||||
*
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="invoice")
|
||||
*/
|
||||
#[ORM\Entity]
|
||||
#[ORM\Table(name: 'invoice')]
|
||||
class Invoice
|
||||
{
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Acme\InvoiceModule\Model\InvoiceSubjectInterface")
|
||||
* @var InvoiceSubjectInterface
|
||||
*/
|
||||
protected $subject;
|
||||
#[ORM\ManyToOne(targetEntity: InvoiceSubjectInterface::class)]
|
||||
protected InvoiceSubjectInterface $subject;
|
||||
}
|
||||
|
||||
An InvoiceSubjectInterface
|
||||
|
||||
@@ -12,9 +12,8 @@ page only handles Security issues in the ORM.
|
||||
|
||||
- `DBAL Security Page <https://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/security.html>`
|
||||
|
||||
If you find a Security bug in Doctrine, please report it on Jira and change the
|
||||
Security Level to "Security Issues". It will be visible to Doctrine Core
|
||||
developers and you only.
|
||||
If you find a Security bug in Doctrine, please follow our
|
||||
`Security reporting guidelines <https://www.doctrine-project.org/policies/security.html#reporting>`_.
|
||||
|
||||
User input and Doctrine ORM
|
||||
---------------------------
|
||||
|
||||
@@ -57,7 +57,15 @@ final class DefaultTypedFieldMapper implements TypedFieldMapper
|
||||
$mapping['enumType'] = $type->getName();
|
||||
|
||||
$reflection = new ReflectionEnum($type->getName());
|
||||
$type = $reflection->getBackingType();
|
||||
if (! $reflection->isBacked()) {
|
||||
throw MappingException::backedEnumTypeRequired(
|
||||
$field->class,
|
||||
$mapping['fieldName'],
|
||||
$mapping['enumType']
|
||||
);
|
||||
}
|
||||
|
||||
$type = $reflection->getBackingType();
|
||||
|
||||
assert($type instanceof ReflectionNamedType);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Mapping\Driver;
|
||||
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\MappingException;
|
||||
@@ -56,15 +55,6 @@ class XmlDriver extends FileDriver
|
||||
);
|
||||
}
|
||||
|
||||
if (! $isXsdValidationEnabled) {
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/6728',
|
||||
'Using XML mapping driver with XSD validation disabled is deprecated'
|
||||
. ' and will not be supported in Doctrine ORM 3.0.'
|
||||
);
|
||||
}
|
||||
|
||||
if ($isXsdValidationEnabled && ! extension_loaded('dom')) {
|
||||
throw new LogicException(
|
||||
'XSD validation cannot be enabled because the DOM extension is missing.'
|
||||
|
||||
@@ -954,6 +954,16 @@ class MappingException extends ORMException
|
||||
return new self(sprintf('Enum types require PHP 8.1 in %s::$%s', $className, $fieldName));
|
||||
}
|
||||
|
||||
public static function backedEnumTypeRequired(string $className, string $fieldName, string $enumType): self
|
||||
{
|
||||
return new self(sprintf(
|
||||
'Attempting to map a non-backed enum type %s in entity %s::$%s. Please use backed enums only',
|
||||
$enumType,
|
||||
$className,
|
||||
$fieldName
|
||||
));
|
||||
}
|
||||
|
||||
public static function nonEnumTypeMapped(string $className, string $fieldName, string $enumType): self
|
||||
{
|
||||
return new self(sprintf(
|
||||
|
||||
@@ -31,6 +31,7 @@ use Doctrine\ORM\Query\QueryException;
|
||||
use Doctrine\ORM\Repository\Exception\InvalidFindByCall;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Doctrine\ORM\Utility\IdentifierFlattener;
|
||||
use Doctrine\ORM\Utility\LockSqlHelper;
|
||||
use Doctrine\ORM\Utility\PersisterHelper;
|
||||
use LengthException;
|
||||
|
||||
@@ -92,6 +93,8 @@ use function trim;
|
||||
*/
|
||||
class BasicEntityPersister implements EntityPersister
|
||||
{
|
||||
use LockSqlHelper;
|
||||
|
||||
/** @var array<string,string> */
|
||||
private static $comparisonMap = [
|
||||
Comparison::EQ => '= %s',
|
||||
@@ -1116,11 +1119,11 @@ class BasicEntityPersister implements EntityPersister
|
||||
|
||||
switch ($lockMode) {
|
||||
case LockMode::PESSIMISTIC_READ:
|
||||
$lockSql = ' ' . $this->platform->getReadLockSQL();
|
||||
$lockSql = ' ' . $this->getReadLockSQL($this->platform);
|
||||
break;
|
||||
|
||||
case LockMode::PESSIMISTIC_WRITE:
|
||||
$lockSql = ' ' . $this->platform->getWriteLockSQL();
|
||||
$lockSql = ' ' . $this->getWriteLockSQL($this->platform);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1578,11 +1581,11 @@ class BasicEntityPersister implements EntityPersister
|
||||
|
||||
switch ($lockMode) {
|
||||
case LockMode::PESSIMISTIC_READ:
|
||||
$lockSql = $this->platform->getReadLockSQL();
|
||||
$lockSql = $this->getReadLockSQL($this->platform);
|
||||
|
||||
break;
|
||||
case LockMode::PESSIMISTIC_WRITE:
|
||||
$lockSql = $this->platform->getWriteLockSQL();
|
||||
$lockSql = $this->getWriteLockSQL($this->platform);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Internal\SQLResultCasing;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Utility\LockSqlHelper;
|
||||
use Doctrine\ORM\Utility\PersisterHelper;
|
||||
use LengthException;
|
||||
|
||||
@@ -26,6 +27,7 @@ use function implode;
|
||||
*/
|
||||
class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
{
|
||||
use LockSqlHelper;
|
||||
use SQLResultCasing;
|
||||
|
||||
/**
|
||||
@@ -316,12 +318,12 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
|
||||
switch ($lockMode) {
|
||||
case LockMode::PESSIMISTIC_READ:
|
||||
$lockSql = ' ' . $this->platform->getReadLockSQL();
|
||||
$lockSql = ' ' . $this->getReadLockSQL($this->platform);
|
||||
|
||||
break;
|
||||
|
||||
case LockMode::PESSIMISTIC_WRITE:
|
||||
$lockSql = ' ' . $this->platform->getWriteLockSQL();
|
||||
$lockSql = ' ' . $this->getWriteLockSQL($this->platform);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ use Doctrine\ORM\Mapping\QuoteStrategy;
|
||||
use Doctrine\ORM\OptimisticLockException;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Utility\HierarchyDiscriminatorResolver;
|
||||
use Doctrine\ORM\Utility\LockSqlHelper;
|
||||
use Doctrine\ORM\Utility\PersisterHelper;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
@@ -48,6 +49,8 @@ use function trim;
|
||||
*/
|
||||
class SqlWalker implements TreeWalker
|
||||
{
|
||||
use LockSqlHelper;
|
||||
|
||||
public const HINT_DISTINCT = 'doctrine.distinct';
|
||||
|
||||
/**
|
||||
@@ -577,11 +580,11 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
|
||||
if ($lockMode === LockMode::PESSIMISTIC_READ) {
|
||||
return $sql . ' ' . $this->platform->getReadLockSQL();
|
||||
return $sql . ' ' . $this->getReadLockSQL($this->platform);
|
||||
}
|
||||
|
||||
if ($lockMode === LockMode::PESSIMISTIC_WRITE) {
|
||||
return $sql . ' ' . $this->platform->getWriteLockSQL();
|
||||
return $sql . ' ' . $this->getWriteLockSQL($this->platform);
|
||||
}
|
||||
|
||||
if ($lockMode !== LockMode::OPTIMISTIC) {
|
||||
|
||||
@@ -257,7 +257,7 @@ class LimitSubqueryOutputWalker extends SqlWalker
|
||||
$innerSql
|
||||
);
|
||||
|
||||
// http://www.doctrine-project.org/jira/browse/DDC-1958
|
||||
// https://github.com/doctrine/orm/issues/2630
|
||||
$sql = $this->preserveSqlOrdering($sqlIdentifier, $innerSql, $sql, $orderByClause);
|
||||
|
||||
// Apply the limit and offset.
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Utility;
|
||||
|
||||
use Doctrine\DBAL\Platforms\AbstractMySQLPlatform;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Platforms\DB2Platform;
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
|
||||
use Doctrine\DBAL\Platforms\SqlitePlatform;
|
||||
use Doctrine\DBAL\Platforms\SQLServerPlatform;
|
||||
|
||||
/** @internal */
|
||||
trait LockSqlHelper
|
||||
{
|
||||
private function getReadLockSQL(AbstractPlatform $platform): string
|
||||
{
|
||||
if ($platform instanceof AbstractMySQLPlatform || $platform instanceof MySQLPlatform) {
|
||||
return 'LOCK IN SHARE MODE';
|
||||
}
|
||||
|
||||
if ($platform instanceof PostgreSQLPlatform) {
|
||||
return 'FOR SHARE';
|
||||
}
|
||||
|
||||
return $this->getWriteLockSQL($platform);
|
||||
}
|
||||
|
||||
private function getWriteLockSQL(AbstractPlatform $platform): string
|
||||
{
|
||||
if ($platform instanceof DB2Platform) {
|
||||
return 'WITH RR USE AND KEEP UPDATE LOCKS';
|
||||
}
|
||||
|
||||
if ($platform instanceof SqlitePlatform) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($platform instanceof SQLServerPlatform) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return 'FOR UPDATE';
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,8 @@ parameters:
|
||||
- '/Call to an undefined method Doctrine\\DBAL\\Connection::createSchemaManager\(\)\./'
|
||||
# Class name will change in DBAL 3.
|
||||
- '/^Class Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform not found\.$/'
|
||||
- '/^Class Doctrine\\DBAL\\Platforms\\AbstractMySQLPlatform not found\.$/'
|
||||
- '/^Class Doctrine\\DBAL\\Platforms\\MySQLPlatform not found\.$/'
|
||||
-
|
||||
message: '/Doctrine\\DBAL\\Platforms\\MyS(ql|QL)Platform/'
|
||||
path: lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php
|
||||
|
||||
@@ -27,6 +27,9 @@ parameters:
|
||||
-
|
||||
message: '/^Call to static method ensure\(\) on an unknown class Doctrine\\DBAL\\ForwardCompatibility\\Result\.$/'
|
||||
path: lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php
|
||||
-
|
||||
message: '/^Instanceof between Doctrine\\DBAL\\Platforms\\AbstractPlatform and Doctrine\\DBAL\\Platforms\\MySQLPlatform will always evaluate to false\.$/'
|
||||
path: lib/Doctrine/ORM/Utility/LockSqlHelper.php
|
||||
|
||||
# False positive
|
||||
-
|
||||
|
||||
@@ -27,6 +27,9 @@ parameters:
|
||||
-
|
||||
message: '/^Call to static method ensure\(\) on an unknown class Doctrine\\DBAL\\ForwardCompatibility\\Result\.$/'
|
||||
path: lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php
|
||||
-
|
||||
message: '/^Instanceof between Doctrine\\DBAL\\Platforms\\AbstractPlatform and Doctrine\\DBAL\\Platforms\\MySQLPlatform will always evaluate to false\.$/'
|
||||
path: lib/Doctrine/ORM/Utility/LockSqlHelper.php
|
||||
|
||||
# False positive
|
||||
-
|
||||
|
||||
@@ -88,6 +88,8 @@
|
||||
<referencedMethod name="Doctrine\DBAL\Platforms\AbstractPlatform::supportsForeignKeyConstraints"/>
|
||||
<!-- Remove on 3.0.x -->
|
||||
<referencedMethod name="Doctrine\DBAL\Connection::getEventManager"/>
|
||||
<referencedMethod name="Doctrine\DBAL\Platforms\AbstractPlatform::getReadLockSQL"/>
|
||||
<referencedMethod name="Doctrine\DBAL\Platforms\AbstractPlatform::getWriteLockSQL"/>
|
||||
<referencedMethod name="Doctrine\DBAL\Schema\Schema::visit"/>
|
||||
<referencedMethod name="Doctrine\DBAL\Schema\SchemaDiff::toSaveSql"/>
|
||||
<referencedMethod name="Doctrine\DBAL\Schema\SchemaDiff::toSql"/>
|
||||
@@ -158,6 +160,7 @@
|
||||
<errorLevel type="suppress">
|
||||
<!-- Class name changes in DBAL 3. -->
|
||||
<referencedClass name="Doctrine\DBAL\Platforms\PostgreSQLPlatform" />
|
||||
<referencedClass name="Doctrine\DBAL\Platforms\MySQLPlatform" />
|
||||
</errorLevel>
|
||||
</InvalidClass>
|
||||
<InvalidParamDefault>
|
||||
@@ -256,6 +259,7 @@
|
||||
<errorLevel type="suppress">
|
||||
<file name="lib/Doctrine/ORM/Internal/SQLResultCasing.php"/>
|
||||
<file name="lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php"/>
|
||||
<file name="lib/Doctrine/ORM/Utility/LockSqlHelper.php"/>
|
||||
</errorLevel>
|
||||
</TypeDoesNotContainType>
|
||||
<UndefinedClass>
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Models\Enums;
|
||||
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
|
||||
class FaultySwitch
|
||||
{
|
||||
#[Column(type: 'string')]
|
||||
public string $value;
|
||||
|
||||
/**
|
||||
* The following line is ignored on psalm and phpstan so that we can test
|
||||
* that the mapping is throwing an exception when a non-backed enum is used.
|
||||
*
|
||||
* @psalm-suppress InvalidArgument
|
||||
*/
|
||||
#[Column(enumType: SwitchStatus::class)]
|
||||
public SwitchStatus $status;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Models\Enums;
|
||||
|
||||
enum SwitchStatus
|
||||
{
|
||||
case ON;
|
||||
case OFF;
|
||||
}
|
||||
@@ -18,6 +18,10 @@ class EagerFetchCollectionTest extends OrmFunctionalTestCase
|
||||
parent::setUp();
|
||||
|
||||
$this->createSchemaForModels(EagerFetchOwner::class, EagerFetchChild::class);
|
||||
|
||||
// Ensure tables are empty
|
||||
$this->_em->getRepository(EagerFetchChild::class)->createQueryBuilder('o')->delete()->getQuery()->execute();
|
||||
$this->_em->getRepository(EagerFetchOwner::class)->createQueryBuilder('o')->delete()->getQuery()->execute();
|
||||
}
|
||||
|
||||
public function testEagerFetchMode(): void
|
||||
@@ -91,9 +95,11 @@ class EagerFetchCollectionTest extends OrmFunctionalTestCase
|
||||
$this->_em->clear();
|
||||
|
||||
$iterable = $this->_em->getRepository(EagerFetchOwner::class)->createQueryBuilder('o')->getQuery()->toIterable();
|
||||
$owner = $iterable->current();
|
||||
|
||||
$this->assertCount(2, $owner->children);
|
||||
// There is only a single record, but use a foreach to ensure the iterator is marked as finished and the table lock is released
|
||||
foreach ($iterable as $owner) {
|
||||
$this->assertCount(2, $owner->children);
|
||||
}
|
||||
}
|
||||
|
||||
protected function createOwnerWithChildren(int $children): EagerFetchOwner
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Doctrine\Tests\ORM\Functional\Locking;
|
||||
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\DBAL\Platforms\SQLitePlatform;
|
||||
use Doctrine\ORM\OptimisticLockException;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\TransactionRequiredException;
|
||||
@@ -168,9 +169,7 @@ class LockTest extends OrmFunctionalTestCase
|
||||
*/
|
||||
public function testLockPessimisticWrite(): void
|
||||
{
|
||||
$writeLockSql = $this->_em->getConnection()->getDatabasePlatform()->getWriteLockSQL();
|
||||
|
||||
if (! $writeLockSql) {
|
||||
if ($this->_em->getConnection()->getDatabasePlatform() instanceof SQLitePlatform) {
|
||||
self::markTestSkipped('Database Driver has no Write Lock support.');
|
||||
}
|
||||
|
||||
@@ -195,7 +194,7 @@ class LockTest extends OrmFunctionalTestCase
|
||||
$lastLoggedQuery = $this->getLastLoggedQuery(1)['sql'];
|
||||
}
|
||||
|
||||
self::assertStringContainsString($writeLockSql, $lastLoggedQuery);
|
||||
self::assertStringContainsString('FOR UPDATE', $lastLoggedQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -203,9 +202,7 @@ class LockTest extends OrmFunctionalTestCase
|
||||
*/
|
||||
public function testRefreshWithLockPessimisticWrite(): void
|
||||
{
|
||||
$writeLockSql = $this->_em->getConnection()->getDatabasePlatform()->getWriteLockSQL();
|
||||
|
||||
if (! $writeLockSql) {
|
||||
if ($this->_em->getConnection()->getDatabasePlatform() instanceof SQLitePlatform) {
|
||||
self::markTestSkipped('Database Driver has no Write Lock support.');
|
||||
}
|
||||
|
||||
@@ -230,15 +227,13 @@ class LockTest extends OrmFunctionalTestCase
|
||||
$lastLoggedQuery = $this->getLastLoggedQuery(1)['sql'];
|
||||
}
|
||||
|
||||
self::assertStringContainsString($writeLockSql, $lastLoggedQuery);
|
||||
self::assertStringContainsString('FOR UPDATE', $lastLoggedQuery);
|
||||
}
|
||||
|
||||
/** @group DDC-178 */
|
||||
public function testLockPessimisticRead(): void
|
||||
{
|
||||
$readLockSql = $this->_em->getConnection()->getDatabasePlatform()->getReadLockSQL();
|
||||
|
||||
if (! $readLockSql) {
|
||||
if ($this->_em->getConnection()->getDatabasePlatform() instanceof SQLitePlatform) {
|
||||
self::markTestSkipped('Database Driver has no Write Lock support.');
|
||||
}
|
||||
|
||||
@@ -264,7 +259,11 @@ class LockTest extends OrmFunctionalTestCase
|
||||
$lastLoggedQuery = $this->getLastLoggedQuery(1)['sql'];
|
||||
}
|
||||
|
||||
self::assertStringContainsString($readLockSql, $lastLoggedQuery);
|
||||
self::assertThat($lastLoggedQuery, self::logicalOr(
|
||||
self::stringContains('FOR UPDATE'),
|
||||
self::stringContains('FOR SHARE'),
|
||||
self::stringContains('LOCK IN SHARE MODE')
|
||||
));
|
||||
}
|
||||
|
||||
/** @group DDC-1693 */
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Internal;
|
||||
|
||||
class Node
|
||||
{
|
||||
/** @var string */
|
||||
public $name;
|
||||
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
}
|
||||
@@ -287,14 +287,3 @@ class TopologicalSortTest extends OrmTestCase
|
||||
}, array_values($this->topologicalSort->sort()));
|
||||
}
|
||||
}
|
||||
|
||||
class Node
|
||||
{
|
||||
/** @var string */
|
||||
public $name;
|
||||
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Mapping;
|
||||
|
||||
use Doctrine\ORM\Mapping\DefaultTypedFieldMapper;
|
||||
use Doctrine\ORM\Mapping\MappingException;
|
||||
use Doctrine\Tests\Models\Enums\FaultySwitch;
|
||||
use Doctrine\Tests\OrmTestCase;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* @requires PHP >= 8.1
|
||||
*/
|
||||
class TypedEnumFieldMapperTest extends OrmTestCase
|
||||
{
|
||||
private static function defaultTypedFieldMapper(): DefaultTypedFieldMapper
|
||||
{
|
||||
return new DefaultTypedFieldMapper();
|
||||
}
|
||||
|
||||
public function testNotBackedEnumThrows(): void
|
||||
{
|
||||
$reflectionClass = new ReflectionClass(FaultySwitch::class);
|
||||
|
||||
$this->expectException(MappingException::class);
|
||||
$this->expectExceptionMessage(
|
||||
'Attempting to map a non-backed enum type Doctrine\Tests\Models\Enums\SwitchStatus in entity Doctrine\Tests\Models\Enums\FaultySwitch::$status. Please use backed enums only'
|
||||
);
|
||||
|
||||
self::defaultTypedFieldMapper()->validateAndComplete(['fieldName' => 'status'], $reflectionClass->getProperty('status'));
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Tools\Pagination;
|
||||
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Platforms\OraclePlatform;
|
||||
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
|
||||
@@ -18,273 +19,212 @@ class_exists('Doctrine\DBAL\Platforms\PostgreSqlPlatform');
|
||||
|
||||
final class LimitSubqueryOutputWalkerTest extends PaginationTestCase
|
||||
{
|
||||
/**
|
||||
* @var AbstractPlatform|null
|
||||
*/
|
||||
private $originalDatabasePlatform;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->originalDatabasePlatform = $this->entityManager->getConnection()->getDatabasePlatform();
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
if ($this->originalDatabasePlatform) {
|
||||
$this->entityManager->getConnection()->setDatabasePlatform($this->originalDatabasePlatform);
|
||||
}
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
private function replaceDatabasePlatform(AbstractPlatform $platform): void
|
||||
{
|
||||
$this->entityManager->getConnection()->setDatabasePlatform($platform);
|
||||
}
|
||||
|
||||
public function testLimitSubquery(): void
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN p.category c JOIN p.author a'
|
||||
);
|
||||
$query->expireQueryCache(true);
|
||||
$limitQuery = clone $query;
|
||||
$limitQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
$query = $this->createQuery('SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN p.category c JOIN p.author a');
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT id_0 FROM (SELECT m0_.id AS id_0, m0_.title AS title_1, c1_.id AS id_2, a2_.id AS id_3, a2_.name AS name_4, m0_.author_id AS author_id_5, m0_.category_id AS category_id_6 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result',
|
||||
$limitQuery->getSQL()
|
||||
'SELECT DISTINCT id_0 FROM (SELECT m0_.id AS id_0, m0_.title AS title_1, c1_.id AS id_2, a2_.id AS id_3, a2_.name AS name_4, m0_.author_id AS author_id_5, m0_.category_id AS category_id_6 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result LIMIT 20 OFFSET 10',
|
||||
$query->getSQL()
|
||||
);
|
||||
}
|
||||
|
||||
public function testLimitSubqueryWithSortPg(): void
|
||||
{
|
||||
$odp = $this->entityManager->getConnection()->getDatabasePlatform();
|
||||
$this->entityManager->getConnection()->setDatabasePlatform(new PostgreSQLPlatform());
|
||||
$this->replaceDatabasePlatform(new PostgreSQLPlatform());
|
||||
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN p.category c JOIN p.author a ORDER BY p.title'
|
||||
);
|
||||
$limitQuery = clone $query;
|
||||
$limitQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
$query = $this->createQuery('SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN p.category c JOIN p.author a ORDER BY p.title');
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT id_0, MIN(sclr_5) AS dctrn_minrownum FROM (SELECT m0_.id AS id_0, m0_.title AS title_1, c1_.id AS id_2, a2_.id AS id_3, a2_.name AS name_4, ROW_NUMBER() OVER(ORDER BY m0_.title ASC) AS sclr_5, m0_.author_id AS author_id_6, m0_.category_id AS category_id_7 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC',
|
||||
$limitQuery->getSQL()
|
||||
'SELECT DISTINCT id_0, MIN(sclr_5) AS dctrn_minrownum FROM (SELECT m0_.id AS id_0, m0_.title AS title_1, c1_.id AS id_2, a2_.id AS id_3, a2_.name AS name_4, ROW_NUMBER() OVER(ORDER BY m0_.title ASC) AS sclr_5, m0_.author_id AS author_id_6, m0_.category_id AS category_id_7 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC LIMIT 20 OFFSET 10',
|
||||
$query->getSQL()
|
||||
);
|
||||
|
||||
$this->entityManager->getConnection()->setDatabasePlatform($odp);
|
||||
}
|
||||
|
||||
public function testLimitSubqueryWithScalarSortPg(): void
|
||||
{
|
||||
$odp = $this->entityManager->getConnection()->getDatabasePlatform();
|
||||
$this->entityManager->getConnection()->setDatabasePlatform(new PostgreSQLPlatform());
|
||||
$this->replaceDatabasePlatform(new PostgreSQLPlatform());
|
||||
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity'
|
||||
);
|
||||
$limitQuery = clone $query;
|
||||
$limitQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
$query = $this->createQuery('SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity');
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT id_1, MIN(sclr_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS sclr_0, u1_.id AS id_1, g0_.id AS id_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC) AS sclr_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY id_1 ORDER BY dctrn_minrownum ASC',
|
||||
$limitQuery->getSQL()
|
||||
'SELECT DISTINCT id_1, MIN(sclr_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS sclr_0, u1_.id AS id_1, g0_.id AS id_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC) AS sclr_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY id_1 ORDER BY dctrn_minrownum ASC LIMIT 20 OFFSET 10',
|
||||
$query->getSQL()
|
||||
);
|
||||
|
||||
$this->entityManager->getConnection()->setDatabasePlatform($odp);
|
||||
}
|
||||
|
||||
public function testLimitSubqueryWithMixedSortPg(): void
|
||||
{
|
||||
$odp = $this->entityManager->getConnection()->getDatabasePlatform();
|
||||
$this->entityManager->getConnection()->setDatabasePlatform(new PostgreSQLPlatform());
|
||||
$this->replaceDatabasePlatform(new PostgreSQLPlatform());
|
||||
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity, u.id DESC'
|
||||
);
|
||||
$limitQuery = clone $query;
|
||||
$limitQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
$query = $this->createQuery('SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity, u.id DESC');
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT id_1, MIN(sclr_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS sclr_0, u1_.id AS id_1, g0_.id AS id_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC, u1_.id DESC) AS sclr_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY id_1 ORDER BY dctrn_minrownum ASC',
|
||||
$limitQuery->getSQL()
|
||||
'SELECT DISTINCT id_1, MIN(sclr_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS sclr_0, u1_.id AS id_1, g0_.id AS id_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC, u1_.id DESC) AS sclr_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY id_1 ORDER BY dctrn_minrownum ASC LIMIT 20 OFFSET 10',
|
||||
$query->getSQL()
|
||||
);
|
||||
|
||||
$this->entityManager->getConnection()->setDatabasePlatform($odp);
|
||||
}
|
||||
|
||||
public function testLimitSubqueryWithHiddenScalarSortPg(): void
|
||||
{
|
||||
$odp = $this->entityManager->getConnection()->getDatabasePlatform();
|
||||
$this->entityManager->getConnection()->setDatabasePlatform(new PostgreSQLPlatform());
|
||||
$this->replaceDatabasePlatform(new PostgreSQLPlatform());
|
||||
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT u, g, COUNT(g.id) AS hidden g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity, u.id DESC'
|
||||
);
|
||||
$limitQuery = clone $query;
|
||||
$limitQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
$query = $this->createQuery('SELECT u, g, COUNT(g.id) AS hidden g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity, u.id DESC');
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT id_1, MIN(sclr_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS sclr_0, u1_.id AS id_1, g0_.id AS id_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC, u1_.id DESC) AS sclr_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY id_1 ORDER BY dctrn_minrownum ASC',
|
||||
$limitQuery->getSQL()
|
||||
'SELECT DISTINCT id_1, MIN(sclr_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS sclr_0, u1_.id AS id_1, g0_.id AS id_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC, u1_.id DESC) AS sclr_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY id_1 ORDER BY dctrn_minrownum ASC LIMIT 20 OFFSET 10',
|
||||
$query->getSQL()
|
||||
);
|
||||
|
||||
$this->entityManager->getConnection()->setDatabasePlatform($odp);
|
||||
}
|
||||
|
||||
public function testLimitSubqueryPg(): void
|
||||
{
|
||||
$odp = $this->entityManager->getConnection()->getDatabasePlatform();
|
||||
$this->entityManager->getConnection()->setDatabasePlatform(new PostgreSQLPlatform());
|
||||
$this->replaceDatabasePlatform(new PostgreSQLPlatform());
|
||||
|
||||
$this->testLimitSubquery();
|
||||
|
||||
$this->entityManager->getConnection()->setDatabasePlatform($odp);
|
||||
}
|
||||
|
||||
public function testLimitSubqueryWithSortOracle(): void
|
||||
{
|
||||
$odp = $this->entityManager->getConnection()->getDatabasePlatform();
|
||||
$this->entityManager->getConnection()->setDatabasePlatform(new OraclePlatform());
|
||||
$this->replaceDatabasePlatform(new OraclePlatform());
|
||||
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN p.category c JOIN p.author a ORDER BY p.title'
|
||||
);
|
||||
$query->expireQueryCache(true);
|
||||
$limitQuery = clone $query;
|
||||
$limitQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
$query = $this->createQuery('SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN p.category c JOIN p.author a ORDER BY p.title');
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT ID_0, MIN(SCLR_5) AS dctrn_minrownum FROM (SELECT m0_.id AS ID_0, m0_.title AS TITLE_1, c1_.id AS ID_2, a2_.id AS ID_3, a2_.name AS NAME_4, ROW_NUMBER() OVER(ORDER BY m0_.title ASC) AS SCLR_5, m0_.author_id AS AUTHOR_ID_6, m0_.category_id AS CATEGORY_ID_7 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result GROUP BY ID_0 ORDER BY dctrn_minrownum ASC',
|
||||
$limitQuery->getSQL()
|
||||
'SELECT * FROM (SELECT a.*, ROWNUM AS doctrine_rownum FROM (SELECT DISTINCT ID_0, MIN(SCLR_5) AS dctrn_minrownum FROM (SELECT m0_.id AS ID_0, m0_.title AS TITLE_1, c1_.id AS ID_2, a2_.id AS ID_3, a2_.name AS NAME_4, ROW_NUMBER() OVER(ORDER BY m0_.title ASC) AS SCLR_5, m0_.author_id AS AUTHOR_ID_6, m0_.category_id AS CATEGORY_ID_7 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result GROUP BY ID_0 ORDER BY dctrn_minrownum ASC) a WHERE ROWNUM <= 30) WHERE doctrine_rownum >= 11',
|
||||
$query->getSQL()
|
||||
);
|
||||
|
||||
$this->entityManager->getConnection()->setDatabasePlatform($odp);
|
||||
}
|
||||
|
||||
public function testLimitSubqueryWithScalarSortOracle(): void
|
||||
{
|
||||
$odp = $this->entityManager->getConnection()->getDatabasePlatform();
|
||||
$this->entityManager->getConnection()->setDatabasePlatform(new OraclePlatform());
|
||||
$this->replaceDatabasePlatform(new OraclePlatform());
|
||||
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity'
|
||||
);
|
||||
$query->expireQueryCache(true);
|
||||
$limitQuery = clone $query;
|
||||
$limitQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
$query = $this->createQuery('SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity');
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT ID_1, MIN(SCLR_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS SCLR_0, u1_.id AS ID_1, g0_.id AS ID_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC) AS SCLR_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY ID_1 ORDER BY dctrn_minrownum ASC',
|
||||
$limitQuery->getSQL()
|
||||
'SELECT * FROM (SELECT a.*, ROWNUM AS doctrine_rownum FROM (SELECT DISTINCT ID_1, MIN(SCLR_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS SCLR_0, u1_.id AS ID_1, g0_.id AS ID_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC) AS SCLR_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY ID_1 ORDER BY dctrn_minrownum ASC) a WHERE ROWNUM <= 30) WHERE doctrine_rownum >= 11',
|
||||
$query->getSQL()
|
||||
);
|
||||
|
||||
$this->entityManager->getConnection()->setDatabasePlatform($odp);
|
||||
}
|
||||
|
||||
public function testLimitSubqueryWithMixedSortOracle(): void
|
||||
{
|
||||
$odp = $this->entityManager->getConnection()->getDatabasePlatform();
|
||||
$this->entityManager->getConnection()->setDatabasePlatform(new OraclePlatform());
|
||||
$this->replaceDatabasePlatform(new OraclePlatform());
|
||||
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity, u.id DESC'
|
||||
);
|
||||
$query->expireQueryCache(true);
|
||||
$limitQuery = clone $query;
|
||||
$limitQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
$query = $this->createQuery('SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity, u.id DESC');
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT ID_1, MIN(SCLR_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS SCLR_0, u1_.id AS ID_1, g0_.id AS ID_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC, u1_.id DESC) AS SCLR_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY ID_1 ORDER BY dctrn_minrownum ASC',
|
||||
$limitQuery->getSQL()
|
||||
'SELECT * FROM (SELECT a.*, ROWNUM AS doctrine_rownum FROM (SELECT DISTINCT ID_1, MIN(SCLR_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS SCLR_0, u1_.id AS ID_1, g0_.id AS ID_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC, u1_.id DESC) AS SCLR_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY ID_1 ORDER BY dctrn_minrownum ASC) a WHERE ROWNUM <= 30) WHERE doctrine_rownum >= 11',
|
||||
$query->getSQL()
|
||||
);
|
||||
|
||||
$this->entityManager->getConnection()->setDatabasePlatform($odp);
|
||||
}
|
||||
|
||||
public function testLimitSubqueryOracle(): void
|
||||
{
|
||||
$odp = $this->entityManager->getConnection()->getDatabasePlatform();
|
||||
$this->entityManager->getConnection()->setDatabasePlatform(new OraclePlatform());
|
||||
$this->replaceDatabasePlatform(new OraclePlatform());
|
||||
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN p.category c JOIN p.author a'
|
||||
);
|
||||
$query->expireQueryCache(true);
|
||||
$limitQuery = clone $query;
|
||||
$limitQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
$query = $this->createQuery('SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN p.category c JOIN p.author a');
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT ID_0 FROM (SELECT m0_.id AS ID_0, m0_.title AS TITLE_1, c1_.id AS ID_2, a2_.id AS ID_3, a2_.name AS NAME_4, m0_.author_id AS AUTHOR_ID_5, m0_.category_id AS CATEGORY_ID_6 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result',
|
||||
$limitQuery->getSQL()
|
||||
'SELECT * FROM (SELECT a.*, ROWNUM AS doctrine_rownum FROM (SELECT DISTINCT ID_0 FROM (SELECT m0_.id AS ID_0, m0_.title AS TITLE_1, c1_.id AS ID_2, a2_.id AS ID_3, a2_.name AS NAME_4, m0_.author_id AS AUTHOR_ID_5, m0_.category_id AS CATEGORY_ID_6 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result) a WHERE ROWNUM <= 30) WHERE doctrine_rownum >= 11',
|
||||
$query->getSQL()
|
||||
);
|
||||
|
||||
$this->entityManager->getConnection()->setDatabasePlatform($odp);
|
||||
}
|
||||
|
||||
public function testCountQueryMixedResultsWithName(): void
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT a, sum(a.name) as foo FROM Doctrine\Tests\ORM\Tools\Pagination\Author a'
|
||||
);
|
||||
$limitQuery = clone $query;
|
||||
$limitQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
$query = $this->createQuery('SELECT a, sum(a.name) as foo FROM Doctrine\Tests\ORM\Tools\Pagination\Author a');
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT id_0 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, sum(a0_.name) AS sclr_2 FROM Author a0_) dctrn_result',
|
||||
$limitQuery->getSQL()
|
||||
'SELECT DISTINCT id_0 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, sum(a0_.name) AS sclr_2 FROM Author a0_) dctrn_result LIMIT 20 OFFSET 10',
|
||||
$query->getSQL()
|
||||
);
|
||||
}
|
||||
|
||||
/** @group DDC-3336 */
|
||||
public function testCountQueryWithArithmeticOrderByCondition(): void
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT a FROM Doctrine\Tests\ORM\Tools\Pagination\Author a ORDER BY (1 - 1000) * 1 DESC'
|
||||
);
|
||||
$this->entityManager->getConnection()->setDatabasePlatform(new MySQLPlatform());
|
||||
$this->replaceDatabasePlatform(new MySQLPlatform());
|
||||
|
||||
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
$query = $this->createQuery('SELECT a FROM Doctrine\Tests\ORM\Tools\Pagination\Author a ORDER BY (1 - 1000) * 1 DESC');
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, (1 - 1000) * 1 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1 FROM Author a0_) dctrn_result_inner ORDER BY (1 - 1000) * 1 DESC) dctrn_result',
|
||||
'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, (1 - 1000) * 1 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1 FROM Author a0_) dctrn_result_inner ORDER BY (1 - 1000) * 1 DESC) dctrn_result LIMIT 20 OFFSET 10',
|
||||
$query->getSQL()
|
||||
);
|
||||
}
|
||||
|
||||
public function testCountQueryWithComplexScalarOrderByItemWithoutJoin(): void
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT a FROM Doctrine\Tests\ORM\Tools\Pagination\Avatar a ORDER BY a.imageHeight * a.imageWidth DESC'
|
||||
);
|
||||
$this->entityManager->getConnection()->setDatabasePlatform(new MySQLPlatform());
|
||||
$this->replaceDatabasePlatform(new MySQLPlatform());
|
||||
|
||||
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
$query = $this->createQuery('SELECT a FROM Doctrine\Tests\ORM\Tools\Pagination\Avatar a ORDER BY a.imageHeight * a.imageWidth DESC');
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, imageHeight_2 * imageWidth_3 FROM (SELECT a0_.id AS id_0, a0_.image AS image_1, a0_.imageHeight AS imageHeight_2, a0_.imageWidth AS imageWidth_3, a0_.imageAltDesc AS imageAltDesc_4, a0_.user_id AS user_id_5 FROM Avatar a0_) dctrn_result_inner ORDER BY imageHeight_2 * imageWidth_3 DESC) dctrn_result',
|
||||
'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, imageHeight_2 * imageWidth_3 FROM (SELECT a0_.id AS id_0, a0_.image AS image_1, a0_.imageHeight AS imageHeight_2, a0_.imageWidth AS imageWidth_3, a0_.imageAltDesc AS imageAltDesc_4, a0_.user_id AS user_id_5 FROM Avatar a0_) dctrn_result_inner ORDER BY imageHeight_2 * imageWidth_3 DESC) dctrn_result LIMIT 20 OFFSET 10',
|
||||
$query->getSQL()
|
||||
);
|
||||
}
|
||||
|
||||
public function testCountQueryWithComplexScalarOrderByItemJoinedWithoutPartial(): void
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT u FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.avatar a ORDER BY a.imageHeight * a.imageWidth DESC'
|
||||
);
|
||||
$this->entityManager->getConnection()->setDatabasePlatform(new MySQLPlatform());
|
||||
$this->replaceDatabasePlatform(new MySQLPlatform());
|
||||
|
||||
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
$query = $this->createQuery('SELECT u FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.avatar a ORDER BY a.imageHeight * a.imageWidth DESC');
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, imageHeight_3 * imageWidth_4 FROM (SELECT u0_.id AS id_0, a1_.id AS id_1, a1_.image AS image_2, a1_.imageHeight AS imageHeight_3, a1_.imageWidth AS imageWidth_4, a1_.imageAltDesc AS imageAltDesc_5, a1_.user_id AS user_id_6 FROM User u0_ INNER JOIN Avatar a1_ ON u0_.id = a1_.user_id) dctrn_result_inner ORDER BY imageHeight_3 * imageWidth_4 DESC) dctrn_result',
|
||||
'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, imageHeight_3 * imageWidth_4 FROM (SELECT u0_.id AS id_0, a1_.id AS id_1, a1_.image AS image_2, a1_.imageHeight AS imageHeight_3, a1_.imageWidth AS imageWidth_4, a1_.imageAltDesc AS imageAltDesc_5, a1_.user_id AS user_id_6 FROM User u0_ INNER JOIN Avatar a1_ ON u0_.id = a1_.user_id) dctrn_result_inner ORDER BY imageHeight_3 * imageWidth_4 DESC) dctrn_result LIMIT 20 OFFSET 10',
|
||||
$query->getSQL()
|
||||
);
|
||||
}
|
||||
|
||||
public function testCountQueryWithComplexScalarOrderByItemJoinedWithPartial(): void
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT u, partial a.{id, imageAltDesc} FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.avatar a ORDER BY a.imageHeight * a.imageWidth DESC'
|
||||
);
|
||||
$this->entityManager->getConnection()->setDatabasePlatform(new MySQLPlatform());
|
||||
$this->replaceDatabasePlatform(new MySQLPlatform());
|
||||
|
||||
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
$query = $this->createQuery('SELECT u, partial a.{id, imageAltDesc} FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.avatar a ORDER BY a.imageHeight * a.imageWidth DESC');
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, imageHeight_5 * imageWidth_6 FROM (SELECT u0_.id AS id_0, a1_.id AS id_1, a1_.imageAltDesc AS imageAltDesc_2, a1_.id AS id_3, a1_.image AS image_4, a1_.imageHeight AS imageHeight_5, a1_.imageWidth AS imageWidth_6, a1_.imageAltDesc AS imageAltDesc_7, a1_.user_id AS user_id_8 FROM User u0_ INNER JOIN Avatar a1_ ON u0_.id = a1_.user_id) dctrn_result_inner ORDER BY imageHeight_5 * imageWidth_6 DESC) dctrn_result',
|
||||
'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, imageHeight_5 * imageWidth_6 FROM (SELECT u0_.id AS id_0, a1_.id AS id_1, a1_.imageAltDesc AS imageAltDesc_2, a1_.id AS id_3, a1_.image AS image_4, a1_.imageHeight AS imageHeight_5, a1_.imageWidth AS imageWidth_6, a1_.imageAltDesc AS imageAltDesc_7, a1_.user_id AS user_id_8 FROM User u0_ INNER JOIN Avatar a1_ ON u0_.id = a1_.user_id) dctrn_result_inner ORDER BY imageHeight_5 * imageWidth_6 DESC) dctrn_result LIMIT 20 OFFSET 10',
|
||||
$query->getSQL()
|
||||
);
|
||||
}
|
||||
|
||||
public function testCountQueryWithComplexScalarOrderByItemOracle(): void
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT a FROM Doctrine\Tests\ORM\Tools\Pagination\Avatar a ORDER BY a.imageHeight * a.imageWidth DESC'
|
||||
);
|
||||
$this->entityManager->getConnection()->setDatabasePlatform(new OraclePlatform());
|
||||
$this->replaceDatabasePlatform(new OraclePlatform());
|
||||
|
||||
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
$query = $this->createQuery('SELECT a FROM Doctrine\Tests\ORM\Tools\Pagination\Avatar a ORDER BY a.imageHeight * a.imageWidth DESC');
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT ID_0, MIN(SCLR_5) AS dctrn_minrownum FROM (SELECT a0_.id AS ID_0, a0_.image AS IMAGE_1, a0_.imageHeight AS IMAGEHEIGHT_2, a0_.imageWidth AS IMAGEWIDTH_3, a0_.imageAltDesc AS IMAGEALTDESC_4, ROW_NUMBER() OVER(ORDER BY a0_.imageHeight * a0_.imageWidth DESC) AS SCLR_5, a0_.user_id AS USER_ID_6 FROM Avatar a0_) dctrn_result GROUP BY ID_0 ORDER BY dctrn_minrownum ASC',
|
||||
'SELECT * FROM (SELECT a.*, ROWNUM AS doctrine_rownum FROM (SELECT DISTINCT ID_0, MIN(SCLR_5) AS dctrn_minrownum FROM (SELECT a0_.id AS ID_0, a0_.image AS IMAGE_1, a0_.imageHeight AS IMAGEHEIGHT_2, a0_.imageWidth AS IMAGEWIDTH_3, a0_.imageAltDesc AS IMAGEALTDESC_4, ROW_NUMBER() OVER(ORDER BY a0_.imageHeight * a0_.imageWidth DESC) AS SCLR_5, a0_.user_id AS USER_ID_6 FROM Avatar a0_) dctrn_result GROUP BY ID_0 ORDER BY dctrn_minrownum ASC) a WHERE ROWNUM <= 30) WHERE doctrine_rownum >= 11',
|
||||
$query->getSQL()
|
||||
);
|
||||
}
|
||||
@@ -292,75 +232,65 @@ final class LimitSubqueryOutputWalkerTest extends PaginationTestCase
|
||||
/** @group DDC-3434 */
|
||||
public function testLimitSubqueryWithHiddenSelectionInOrderBy(): void
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
$query = $this->createQuery(
|
||||
'SELECT a, a.name AS HIDDEN ord FROM Doctrine\Tests\ORM\Tools\Pagination\Author a ORDER BY ord DESC'
|
||||
);
|
||||
|
||||
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, name_2 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, a0_.name AS name_2 FROM Author a0_) dctrn_result_inner ORDER BY name_2 DESC) dctrn_result',
|
||||
'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, name_2 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, a0_.name AS name_2 FROM Author a0_) dctrn_result_inner ORDER BY name_2 DESC) dctrn_result LIMIT 20 OFFSET 10',
|
||||
$query->getSQL()
|
||||
);
|
||||
}
|
||||
|
||||
public function testLimitSubqueryWithColumnWithSortDirectionInName(): void
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT a FROM Doctrine\Tests\ORM\Tools\Pagination\Avatar a ORDER BY a.imageAltDesc DESC'
|
||||
);
|
||||
$this->entityManager->getConnection()->setDatabasePlatform(new MySQLPlatform());
|
||||
|
||||
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
$this->replaceDatabasePlatform(new MySQLPlatform());
|
||||
$query = $this->createQuery('SELECT a FROM Doctrine\Tests\ORM\Tools\Pagination\Avatar a ORDER BY a.imageAltDesc DESC');
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, imageAltDesc_4 FROM (SELECT a0_.id AS id_0, a0_.image AS image_1, a0_.imageHeight AS imageHeight_2, a0_.imageWidth AS imageWidth_3, a0_.imageAltDesc AS imageAltDesc_4, a0_.user_id AS user_id_5 FROM Avatar a0_) dctrn_result_inner ORDER BY imageAltDesc_4 DESC) dctrn_result',
|
||||
'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, imageAltDesc_4 FROM (SELECT a0_.id AS id_0, a0_.image AS image_1, a0_.imageHeight AS imageHeight_2, a0_.imageWidth AS imageWidth_3, a0_.imageAltDesc AS imageAltDesc_4, a0_.user_id AS user_id_5 FROM Avatar a0_) dctrn_result_inner ORDER BY imageAltDesc_4 DESC) dctrn_result LIMIT 20 OFFSET 10',
|
||||
$query->getSQL()
|
||||
);
|
||||
}
|
||||
|
||||
public function testLimitSubqueryWithOrderByInnerJoined(): void
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT b FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost b JOIN b.author a ORDER BY a.name ASC'
|
||||
);
|
||||
|
||||
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
$query = $this->createQuery('SELECT b FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost b JOIN b.author a ORDER BY a.name ASC');
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, name_2 FROM (SELECT b0_.id AS id_0, a1_.id AS id_1, a1_.name AS name_2, b0_.author_id AS author_id_3, b0_.category_id AS category_id_4 FROM BlogPost b0_ INNER JOIN Author a1_ ON b0_.author_id = a1_.id) dctrn_result_inner ORDER BY name_2 ASC) dctrn_result',
|
||||
'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, name_2 FROM (SELECT b0_.id AS id_0, a1_.id AS id_1, a1_.name AS name_2, b0_.author_id AS author_id_3, b0_.category_id AS category_id_4 FROM BlogPost b0_ INNER JOIN Author a1_ ON b0_.author_id = a1_.id) dctrn_result_inner ORDER BY name_2 ASC) dctrn_result LIMIT 20 OFFSET 10',
|
||||
$query->getSQL()
|
||||
);
|
||||
}
|
||||
|
||||
public function testLimitSubqueryWithOrderByAndSubSelectInWhereClauseMySql(): void
|
||||
{
|
||||
$this->entityManager->getConnection()->setDatabasePlatform(new MySQLPlatform());
|
||||
$query = $this->entityManager->createQuery(
|
||||
$this->replaceDatabasePlatform(new MySQLPlatform());
|
||||
|
||||
$query = $this->createQuery(
|
||||
'SELECT b FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost b
|
||||
WHERE ((SELECT COUNT(simple.id) FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost simple) = 1)
|
||||
ORDER BY b.id DESC'
|
||||
);
|
||||
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0 FROM (SELECT b0_.id AS id_0, b0_.author_id AS author_id_1, b0_.category_id AS category_id_2 FROM BlogPost b0_ WHERE ((SELECT COUNT(b1_.id) AS sclr_3 FROM BlogPost b1_) = 1)) dctrn_result_inner ORDER BY id_0 DESC) dctrn_result',
|
||||
'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0 FROM (SELECT b0_.id AS id_0, b0_.author_id AS author_id_1, b0_.category_id AS category_id_2 FROM BlogPost b0_ WHERE ((SELECT COUNT(b1_.id) AS sclr_3 FROM BlogPost b1_) = 1)) dctrn_result_inner ORDER BY id_0 DESC) dctrn_result LIMIT 20 OFFSET 10',
|
||||
$query->getSQL()
|
||||
);
|
||||
}
|
||||
|
||||
public function testLimitSubqueryWithOrderByAndSubSelectInWhereClausePgSql(): void
|
||||
{
|
||||
$this->entityManager->getConnection()->setDatabasePlatform(new PostgreSQLPlatform());
|
||||
$query = $this->entityManager->createQuery(
|
||||
$this->replaceDatabasePlatform(new PostgreSQLPlatform());
|
||||
|
||||
$query = $this->createQuery(
|
||||
'SELECT b FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost b
|
||||
WHERE ((SELECT COUNT(simple.id) FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost simple) = 1)
|
||||
ORDER BY b.id DESC'
|
||||
);
|
||||
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT id_0, MIN(sclr_1) AS dctrn_minrownum FROM (SELECT b0_.id AS id_0, ROW_NUMBER() OVER(ORDER BY b0_.id DESC) AS sclr_1, b0_.author_id AS author_id_2, b0_.category_id AS category_id_3 FROM BlogPost b0_ WHERE ((SELECT COUNT(b1_.id) AS sclr_4 FROM BlogPost b1_) = 1)) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC',
|
||||
'SELECT DISTINCT id_0, MIN(sclr_1) AS dctrn_minrownum FROM (SELECT b0_.id AS id_0, ROW_NUMBER() OVER(ORDER BY b0_.id DESC) AS sclr_1, b0_.author_id AS author_id_2, b0_.category_id AS category_id_3 FROM BlogPost b0_ WHERE ((SELECT COUNT(b1_.id) AS sclr_4 FROM BlogPost b1_) = 1)) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC LIMIT 20 OFFSET 10',
|
||||
$query->getSQL()
|
||||
);
|
||||
}
|
||||
@@ -370,16 +300,15 @@ ORDER BY b.id DESC'
|
||||
*/
|
||||
public function testLimitSubqueryOrderByFieldFromMappedSuperclass(): void
|
||||
{
|
||||
$this->entityManager->getConnection()->setDatabasePlatform(new MySQLPlatform());
|
||||
$this->replaceDatabasePlatform(new MySQLPlatform());
|
||||
|
||||
// now use the third one in query
|
||||
$query = $this->entityManager->createQuery(
|
||||
$query = $this->createQuery(
|
||||
'SELECT b FROM Doctrine\Tests\ORM\Tools\Pagination\Banner b ORDER BY b.id DESC'
|
||||
);
|
||||
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0 FROM (SELECT b0_.id AS id_0, b0_.name AS name_1 FROM Banner b0_) dctrn_result_inner ORDER BY id_0 DESC) dctrn_result',
|
||||
'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0 FROM (SELECT b0_.id AS id_0, b0_.name AS name_1 FROM Banner b0_) dctrn_result_inner ORDER BY id_0 DESC) dctrn_result LIMIT 20 OFFSET 10',
|
||||
$query->getSQL()
|
||||
);
|
||||
}
|
||||
@@ -389,9 +318,9 @@ ORDER BY b.id DESC'
|
||||
*/
|
||||
public function testLimitSubqueryOrderBySubSelectOrderByExpression(): void
|
||||
{
|
||||
$this->entityManager->getConnection()->setDatabasePlatform(new MySQLPlatform());
|
||||
$this->replaceDatabasePlatform(new MySQLPlatform());
|
||||
|
||||
$query = $this->entityManager->createQuery(
|
||||
$query = $this->createQuery(
|
||||
'SELECT a,
|
||||
(
|
||||
SELECT MIN(bp.title)
|
||||
@@ -401,10 +330,9 @@ ORDER BY b.id DESC'
|
||||
FROM Doctrine\Tests\ORM\Tools\Pagination\Author a
|
||||
ORDER BY first_blog_post DESC'
|
||||
);
|
||||
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, sclr_2 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, (SELECT MIN(m1_.title) AS sclr_3 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) AS sclr_2 FROM Author a0_) dctrn_result_inner ORDER BY sclr_2 DESC) dctrn_result',
|
||||
'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, sclr_2 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, (SELECT MIN(m1_.title) AS sclr_3 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) AS sclr_2 FROM Author a0_) dctrn_result_inner ORDER BY sclr_2 DESC) dctrn_result LIMIT 20 OFFSET 10',
|
||||
$query->getSQL()
|
||||
);
|
||||
}
|
||||
@@ -414,9 +342,9 @@ ORDER BY b.id DESC'
|
||||
*/
|
||||
public function testLimitSubqueryOrderBySubSelectOrderByExpressionPg(): void
|
||||
{
|
||||
$this->entityManager->getConnection()->setDatabasePlatform(new PostgreSQLPlatform());
|
||||
$this->replaceDatabasePlatform(new PostgreSQLPlatform());
|
||||
|
||||
$query = $this->entityManager->createQuery(
|
||||
$query = $this->createQuery(
|
||||
'SELECT a,
|
||||
(
|
||||
SELECT MIN(bp.title)
|
||||
@@ -426,10 +354,9 @@ ORDER BY b.id DESC'
|
||||
FROM Doctrine\Tests\ORM\Tools\Pagination\Author a
|
||||
ORDER BY first_blog_post DESC'
|
||||
);
|
||||
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT id_0, MIN(sclr_4) AS dctrn_minrownum FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, (SELECT MIN(m1_.title) AS sclr_3 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) AS sclr_2, ROW_NUMBER() OVER(ORDER BY (SELECT MIN(m1_.title) AS sclr_5 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) DESC) AS sclr_4 FROM Author a0_) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC',
|
||||
'SELECT DISTINCT id_0, MIN(sclr_4) AS dctrn_minrownum FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, (SELECT MIN(m1_.title) AS sclr_3 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) AS sclr_2, ROW_NUMBER() OVER(ORDER BY (SELECT MIN(m1_.title) AS sclr_5 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) DESC) AS sclr_4 FROM Author a0_) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC LIMIT 20 OFFSET 10',
|
||||
$query->getSQL()
|
||||
);
|
||||
}
|
||||
@@ -439,9 +366,9 @@ ORDER BY b.id DESC'
|
||||
*/
|
||||
public function testLimitSubqueryOrderBySubSelectOrderByExpressionOracle(): void
|
||||
{
|
||||
$this->entityManager->getConnection()->setDatabasePlatform(new OraclePlatform());
|
||||
$this->replaceDatabasePlatform(new OraclePlatform());
|
||||
|
||||
$query = $this->entityManager->createQuery(
|
||||
$query = $this->createQuery(
|
||||
'SELECT a,
|
||||
(
|
||||
SELECT MIN(bp.title)
|
||||
@@ -451,11 +378,20 @@ ORDER BY b.id DESC'
|
||||
FROM Doctrine\Tests\ORM\Tools\Pagination\Author a
|
||||
ORDER BY first_blog_post DESC'
|
||||
);
|
||||
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
|
||||
self::assertSame(
|
||||
'SELECT DISTINCT ID_0, MIN(SCLR_4) AS dctrn_minrownum FROM (SELECT a0_.id AS ID_0, a0_.name AS NAME_1, (SELECT MIN(m1_.title) AS SCLR_3 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) AS SCLR_2, ROW_NUMBER() OVER(ORDER BY (SELECT MIN(m1_.title) AS SCLR_5 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) DESC) AS SCLR_4 FROM Author a0_) dctrn_result GROUP BY ID_0 ORDER BY dctrn_minrownum ASC',
|
||||
'SELECT * FROM (SELECT a.*, ROWNUM AS doctrine_rownum FROM (SELECT DISTINCT ID_0, MIN(SCLR_4) AS dctrn_minrownum FROM (SELECT a0_.id AS ID_0, a0_.name AS NAME_1, (SELECT MIN(m1_.title) AS SCLR_3 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) AS SCLR_2, ROW_NUMBER() OVER(ORDER BY (SELECT MIN(m1_.title) AS SCLR_5 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) DESC) AS SCLR_4 FROM Author a0_) dctrn_result GROUP BY ID_0 ORDER BY dctrn_minrownum ASC) a WHERE ROWNUM <= 30) WHERE doctrine_rownum >= 11',
|
||||
$query->getSQL()
|
||||
);
|
||||
}
|
||||
|
||||
private function createQuery(string $dql): Query
|
||||
{
|
||||
$query = $this->entityManager->createQuery($dql);
|
||||
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||
$query->setFirstResult(10);
|
||||
$query->setMaxResults(20);
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user