Repository: doctrine/dbal (php)

Issue:
ResultCacheStatement::fetchAllAssociative does not store results in cache
### Bug Report

|    Q        |   A
|------------ | ------
| BC Break    | yes/no
| Version     | 2.12.0

#### Summary

Using `fetchAllAssociative` to fetch query results on a Symfony project using `doctrine/dbal` (no ORM) with a Redis cache seems to not use the cache. But `fetchAll` does.  

#### Current behaviour

The query is executed at each run.

#### How to reproduce

```php
$sql = <<<SQL
    SELECT
        *
    FROM my_table
    WHERE id = :id
SQL;

$connection = ;// instance of Doctrine\DBAL\Connection
$stmt = $connection->executeCacheQuery(
    $sql,
    ['id' => 1],
    ['id' => PDO::PARAM_INT],
    new QueryCacheProfile(
        360,
        'my_key'
    )
);
$results = $stmt->fetchAllAssociative(); // This will always execute a query
```

This problem also shows up when using `fetchAllNumeric`, `fetchAssociative`, and `fetchNumeric`, but not when using `fetchAll`


#### Expected behaviour

The query should be executed at the same occurence with `fetchAllAssociative|fetchAllNumeric|fetchAssociative|fetchNumeric` as with `fetchAll`

I've tried to dig a bit in the code, I saw 2 things:
- `fetchAllAssociative` and `fetchAllNumeric`:
  I am a bit less confident there as it seems to have "nested statement calls" (I do not know how it is called, look for example at lines [256](https://github.com/doctrine/dbal/blob/cad295a6b777b759fe3dc0be0cc2a3d7e87aa8e1/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php#L256) and [258](https://github.com/doctrine/dbal/blob/cad295a6b777b759fe3dc0be0cc2a3d7e87aa8e1/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php#L258]), but they both calls a method [store()](https://github.com/doctrine/dbal/blob/cad295a6b777b759fe3dc0be0cc2a3d7e87aa8e1/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php#L328) that will also not save data in cache.

-  `fetchAssociative` and `fetchNumeric`:
   They both call method `doFetch`, but this methods will not store the result if it differs from `false`
https://github.com/doctrine/dbal/blob/cad295a6b777b759fe3dc0be0cc2a3d7e87aa8e1/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php#L314-L320

**Update**: `fetchAssociative` and `fetchNumeric` are probably not an issue, according to the doc


Pull request description:
ResultCacheStatement::fetchAllAssociative does not store results in cache
|      Q       |   A
|------------- | -----------
| Type         | bug
| BC Break     | no

Fixes #4406.

Patch:
diff --git a/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php b/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php
index 07a6c2206..61bb50220 100644
--- a/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php
+++ b/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php
@@ -242,7 +242,9 @@ class ResultCacheStatement implements IteratorAggregate, ResultStatement, Result
             $data = $this->statement->fetchAll(FetchMode::ASSOCIATIVE);
         }
 
-        $this->store($data);
+        $this->data = $data;
+
+        $this->saveToCache();
 
         return array_map('array_values', $data);
     }
@@ -258,7 +260,9 @@ class ResultCacheStatement implements IteratorAggregate, ResultStatement, Result
             $data = $this->statement->fetchAll(FetchMode::ASSOCIATIVE);
         }
 
-        $this->store($data);
+        $this->data = $data;
+
+        $this->saveToCache();
 
         return $data;
     }
@@ -322,14 +326,6 @@ class ResultCacheStatement implements IteratorAggregate, ResultStatement, Result
         return false;
     }
 
-    /**
-     * @param array<int,array<string,mixed>> $data
-     */
-    private function store(array $data): void
-    {
-        $this->data = $data;
-    }
-
     private function saveToCache(): void
     {
         if ($this->data === null) {


Test patch:
diff --git a/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php b/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php
index 05d3ff70c..5336a1053 100644
--- a/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php
+++ b/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php
@@ -4,6 +4,7 @@ namespace Doctrine\Tests\DBAL\Functional;
 
 use Doctrine\Common\Cache\ArrayCache;
 use Doctrine\DBAL\Cache\QueryCacheProfile;
+use Doctrine\DBAL\Cache\ResultCacheStatement;
 use Doctrine\DBAL\Driver\ResultStatement;
 use Doctrine\DBAL\FetchMode;
 use Doctrine\DBAL\Logging\DebugStack;
@@ -212,7 +213,10 @@ class ResultCacheTest extends DbalFunctionalTestCase
         self::assertCount(2, $this->sqlLogger->queries);
     }
 
-    public function testFetchAllSavesCache(): void
+    /**
+     * @dataProvider fetchAllProvider
+     */
+    public function testFetchingAllRowsSavesCache(callable $fetchAll): void
     {
         $layerCache = new ArrayCache();
 
@@ -222,11 +226,42 @@ class ResultCacheTest extends DbalFunctionalTestCase
             [],
             new QueryCacheProfile(0, 'testcachekey', $layerCache)
         );
-        $stmt->fetchAll();
+
+        $fetchAll($stmt);
 
         self::assertCount(1, $layerCache->fetch('testcachekey'));
     }
 
+    /**
+     * @return iterable<string,list<mixed>>
+     */
+    public static function fetchAllProvider(): iterable
+    {
+        yield 'fetchAll' => [
+            static function (ResultCacheStatement $statement): void {
+                $statement->fetchAll();
+            },
+        ];
+
+        yield 'fetchAllAssociative' => [
+            static function (ResultCacheStatement $statement): void {
+                $statement->fetchAllAssociative();
+            },
+        ];
+
+        yield 'fetchAllNumeric' => [
+            static function (ResultCacheStatement $statement): void {
+                $statement->fetchAllNumeric();
+            },
+        ];
+
+        yield 'fetchFirstColumn' => [
+            static function (ResultCacheStatement $result): void {
+                $result->fetchFirstColumn();
+            },
+        ];
+    }
+
     public function testFetchAllColumn(): void
     {
         $query = $this->connection->getDatabasePlatform()