From 9e1946f261f1dfb665863060eb76a638c8eb279e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Mon, 9 Oct 2023 17:12:45 +0200 Subject: [PATCH] Remove partial syntax in DQL It is no longer possible to use the "PARTIAL" keyword in a DQL query, or to artificially build an AST with a partial object expression. It is still possible to use the result set mapping API to build partial objects. --- UPGRADE.md | 7 + docs/en/index.rst | 1 - .../reference/dql-doctrine-query-language.rst | 40 +----- docs/en/reference/partial-objects.rst | 98 -------------- docs/en/sidebar.rst | 1 - lib/Doctrine/ORM/Cache/DefaultQueryCache.php | 5 - .../Cache/Exception/FeatureNotImplemented.php | 5 - lib/Doctrine/ORM/Query.php | 8 -- .../ORM/Query/AST/PartialObjectExpression.php | 15 --- lib/Doctrine/ORM/Query/Parser.php | 120 +----------------- lib/Doctrine/ORM/Query/SqlWalker.php | 67 +++------- lib/Doctrine/ORM/UnitOfWork.php | 13 -- ...PartialObjectHydrationPerformanceBench.php | 84 ------------ ...PartialObjectHydrationPerformanceBench.php | 72 ----------- .../OneToOneUnidirectionalAssociationTest.php | 14 -- .../ORM/Functional/PostLoadEventTest.php | 21 --- .../SecondLevelCacheQueryCacheTest.php | 25 ---- .../ORM/Functional/Ticket/DDC163Test.php | 2 +- .../ORM/Functional/Ticket/DDC2519Test.php | 76 ----------- .../ORM/Functional/Ticket/GH8443Test.php | 32 ----- .../Tests/ORM/Functional/ValueObjectsTest.php | 61 --------- .../ORM/Query/LanguageRecognitionTest.php | 10 -- .../ORM/Query/SelectSqlGenerationTest.php | 102 +-------------- .../LimitSubqueryOutputWalkerTest.php | 16 --- 24 files changed, 34 insertions(+), 861 deletions(-) delete mode 100644 docs/en/reference/partial-objects.rst delete mode 100644 lib/Doctrine/ORM/Query/AST/PartialObjectExpression.php delete mode 100644 tests/Doctrine/Performance/Hydration/MixedQueryFetchJoinPartialObjectHydrationPerformanceBench.php delete mode 100644 tests/Doctrine/Performance/Hydration/SimpleQueryPartialObjectHydrationPerformanceBench.php delete mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2519Test.php diff --git a/UPGRADE.md b/UPGRADE.md index b8680f7ed89..af2bfa75288 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,5 +1,12 @@ # Upgrade to 3.0 +## BC BREAK: Partial objects are removed + +- The `PARTIAL` keyword no longer exists. +- `Doctrine\ORM\Query\AST\PartialObjectExpression`is removed. +- `Doctrine\ORM\Query\SqlWalker::HINT_PARTIAL` and + `Doctrine\ORM\Query::HINT_FORCE_PARTIAL_LOAD` are removed. + ## BC BREAK: `Doctrine\ORM\Persister\Entity\EntityPersister::executeInserts()` return type changed to `void` Implementors should adapt to the new signature, and should call diff --git a/docs/en/index.rst b/docs/en/index.rst index 4977670291a..91d2df8ca89 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -73,7 +73,6 @@ Advanced Topics * :doc:`TypedFieldMapper ` * :doc:`Improving Performance ` * :doc:`Caching ` -* :doc:`Partial Objects ` * :doc:`Change Tracking Policies ` * :doc:`Best Practices ` * :doc:`Metadata Drivers ` diff --git a/docs/en/reference/dql-doctrine-query-language.rst b/docs/en/reference/dql-doctrine-query-language.rst index d84c4b00efe..d3ad8bca845 100644 --- a/docs/en/reference/dql-doctrine-query-language.rst +++ b/docs/en/reference/dql-doctrine-query-language.rst @@ -518,33 +518,6 @@ when the DQL is switched to an arbitrary join. - HAVING is applied to the results of a query after aggregation (GROUP BY) - -Partial Object Syntax -^^^^^^^^^^^^^^^^^^^^^ - -By default when you run a DQL query in Doctrine and select only a -subset of the fields for a given entity, you do not receive objects -back. Instead, you receive only arrays as a flat rectangular result -set, similar to how you would if you were just using SQL directly -and joining some data. - -If you want to select partial objects you can use the ``partial`` -DQL keyword: - -.. code-block:: php - - createQuery('SELECT partial u.{id, username} FROM CmsUser u'); - $users = $query->getResult(); // array of partially loaded CmsUser objects - -You use the partial syntax when joining as well: - -.. code-block:: php - - createQuery('SELECT partial u.{id, username}, partial a.{id, name} FROM CmsUser u JOIN u.articles a'); - $users = $query->getResult(); // array of partially loaded CmsUser objects - "NEW" Operator Syntax ^^^^^^^^^^^^^^^^^^^^^ @@ -1417,15 +1390,6 @@ exist mostly internal query hints that are not be consumed in userland. However the following few hints are to be used in userland: - -- ``Query::HINT_FORCE_PARTIAL_LOAD`` - Allows to hydrate objects - although not all their columns are fetched. This query hint can be - used to handle memory consumption problems with large result-sets - that contain char or binary data. Doctrine has no way of implicitly - reloading this data. Partially loaded objects have to be passed to - ``EntityManager::refresh()`` if they are to be reloaded fully from - the database. This query hint is deprecated and will be removed - in the future (\ `Details `_) - ``Query::HINT_REFRESH`` - This query is used internally by ``EntityManager::refresh()`` and can be used in userland as well. If you specify this hint and a query returns the data for an entity @@ -1678,10 +1642,8 @@ Select Expressions .. code-block:: php - SelectExpression ::= (IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression | NewObjectExpression) [["AS"] ["HIDDEN"] AliasResultVariable] + SelectExpression ::= (IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | "(" Subselect ")" | CaseExpression | NewObjectExpression) [["AS"] ["HIDDEN"] AliasResultVariable] SimpleSelectExpression ::= (StateFieldPathExpression | IdentificationVariable | FunctionDeclaration | AggregateExpression | "(" Subselect ")" | ScalarExpression) [["AS"] AliasResultVariable] - PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet - PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}" NewObjectExpression ::= "NEW" AbstractSchemaName "(" NewObjectArg {"," NewObjectArg}* ")" NewObjectArg ::= ScalarExpression | "(" Subselect ")" diff --git a/docs/en/reference/partial-objects.rst b/docs/en/reference/partial-objects.rst deleted file mode 100644 index 51f173adf6c..00000000000 --- a/docs/en/reference/partial-objects.rst +++ /dev/null @@ -1,98 +0,0 @@ -Partial Objects -=============== - - -.. note:: - - Creating Partial Objects through DQL is deprecated and - will be removed in the future, use data transfer object - support in DQL instead. (\ `Details - `_) - -A partial object is an object whose state is not fully initialized -after being reconstituted from the database and that is -disconnected from the rest of its data. The following section will -describe why partial objects are problematic and what the approach -of Doctrine2 to this problem is. - -.. note:: - - The partial object problem in general does not apply to - methods or queries where you do not retrieve the query result as - objects. Examples are: ``Query#getArrayResult()``, - ``Query#getScalarResult()``, ``Query#getSingleScalarResult()``, - etc. - -.. warning:: - - Use of partial objects is tricky. Fields that are not retrieved - from the database will not be updated by the UnitOfWork even if they - get changed in your objects. You can only promote a partial object - to a fully-loaded object by calling ``EntityManager#refresh()`` - or a DQL query with the refresh flag. - - -What is the problem? --------------------- - -In short, partial objects are problematic because they are usually -objects with broken invariants. As such, code that uses these -partial objects tends to be very fragile and either needs to "know" -which fields or methods can be safely accessed or add checks around -every field access or method invocation. The same holds true for -the internals, i.e. the method implementations, of such objects. -You usually simply assume the state you need in the method is -available, after all you properly constructed this object before -you pushed it into the database, right? These blind assumptions can -quickly lead to null reference errors when working with such -partial objects. - -It gets worse with the scenario of an optional association (0..1 to -1). When the associated field is NULL, you don't know whether this -object does not have an associated object or whether it was simply -not loaded when the owning object was loaded from the database. - -These are reasons why many ORMs do not allow partial objects at all -and instead you always have to load an object with all its fields -(associations being proxied). One secure way to allow partial -objects is if the programming language/platform allows the ORM tool -to hook deeply into the object and instrument it in such a way that -individual fields (not only associations) can be loaded lazily on -first access. This is possible in Java, for example, through -bytecode instrumentation. In PHP though this is not possible, so -there is no way to have "secure" partial objects in an ORM with -transparent persistence. - -Doctrine, by default, does not allow partial objects. That means, -any query that only selects partial object data and wants to -retrieve the result as objects (i.e. ``Query#getResult()``) will -raise an exception telling you that partial objects are dangerous. -If you want to force a query to return you partial objects, -possibly as a performance tweak, you can use the ``partial`` -keyword as follows: - -.. code-block:: php - - createQuery("select partial u.{id,name} from MyApp\Domain\User u"); - -You can also get a partial reference instead of a proxy reference by -calling: - -.. code-block:: php - - getPartialReference('MyApp\Domain\User', 1); - -Partial references are objects with only the identifiers set as they -are passed to the second argument of the ``getPartialReference()`` method. -All other fields are null. - -When should I force partial objects? ------------------------------------- - -Mainly for optimization purposes, but be careful of premature -optimization as partial objects lead to potentially more fragile -code. - - diff --git a/docs/en/sidebar.rst b/docs/en/sidebar.rst index 460e4236948..4dfc08c4540 100644 --- a/docs/en/sidebar.rst +++ b/docs/en/sidebar.rst @@ -40,7 +40,6 @@ reference/query-builder reference/native-sql reference/change-tracking-policies - reference/partial-objects reference/attributes-reference reference/xml-mapping reference/php-mapping diff --git a/lib/Doctrine/ORM/Cache/DefaultQueryCache.php b/lib/Doctrine/ORM/Cache/DefaultQueryCache.php index 08e703cd4b0..f3bb8ac95c8 100644 --- a/lib/Doctrine/ORM/Cache/DefaultQueryCache.php +++ b/lib/Doctrine/ORM/Cache/DefaultQueryCache.php @@ -16,7 +16,6 @@ use Doctrine\ORM\PersistentCollection; use Doctrine\ORM\Query; use Doctrine\ORM\Query\ResultSetMapping; -use Doctrine\ORM\Query\SqlWalker; use Doctrine\ORM\UnitOfWork; use function array_map; @@ -211,10 +210,6 @@ public function put(QueryCacheKey $key, ResultSetMapping $rsm, mixed $result, ar throw FeatureNotImplemented::nonSelectStatements(); } - if (($hints[SqlWalker::HINT_PARTIAL] ?? false) === true || ($hints[Query::HINT_FORCE_PARTIAL_LOAD] ?? false) === true) { - throw FeatureNotImplemented::partialEntities(); - } - if (! ($key->cacheMode & Cache::MODE_PUT)) { return false; } diff --git a/lib/Doctrine/ORM/Cache/Exception/FeatureNotImplemented.php b/lib/Doctrine/ORM/Cache/Exception/FeatureNotImplemented.php index 7bae90b775d..8767d574190 100644 --- a/lib/Doctrine/ORM/Cache/Exception/FeatureNotImplemented.php +++ b/lib/Doctrine/ORM/Cache/Exception/FeatureNotImplemented.php @@ -20,9 +20,4 @@ public static function nonSelectStatements(): self { return new self('Second-level cache query supports only select statements.'); } - - public static function partialEntities(): self - { - return new self('Second level cache does not support partial entities.'); - } } diff --git a/lib/Doctrine/ORM/Query.php b/lib/Doctrine/ORM/Query.php index 0c8397e58ff..ef4c36e3530 100644 --- a/lib/Doctrine/ORM/Query.php +++ b/lib/Doctrine/ORM/Query.php @@ -68,14 +68,6 @@ final class Query extends AbstractQuery */ public const HINT_REFRESH_ENTITY = 'doctrine.refresh.entity'; - /** - * The forcePartialLoad query hint forces a particular query to return - * partial objects. - * - * @todo Rename: HINT_OPTIMIZE - */ - public const HINT_FORCE_PARTIAL_LOAD = 'doctrine.forcePartialLoad'; - /** * The includeMetaColumns query hint causes meta columns like foreign keys and * discriminator columns to be selected and returned as part of the query result. diff --git a/lib/Doctrine/ORM/Query/AST/PartialObjectExpression.php b/lib/Doctrine/ORM/Query/AST/PartialObjectExpression.php deleted file mode 100644 index 875f4453fe1..00000000000 --- a/lib/Doctrine/ORM/Query/AST/PartialObjectExpression.php +++ /dev/null @@ -1,15 +0,0 @@ - */ private array $deferredIdentificationVariables = []; - /** @psalm-var list */ - private array $deferredPartialObjectExpressions = []; - /** @psalm-var list */ private array $deferredPathExpressions = []; @@ -229,10 +224,6 @@ public function getAST(): AST\SelectStatement|AST\UpdateStatement|AST\DeleteStat // This also allows post-processing of the AST for modification purposes. $this->processDeferredIdentificationVariables(); - if ($this->deferredPartialObjectExpressions) { - $this->processDeferredPartialObjectExpressions(); - } - if ($this->deferredPathExpressions) { $this->processDeferredPathExpressions(); } @@ -608,44 +599,6 @@ private function processDeferredNewObjectExpressions(AST\SelectStatement $AST): } } - /** - * Validates that the given PartialObjectExpression is semantically correct. - * It must exist in query components list. - */ - private function processDeferredPartialObjectExpressions(): void - { - foreach ($this->deferredPartialObjectExpressions as $deferredItem) { - $expr = $deferredItem['expression']; - $class = $this->getMetadataForDqlAlias($expr->identificationVariable); - - foreach ($expr->partialFieldSet as $field) { - if (isset($class->fieldMappings[$field])) { - continue; - } - - if ( - isset($class->associationMappings[$field]) && - $class->associationMappings[$field]->isToOneOwningSide() - ) { - continue; - } - - $this->semanticalError(sprintf( - "There is no mapped field named '%s' on class %s.", - $field, - $class->name, - ), $deferredItem['token']); - } - - if (array_intersect($class->identifier, $expr->partialFieldSet) !== $class->identifier) { - $this->semanticalError( - 'The partial field selection of class ' . $class->name . ' must contain the identifier.', - $deferredItem['token'], - ); - } - } - } - /** * Validates that the given ResultVariable is semantically correct. * It must exist in query components list. @@ -1670,69 +1623,6 @@ public function JoinAssociationDeclaration(): AST\JoinAssociationDeclaration return new AST\JoinAssociationDeclaration($joinAssociationPathExpression, $aliasIdentificationVariable, $indexBy); } - /** - * PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet - * PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}" - */ - public function PartialObjectExpression(): AST\PartialObjectExpression - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8471', - 'PARTIAL syntax in DQL is deprecated.', - ); - - $this->match(TokenType::T_PARTIAL); - - $partialFieldSet = []; - - $identificationVariable = $this->IdentificationVariable(); - - $this->match(TokenType::T_DOT); - $this->match(TokenType::T_OPEN_CURLY_BRACE); - $this->match(TokenType::T_IDENTIFIER); - - assert($this->lexer->token !== null); - $field = $this->lexer->token->value; - - // First field in partial expression might be embeddable property - while ($this->lexer->isNextToken(TokenType::T_DOT)) { - $this->match(TokenType::T_DOT); - $this->match(TokenType::T_IDENTIFIER); - $field .= '.' . $this->lexer->token->value; - } - - $partialFieldSet[] = $field; - - while ($this->lexer->isNextToken(TokenType::T_COMMA)) { - $this->match(TokenType::T_COMMA); - $this->match(TokenType::T_IDENTIFIER); - - $field = $this->lexer->token->value; - - while ($this->lexer->isNextToken(TokenType::T_DOT)) { - $this->match(TokenType::T_DOT); - $this->match(TokenType::T_IDENTIFIER); - $field .= '.' . $this->lexer->token->value; - } - - $partialFieldSet[] = $field; - } - - $this->match(TokenType::T_CLOSE_CURLY_BRACE); - - $partialObjectExpression = new AST\PartialObjectExpression($identificationVariable, $partialFieldSet); - - // Defer PartialObjectExpression validation - $this->deferredPartialObjectExpressions[] = [ - 'expression' => $partialObjectExpression, - 'nestingLevel' => $this->nestingLevel, - 'token' => $this->lexer->token, - ]; - - return $partialObjectExpression; - } - /** * NewObjectExpression ::= "NEW" AbstractSchemaName "(" NewObjectArg {"," NewObjectArg}* ")" */ @@ -2034,7 +1924,7 @@ public function SimpleWhenClause(): AST\SimpleWhenClause /** * SelectExpression ::= ( * IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | - * PartialObjectExpression | "(" Subselect ")" | CaseExpression | NewObjectExpression + * "(" Subselect ")" | CaseExpression | NewObjectExpression * ) [["AS"] ["HIDDEN"] AliasResultVariable] */ public function SelectExpression(): AST\SelectExpression @@ -2075,12 +1965,6 @@ public function SelectExpression(): AST\SelectExpression break; - // PartialObjectExpression (PARTIAL u.{id, name}) - case $lookaheadType === TokenType::T_PARTIAL: - $expression = $this->PartialObjectExpression(); - $identVariable = $expression->identificationVariable; - break; - // Subselect case $lookaheadType === TokenType::T_OPEN_PARENTHESIS && $peek->type === TokenType::T_SELECT: $this->match(TokenType::T_OPEN_PARENTHESIS); @@ -2106,7 +1990,7 @@ public function SelectExpression(): AST\SelectExpression default: $this->syntaxError( - 'IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression', + 'IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | "(" Subselect ")" | CaseExpression', $this->lexer->lookahead, ); } diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 3f2cd06cb1d..66a4f45525f 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -27,7 +27,6 @@ use function assert; use function count; use function implode; -use function in_array; use function is_array; use function is_float; use function is_numeric; @@ -49,11 +48,6 @@ class SqlWalker { public const HINT_DISTINCT = 'doctrine.distinct'; - /** - * Used to mark a query as containing a PARTIAL expression, which needs to be known by SLC. - */ - public const HINT_PARTIAL = 'doctrine.partial'; - private readonly ResultSetMapping $rsm; /** @@ -308,11 +302,6 @@ private function generateClassTableInheritanceJoins( $sql .= implode(' AND ', array_filter($sqlParts)); } - // Ignore subclassing inclusion if partial objects is disallowed - if ($this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) { - return $sql; - } - // LEFT JOIN child class tables foreach ($class->subClasses as $subClassName) { $subClass = $this->em->getClassMetadata($subClassName); @@ -635,8 +624,7 @@ public function walkSelectClause(AST\SelectClause $selectClause): string $this->query->setHint(self::HINT_DISTINCT, true); } - $addMetaColumns = ! $this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) && - $this->query->getHydrationMode() === Query::HYDRATE_OBJECT + $addMetaColumns = $this->query->getHydrationMode() === Query::HYDRATE_OBJECT || $this->query->getHint(Query::HINT_INCLUDE_META_COLUMNS); foreach ($this->selectedClasses as $selectedClass) { @@ -1313,17 +1301,7 @@ public function walkSelectExpression(AST\SelectExpression $selectExpression): st break; default: - // IdentificationVariable or PartialObjectExpression - if ($expr instanceof AST\PartialObjectExpression) { - $this->query->setHint(self::HINT_PARTIAL, true); - - $dqlAlias = $expr->identificationVariable; - $partialFieldSet = $expr->partialFieldSet; - } else { - $dqlAlias = $expr; - $partialFieldSet = []; - } - + $dqlAlias = $expr; $class = $this->getMetadataForDqlAlias($dqlAlias); $resultAlias = $selectExpression->fieldIdentificationVariable ?: null; @@ -1339,10 +1317,6 @@ public function walkSelectExpression(AST\SelectExpression $selectExpression): st // Select all fields from the queried class foreach ($class->fieldMappings as $fieldName => $mapping) { - if ($partialFieldSet && ! in_array($fieldName, $partialFieldSet, true)) { - continue; - } - $tableName = isset($mapping->inherited) ? $this->em->getClassMetadata($mapping->inherited)->getTableName() : $class->getTableName(); @@ -1369,32 +1343,29 @@ public function walkSelectExpression(AST\SelectExpression $selectExpression): st // Add any additional fields of subclasses (excluding inherited fields) // 1) on Single Table Inheritance: always, since its marginal overhead - // 2) on Class Table Inheritance only if partial objects are disallowed, - // since it requires outer joining subtables. - if ($class->isInheritanceTypeSingleTable() || ! $this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) { - foreach ($class->subClasses as $subClassName) { - $subClass = $this->em->getClassMetadata($subClassName); - $sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias); - - foreach ($subClass->fieldMappings as $fieldName => $mapping) { - if (isset($mapping->inherited) || ($partialFieldSet && ! in_array($fieldName, $partialFieldSet, true))) { - continue; - } + // 2) on Class Table Inheritance + foreach ($class->subClasses as $subClassName) { + $subClass = $this->em->getClassMetadata($subClassName); + $sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias); + + foreach ($subClass->fieldMappings as $fieldName => $mapping) { + if (isset($mapping->inherited)) { + continue; + } - $columnAlias = $this->getSQLColumnAlias($mapping->columnName); - $quotedColumnName = $this->quoteStrategy->getColumnName($fieldName, $subClass, $this->platform); + $columnAlias = $this->getSQLColumnAlias($mapping->columnName); + $quotedColumnName = $this->quoteStrategy->getColumnName($fieldName, $subClass, $this->platform); - $col = $sqlTableAlias . '.' . $quotedColumnName; + $col = $sqlTableAlias . '.' . $quotedColumnName; - $type = Type::getType($mapping->type); - $col = $type->convertToPHPValueSQL($col, $this->platform); + $type = Type::getType($mapping->type); + $col = $type->convertToPHPValueSQL($col, $this->platform); - $sqlParts[] = $col . ' AS ' . $columnAlias; + $sqlParts[] = $col . ' AS ' . $columnAlias; - $this->scalarResultAliasMap[$resultAlias][] = $columnAlias; + $this->scalarResultAliasMap[$resultAlias][] = $columnAlias; - $this->rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $subClassName); - } + $this->rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $subClassName); } } diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 0881c072019..ed38cb8faf0 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -12,7 +12,6 @@ use Doctrine\DBAL; use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection; use Doctrine\DBAL\LockMode; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\Cache\Persister\CachedPersister; use Doctrine\ORM\Event\ListenersInvoker; use Doctrine\ORM\Event\OnClearEventArgs; @@ -2347,18 +2346,6 @@ public function createEntity(string $className, array $data, array &$hints = []) unset($this->eagerLoadingEntities[$class->rootEntityName]); } - // Properly initialize any unfetched associations, if partial objects are not allowed. - if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8471', - 'Partial Objects are deprecated (here entity %s)', - $className, - ); - - return $entity; - } - foreach ($class->associationMappings as $field => $assoc) { // Check if the association is not among the fetch-joined associations already. if (isset($hints['fetchAlias'], $hints['fetched'][$hints['fetchAlias']][$field])) { diff --git a/tests/Doctrine/Performance/Hydration/MixedQueryFetchJoinPartialObjectHydrationPerformanceBench.php b/tests/Doctrine/Performance/Hydration/MixedQueryFetchJoinPartialObjectHydrationPerformanceBench.php deleted file mode 100644 index 6a1638afcf5..00000000000 --- a/tests/Doctrine/Performance/Hydration/MixedQueryFetchJoinPartialObjectHydrationPerformanceBench.php +++ /dev/null @@ -1,84 +0,0 @@ - '1', - 'u__status' => 'developer', - 'u__username' => 'romanb', - 'u__name' => 'Roman', - 'sclr0' => 'ROMANB', - 'p__phonenumber' => '42', - ], - [ - 'u__id' => '1', - 'u__status' => 'developer', - 'u__username' => 'romanb', - 'u__name' => 'Roman', - 'sclr0' => 'ROMANB', - 'p__phonenumber' => '43', - ], - [ - 'u__id' => '2', - 'u__status' => 'developer', - 'u__username' => 'romanb', - 'u__name' => 'Roman', - 'sclr0' => 'JWAGE', - 'p__phonenumber' => '91', - ], - ]; - - for ($i = 4; $i < 2000; ++$i) { - $resultSet[] = [ - 'u__id' => $i, - 'u__status' => 'developer', - 'u__username' => 'jwage', - 'u__name' => 'Jonathan', - 'sclr0' => 'JWAGE' . $i, - 'p__phonenumber' => '91', - ]; - } - - $this->result = ArrayResultFactory::createFromArray($resultSet); - $this->hydrator = new ObjectHydrator(EntityManagerFactory::getEntityManager([])); - $this->rsm = new ResultSetMapping(); - - $this->rsm->addEntityResult(CmsUser::class, 'u'); - $this->rsm->addJoinedEntityResult(CmsPhonenumber::class, 'p', 'u', 'phonenumbers'); - $this->rsm->addFieldResult('u', 'u__id', 'id'); - $this->rsm->addFieldResult('u', 'u__status', 'status'); - $this->rsm->addFieldResult('u', 'u__username', 'username'); - $this->rsm->addFieldResult('u', 'u__name', 'name'); - $this->rsm->addScalarResult('sclr0', 'nameUpper'); - $this->rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber'); - } - - public function benchHydration(): void - { - $this->hydrator->hydrateAll($this->result, $this->rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]); - } -} diff --git a/tests/Doctrine/Performance/Hydration/SimpleQueryPartialObjectHydrationPerformanceBench.php b/tests/Doctrine/Performance/Hydration/SimpleQueryPartialObjectHydrationPerformanceBench.php deleted file mode 100644 index b894068eb2c..00000000000 --- a/tests/Doctrine/Performance/Hydration/SimpleQueryPartialObjectHydrationPerformanceBench.php +++ /dev/null @@ -1,72 +0,0 @@ - '1', - 'u__status' => 'developer', - 'u__username' => 'romanb', - 'u__name' => 'Roman', - ], - [ - 'u__id' => '1', - 'u__status' => 'developer', - 'u__username' => 'romanb', - 'u__name' => 'Roman', - ], - [ - 'u__id' => '2', - 'u__status' => 'developer', - 'u__username' => 'romanb', - 'u__name' => 'Roman', - ], - ]; - - for ($i = 4; $i < 10000; ++$i) { - $resultSet[] = [ - 'u__id' => $i, - 'u__status' => 'developer', - 'u__username' => 'jwage', - 'u__name' => 'Jonathan', - ]; - } - - $this->result = ArrayResultFactory::createFromArray($resultSet); - $this->hydrator = new ObjectHydrator(EntityManagerFactory::getEntityManager([])); - $this->rsm = new ResultSetMapping(); - - $this->rsm->addEntityResult(CmsUser::class, 'u'); - $this->rsm->addFieldResult('u', 'u__id', 'id'); - $this->rsm->addFieldResult('u', 'u__status', 'status'); - $this->rsm->addFieldResult('u', 'u__username', 'username'); - $this->rsm->addFieldResult('u', 'u__name', 'name'); - } - - public function benchHydration(): void - { - $this->hydrator->hydrateAll($this->result, $this->rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]); - } -} diff --git a/tests/Doctrine/Tests/ORM/Functional/OneToOneUnidirectionalAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/OneToOneUnidirectionalAssociationTest.php index b2baaaa254e..cdb34bfcdbf 100644 --- a/tests/Doctrine/Tests/ORM/Functional/OneToOneUnidirectionalAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/OneToOneUnidirectionalAssociationTest.php @@ -5,7 +5,6 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\Query; use Doctrine\Tests\Models\ECommerce\ECommerceProduct; use Doctrine\Tests\Models\ECommerce\ECommerceShipping; use Doctrine\Tests\OrmFunctionalTestCase; @@ -79,19 +78,6 @@ public function testLazyLoadsObjects(): void self::assertEquals(1, $product->getShipping()->getDays()); } - public function testDoesNotLazyLoadObjectsIfConfigurationDoesNotAllowIt(): void - { - $this->createFixture(); - - $query = $this->_em->createQuery('select p from Doctrine\Tests\Models\ECommerce\ECommerceProduct p'); - $query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true); - - $result = $query->getResult(); - $product = $result[0]; - - self::assertNull($product->getShipping()); - } - protected function createFixture(): void { $product = new ECommerceProduct(); diff --git a/tests/Doctrine/Tests/ORM/Functional/PostLoadEventTest.php b/tests/Doctrine/Tests/ORM/Functional/PostLoadEventTest.php index 960cb359d44..dc51472cb52 100644 --- a/tests/Doctrine/Tests/ORM/Functional/PostLoadEventTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/PostLoadEventTest.php @@ -136,27 +136,6 @@ public function testLoadedProxyEntityShouldTriggerEvent(): void $userProxy->getName(); } - public function testLoadedProxyPartialShouldTriggerEvent(): void - { - $eventManager = $this->_em->getEventManager(); - - // Should not be invoked during getReference call - $mockListener = $this->createMock(PostLoadListener::class); - - // CmsUser (partially loaded), CmsAddress (inverse ToOne), 2 CmsPhonenumber - $mockListener - ->expects(self::exactly(4)) - ->method('postLoad') - ->will(self::returnValue(true)); - - $eventManager->addEventListener([Events::postLoad], $mockListener); - - $query = $this->_em->createQuery('SELECT PARTIAL u.{id, name}, p FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.phonenumbers p WHERE u.id = :id'); - - $query->setParameter('id', $this->userId); - $query->getResult(); - } - public function testLoadedProxyAssociationToOneShouldTriggerEvent(): void { $user = $this->_em->find(CmsUser::class, $this->userId); diff --git a/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheQueryCacheTest.php b/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheQueryCacheTest.php index e2dec649342..a3350aa5e6c 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheQueryCacheTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheQueryCacheTest.php @@ -1086,31 +1086,6 @@ public function testHintClearEntityRegionDeleteStatement(): void self::assertFalse($this->cache->containsEntity(Country::class, $this->countries[1]->getId())); } - public function testCacheablePartialQueryException(): void - { - $this->expectException(CacheException::class); - $this->expectExceptionMessage('Second level cache does not support partial entities.'); - $this->evictRegions(); - $this->loadFixturesCountries(); - - $this->_em->createQuery('SELECT PARTIAL c.{id} FROM Doctrine\Tests\Models\Cache\Country c') - ->setCacheable(true) - ->getResult(); - } - - public function testCacheableForcePartialLoadHintQueryException(): void - { - $this->expectException(CacheException::class); - $this->expectExceptionMessage('Second level cache does not support partial entities.'); - $this->evictRegions(); - $this->loadFixturesCountries(); - - $this->_em->createQuery('SELECT c FROM Doctrine\Tests\Models\Cache\Country c') - ->setCacheable(true) - ->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true) - ->getResult(); - } - public function testNonCacheableQueryDeleteStatementException(): void { $this->expectException(CacheException::class); diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC163Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC163Test.php index 6c0c7c94fe3..83aebb4dcae 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC163Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC163Test.php @@ -46,7 +46,7 @@ public function testQueryWithOrConditionUsingTwoRelationOnSameEntity(): void $this->_em->flush(); $this->_em->clear(); - $dql = 'SELECT PARTIAL person.{id,name}, PARTIAL spouse.{id,name}, PARTIAL friend.{id,name} + $dql = 'SELECT person.name as person_name, spouse.name as spouse_name,friend.name as friend_name FROM Doctrine\Tests\Models\Company\CompanyPerson person LEFT JOIN person.spouse spouse LEFT JOIN person.friends friend diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2519Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2519Test.php deleted file mode 100644 index b8dfba13b9c..00000000000 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2519Test.php +++ /dev/null @@ -1,76 +0,0 @@ -useModelSet('legacy'); - - parent::setUp(); - - $this->loadFixture(); - } - - #[Group('DDC-2519')] - public function testIssue(): void - { - $dql = 'SELECT PARTIAL l.{_source, _target} FROM Doctrine\Tests\Models\Legacy\LegacyUserReference l'; - $result = $this->_em->createQuery($dql)->getResult(); - - self::assertCount(2, $result); - self::assertInstanceOf(LegacyUserReference::class, $result[0]); - self::assertInstanceOf(LegacyUserReference::class, $result[1]); - - self::assertInstanceOf(LegacyUser::class, $result[0]->source()); - self::assertInstanceOf(LegacyUser::class, $result[0]->target()); - self::assertInstanceOf(LegacyUser::class, $result[1]->source()); - self::assertInstanceOf(LegacyUser::class, $result[1]->target()); - - self::assertTrue($this->isUninitializedObject($result[0]->target())); - self::assertTrue($this->isUninitializedObject($result[0]->source())); - self::assertTrue($this->isUninitializedObject($result[1]->target())); - self::assertTrue($this->isUninitializedObject($result[1]->source())); - - self::assertNotNull($result[0]->source()->getId()); - self::assertNotNull($result[0]->target()->getId()); - self::assertNotNull($result[1]->source()->getId()); - self::assertNotNull($result[1]->target()->getId()); - } - - public function loadFixture(): void - { - $user1 = new LegacyUser(); - $user1->username = 'FabioBatSilva'; - $user1->name = 'Fabio B. Silva'; - - $user2 = new LegacyUser(); - $user2->username = 'doctrinebot'; - $user2->name = 'Doctrine Bot'; - - $user3 = new LegacyUser(); - $user3->username = 'test'; - $user3->name = 'Tester'; - - $this->_em->persist($user1); - $this->_em->persist($user2); - $this->_em->persist($user3); - - $this->_em->flush(); - - $this->_em->persist(new LegacyUserReference($user1, $user2, 'foo')); - $this->_em->persist(new LegacyUserReference($user1, $user3, 'bar')); - - $this->_em->flush(); - $this->_em->clear(); - } -} diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/GH8443Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH8443Test.php index 44695f1856e..0b5352a6ea8 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/GH8443Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH8443Test.php @@ -14,9 +14,6 @@ use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -use Doctrine\ORM\Query; -use Doctrine\Tests\Models\Company\CompanyManager; -use Doctrine\Tests\Models\Company\CompanyPerson; use Doctrine\Tests\OrmFunctionalTestCase; use PHPUnit\Framework\Attributes\Group; @@ -33,35 +30,6 @@ protected function setUp(): void $this->createSchemaForModels(GH8443Foo::class); } - #[Group('GH-8443')] - public function testJoinRootEntityWithForcePartialLoad(): void - { - $person = new CompanyPerson(); - $person->setName('John'); - - $manager = new CompanyManager(); - $manager->setName('Adam'); - $manager->setSalary(1000); - $manager->setDepartment('IT'); - $manager->setTitle('manager'); - - $manager->setSpouse($person); - - $this->_em->persist($person); - $this->_em->persist($manager); - $this->_em->flush(); - $this->_em->clear(); - - $manager = $this->_em->createQuery( - "SELECT m from Doctrine\Tests\Models\Company\CompanyManager m - JOIN m.spouse s - WITH s.name = 'John'", - )->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true)->getSingleResult(); - $this->_em->refresh($manager); - - $this->assertEquals('John', $manager->getSpouse()->getName()); - } - #[Group('GH-8443')] public function testJoinRootEntityWithOnlyOneEntityInHierarchy(): void { diff --git a/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php b/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php index 3186e71d357..08ce4b17d89 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php @@ -209,58 +209,6 @@ public function testDqlOnEmbeddedObjectsField(): void self::assertNull($this->_em->find(DDC93Person::class, $person->id)); } - public function testPartialDqlOnEmbeddedObjectsField(): void - { - $person = new DDC93Person('Karl', new DDC93Address('Foo', '12345', 'Gosport', new DDC93Country('England'))); - $this->_em->persist($person); - $this->_em->flush(); - $this->_em->clear(); - - // Prove that the entity was persisted correctly. - $dql = 'SELECT p FROM ' . __NAMESPACE__ . '\\DDC93Person p WHERE p.name = :name'; - - $person = $this->_em->createQuery($dql) - ->setParameter('name', 'Karl') - ->getSingleResult(); - - self::assertEquals('Gosport', $person->address->city); - self::assertEquals('Foo', $person->address->street); - self::assertEquals('12345', $person->address->zip); - self::assertEquals('England', $person->address->country->name); - - // Clear the EM and prove that the embeddable can be the subject of a partial query. - $this->_em->clear(); - - $dql = 'SELECT PARTIAL p.{id,address.city} FROM ' . __NAMESPACE__ . '\\DDC93Person p WHERE p.name = :name'; - - $person = $this->_em->createQuery($dql) - ->setParameter('name', 'Karl') - ->getSingleResult(); - - // Selected field must be equal, all other fields must be null. - self::assertEquals('Gosport', $person->address->city); - self::assertNull($person->address->street); - self::assertNull($person->address->zip); - self::assertNull($person->address->country); - self::assertNull($person->name); - - // Clear the EM and prove that the embeddable can be the subject of a partial query regardless of attributes positions. - $this->_em->clear(); - - $dql = 'SELECT PARTIAL p.{address.city, id} FROM ' . __NAMESPACE__ . '\\DDC93Person p WHERE p.name = :name'; - - $person = $this->_em->createQuery($dql) - ->setParameter('name', 'Karl') - ->getSingleResult(); - - // Selected field must be equal, all other fields must be null. - self::assertEquals('Gosport', $person->address->city); - self::assertNull($person->address->street); - self::assertNull($person->address->zip); - self::assertNull($person->address->country); - self::assertNull($person->name); - } - public function testDqlWithNonExistentEmbeddableField(): void { $this->expectException(QueryException::class); @@ -270,15 +218,6 @@ public function testDqlWithNonExistentEmbeddableField(): void ->execute(); } - public function testPartialDqlWithNonExistentEmbeddableField(): void - { - $this->expectException(QueryException::class); - $this->expectExceptionMessage("no mapped field named 'address.asdfasdf'"); - - $this->_em->createQuery('SELECT PARTIAL p.{id,address.asdfasdf} FROM ' . __NAMESPACE__ . '\\DDC93Person p') - ->execute(); - } - public function testEmbeddableWithInheritance(): void { $car = new DDC93Car(new DDC93Address('Foo', '12345', 'Asdf')); diff --git a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php index df27bf907c5..72fc2a8d451 100644 --- a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php +++ b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php @@ -522,16 +522,6 @@ public function testUnknownAbstractSchemaName(): void $this->assertInvalidDQL('SELECT u FROM UnknownClassName u'); } - public function testCorrectPartialObjectLoad(): void - { - $this->assertValidDQL('SELECT PARTIAL u.{id,name} FROM Doctrine\Tests\Models\CMS\CmsUser u'); - } - - public function testIncorrectPartialObjectLoadBecauseOfMissingIdentifier(): void - { - $this->assertInvalidDQL('SELECT PARTIAL u.{name} FROM Doctrine\Tests\Models\CMS\CmsUser u'); - } - public function testScalarExpressionInSelect(): void { $this->assertValidDQL('SELECT u, 42 + u.id AS someNumber FROM Doctrine\Tests\Models\CMS\CmsUser u'); diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index af1b475c585..94f3c8c9d8f 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -1330,20 +1330,6 @@ public function testIdentityFunctionWithCompositePrimaryKey(): void ); } - #[Group('DDC-2519')] - public function testPartialWithAssociationIdentifier(): void - { - $this->assertSqlGeneration( - 'SELECT PARTIAL l.{_source, _target} FROM Doctrine\Tests\Models\Legacy\LegacyUserReference l', - 'SELECT l0_.iUserIdSource AS iUserIdSource_0, l0_.iUserIdTarget AS iUserIdTarget_1 FROM legacy_users_reference l0_', - ); - - $this->assertSqlGeneration( - 'SELECT PARTIAL l.{_description, _source, _target} FROM Doctrine\Tests\Models\Legacy\LegacyUserReference l', - 'SELECT l0_.description AS description_0, l0_.iUserIdSource AS iUserIdSource_1, l0_.iUserIdTarget AS iUserIdTarget_2 FROM legacy_users_reference l0_', - ); - } - #[Group('DDC-1339')] public function testIdentityFunctionInSelectClause(): void { @@ -1378,82 +1364,38 @@ public function testIdentityFunctionDoesNotAcceptStateField(): void } #[Group('DDC-1389')] - public function testInheritanceTypeJoinInRootClassWithDisabledForcePartialLoad(): void + public function testInheritanceTypeJoinInRootClass(): void { $this->assertSqlGeneration( 'SELECT p FROM Doctrine\Tests\Models\Company\CompanyPerson p', 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c1_.car_id AS car_id_8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id', - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false], ); } #[Group('DDC-1389')] - public function testInheritanceTypeJoinInRootClassWithEnabledForcePartialLoad(): void - { - $this->assertSqlGeneration( - 'SELECT p FROM Doctrine\Tests\Models\Company\CompanyPerson p', - 'SELECT c0_.id AS id_0, c0_.name AS name_1, c0_.discr AS discr_2 FROM company_persons c0_', - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true], - ); - } - - #[Group('DDC-1389')] - public function testInheritanceTypeJoinInChildClassWithDisabledForcePartialLoad(): void + public function testInheritanceTypeJoinInChildClass(): void { $this->assertSqlGeneration( 'SELECT e FROM Doctrine\Tests\Models\Company\CompanyEmployee e', 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id', - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false], ); } #[Group('DDC-1389')] - public function testInheritanceTypeJoinInChildClassWithEnabledForcePartialLoad(): void - { - $this->assertSqlGeneration( - 'SELECT e FROM Doctrine\Tests\Models\Company\CompanyEmployee e', - 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c0_.discr AS discr_5 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id', - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true], - ); - } - - #[Group('DDC-1389')] - public function testInheritanceTypeJoinInLeafClassWithDisabledForcePartialLoad(): void + public function testInheritanceTypeJoinInLeafClass(): void { $this->assertSqlGeneration( 'SELECT m FROM Doctrine\Tests\Models\Company\CompanyManager m', 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id', - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false], ); } #[Group('DDC-1389')] - public function testInheritanceTypeJoinInLeafClassWithEnabledForcePartialLoad(): void - { - $this->assertSqlGeneration( - 'SELECT m FROM Doctrine\Tests\Models\Company\CompanyManager m', - 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id', - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true], - ); - } - - #[Group('DDC-1389')] - public function testInheritanceTypeSingleTableInRootClassWithDisabledForcePartialLoad(): void + public function testInheritanceTypeSingleTableInRootClass(): void { $this->assertSqlGeneration( 'SELECT c FROM Doctrine\Tests\Models\Company\CompanyContract c', "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_contracts c0_ WHERE c0_.discr IN ('fix', 'flexible', 'flexultra')", - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false], - ); - } - - #[Group('DDC-1389')] - public function testInheritanceTypeSingleTableInRootClassWithEnabledForcePartialLoad(): void - { - $this->assertSqlGeneration( - 'SELECT c FROM Doctrine\Tests\Models\Company\CompanyContract c', - "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6 FROM company_contracts c0_ WHERE c0_.discr IN ('fix', 'flexible', 'flexultra')", - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true], ); } @@ -1463,17 +1405,6 @@ public function testInheritanceTypeSingleTableInChildClassWithDisabledForceParti $this->assertSqlGeneration( 'SELECT fc FROM Doctrine\Tests\Models\Company\CompanyFlexContract fc', "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.hoursWorked AS hoursWorked_2, c0_.pricePerHour AS pricePerHour_3, c0_.maxPrice AS maxPrice_4, c0_.discr AS discr_5, c0_.salesPerson_id AS salesPerson_id_6 FROM company_contracts c0_ WHERE c0_.discr IN ('flexible', 'flexultra')", - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false], - ); - } - - #[Group('DDC-1389')] - public function testInheritanceTypeSingleTableInChildClassWithEnabledForcePartialLoad(): void - { - $this->assertSqlGeneration( - 'SELECT fc FROM Doctrine\Tests\Models\Company\CompanyFlexContract fc', - "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.hoursWorked AS hoursWorked_2, c0_.pricePerHour AS pricePerHour_3, c0_.maxPrice AS maxPrice_4, c0_.discr AS discr_5 FROM company_contracts c0_ WHERE c0_.discr IN ('flexible', 'flexultra')", - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true], ); } @@ -1483,17 +1414,6 @@ public function testInheritanceTypeSingleTableInLeafClassWithDisabledForcePartia $this->assertSqlGeneration( 'SELECT fuc FROM Doctrine\Tests\Models\Company\CompanyFlexUltraContract fuc', "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.hoursWorked AS hoursWorked_2, c0_.pricePerHour AS pricePerHour_3, c0_.maxPrice AS maxPrice_4, c0_.discr AS discr_5, c0_.salesPerson_id AS salesPerson_id_6 FROM company_contracts c0_ WHERE c0_.discr IN ('flexultra')", - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false], - ); - } - - #[Group('DDC-1389')] - public function testInheritanceTypeSingleTableInLeafClassWithEnabledForcePartialLoad(): void - { - $this->assertSqlGeneration( - 'SELECT fuc FROM Doctrine\Tests\Models\Company\CompanyFlexUltraContract fuc', - "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.hoursWorked AS hoursWorked_2, c0_.pricePerHour AS pricePerHour_3, c0_.maxPrice AS maxPrice_4, c0_.discr AS discr_5 FROM company_contracts c0_ WHERE c0_.discr IN ('flexultra')", - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true], ); } @@ -1770,20 +1690,6 @@ public function testCustomTypeValueSqlForAllFields(): void ); } - public function testCustomTypeValueSqlForPartialObject(): void - { - if (DBALType::hasType('negative_to_positive')) { - DBALType::overrideType('negative_to_positive', NegativeToPositiveType::class); - } else { - DBALType::addType('negative_to_positive', NegativeToPositiveType::class); - } - - $this->assertSqlGeneration( - 'SELECT partial p.{id, customInteger} FROM Doctrine\Tests\Models\CustomType\CustomTypeParent p', - 'SELECT c0_.id AS id_0, -(c0_.customInteger) AS customInteger_1, c0_.child_id AS child_id_2 FROM customtype_parents c0_', - ); - } - #[Group('DDC-1529')] public function testMultipleFromAndInheritanceCondition(): void { diff --git a/tests/Doctrine/Tests/ORM/Tools/Pagination/LimitSubqueryOutputWalkerTest.php b/tests/Doctrine/Tests/ORM/Tools/Pagination/LimitSubqueryOutputWalkerTest.php index 3193c03367f..3ff7830104f 100644 --- a/tests/Doctrine/Tests/ORM/Tools/Pagination/LimitSubqueryOutputWalkerTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/Pagination/LimitSubqueryOutputWalkerTest.php @@ -230,22 +230,6 @@ public function testCountQueryWithComplexScalarOrderByItemJoinedWithoutPartial() ); } - public function testCountQueryWithComplexScalarOrderByItemJoinedWithPartial(): void - { - $entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform()); - - $query = $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', - ); - - $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class); - - 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', - $query->getSQL(), - ); - } - public function testCountQueryWithComplexScalarOrderByItemOracle(): void { $entityManager = $this->createTestEntityManagerWithPlatform(new OraclePlatform());