From a705f526fb5bbd6b6bb51c4800956cb727f510f3 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 15 Mar 2020 01:11:34 +0100 Subject: [PATCH] [GH-7633] disallow cache partial objects (#8050) * [GH-7633] Bugfix: Partial queries were stored in 2LC. There was a check in DefaultQueryCache that prevented partial queries, because they are not supported. However the checked hint Query::HINT_FORCE_PARTIAL_LOAD is optional, so cant be used to prevent caching partial DQL queries. Introduce a new hint that the SqlWalker sets on detecing a PARTIAL query and throw an exception in the DefaultQueryCache if thats found. * Housekeeping: CS * [GH-7633] HINT_FORCE_PARTIAL_LOAD still needs to be checked. * Housekeeping: Fix CS --- lib/Doctrine/ORM/Cache/DefaultQueryCache.php | 2 +- lib/Doctrine/ORM/Query/SqlWalker.php | 7 +++++++ .../SecondLevelCacheQueryCacheTest.php | 16 +++++++++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Cache/DefaultQueryCache.php b/lib/Doctrine/ORM/Cache/DefaultQueryCache.php index 96f0fbd0ed6..83cc6128f9b 100644 --- a/lib/Doctrine/ORM/Cache/DefaultQueryCache.php +++ b/lib/Doctrine/ORM/Cache/DefaultQueryCache.php @@ -260,7 +260,7 @@ public function put(QueryCacheKey $key, ResultSetMapping $rsm, $result, array $h throw new CacheException("Second-level cache query supports only select statements."); } - if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD]) && $hints[Query::HINT_FORCE_PARTIAL_LOAD]) { + if (($hints[Query\SqlWalker::HINT_PARTIAL] ?? false) === true || ($hints[Query::HINT_FORCE_PARTIAL_LOAD] ?? false) === true) { throw new CacheException("Second level cache does not support partial entities."); } diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index d06d070610f..e3eb975020b 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -46,6 +46,11 @@ class SqlWalker implements TreeWalker */ 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'; + /** * @var ResultSetMapping */ @@ -1366,6 +1371,8 @@ public function walkSelectExpression($selectExpression) default: // IdentificationVariable or PartialObjectExpression if ($expr instanceof AST\PartialObjectExpression) { + $this->query->setHint(self::HINT_PARTIAL, true); + $dqlAlias = $expr->identificationVariable; $partialFieldSet = $expr->partialFieldSet; } else { diff --git a/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheQueryCacheTest.php b/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheQueryCacheTest.php index 0a32886dd0f..56188ade6c0 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheQueryCacheTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheQueryCacheTest.php @@ -1095,11 +1095,25 @@ public function testCacheablePartialQueryException() $this->loadFixturesCountries(); $this->_em->createQuery("SELECT PARTIAL c.{id} FROM Doctrine\Tests\Models\Cache\Country c") - ->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true) ->setCacheable(true) ->getResult(); } + /** + * @expectedException \Doctrine\ORM\Cache\CacheException + * @expectedExceptionMessage Second level cache does not support partial entities. + */ + public function testCacheableForcePartialLoadHintQueryException() + { + $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(); + } + /** * @expectedException \Doctrine\ORM\Cache\CacheException * @expectedExceptionMessage Second-level cache query supports only select statements.