Issues (201)

tests/Functional/ResultCacheTest.php (3 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\DBAL\Tests\Functional;
6
7
use Doctrine\Common\Cache\ArrayCache;
8
use Doctrine\DBAL\Cache\QueryCacheProfile;
9
use Doctrine\DBAL\Driver\ResultStatement;
10
use Doctrine\DBAL\FetchMode;
11
use Doctrine\DBAL\Logging\DebugStack;
12
use Doctrine\DBAL\Schema\Table;
13
use Doctrine\DBAL\Tests\FunctionalTestCase;
14
use function array_change_key_case;
15
use function array_merge;
16
use function array_shift;
17
use function array_values;
18
use function is_array;
19
use const CASE_LOWER;
20
21
/**
22
 * @group DDC-217
23
 */
24
class ResultCacheTest extends FunctionalTestCase
25
{
26
    /** @var array<int, array<string, int|string>> */
27
    private $expectedResult = [['test_int' => 100, 'test_string' => 'foo'], ['test_int' => 200, 'test_string' => 'bar'], ['test_int' => 300, 'test_string' => 'baz']];
28
29
    /** @var DebugStack */
30
    private $sqlLogger;
31
32
    protected function setUp() : void
33
    {
34
        parent::setUp();
35
36
        $table = new Table('caching');
37
        $table->addColumn('test_int', 'integer');
38
        $table->addColumn('test_string', 'string', [
39
            'length' => 8,
40
            'notnull' => false,
41
        ]);
42
        $table->setPrimaryKey(['test_int']);
43
44
        $sm = $this->connection->getSchemaManager();
45
        $sm->dropAndCreateTable($table);
46
47
        foreach ($this->expectedResult as $row) {
48
            $this->connection->insert('caching', $row);
49
        }
50
51
        $config = $this->connection->getConfiguration();
52
        $config->setSQLLogger($this->sqlLogger = new DebugStack());
53
54
        $cache = new ArrayCache();
55
        $config->setResultCacheImpl($cache);
56
    }
57
58
    public function testCacheFetchAssoc() : void
59
    {
60
        $this->assertCacheNonCacheSelectSameFetchModeAreEqual(
61
            $this->expectedResult,
62
            FetchMode::ASSOCIATIVE
63
        );
64
    }
65
66
    public function testFetchNum() : void
67
    {
68
        $expectedResult = [];
69
        foreach ($this->expectedResult as $v) {
70
            $expectedResult[] = array_values($v);
71
        }
72
73
        $this->assertCacheNonCacheSelectSameFetchModeAreEqual($expectedResult, FetchMode::NUMERIC);
74
    }
75
76
    public function testFetchBoth() : void
77
    {
78
        $expectedResult = [];
79
        foreach ($this->expectedResult as $v) {
80
            $expectedResult[] = array_merge($v, array_values($v));
81
        }
82
83
        $this->assertCacheNonCacheSelectSameFetchModeAreEqual($expectedResult, FetchMode::MIXED);
84
    }
85
86
    public function testFetchColumn() : void
87
    {
88
        $expectedResult = [];
89
        foreach ($this->expectedResult as $v) {
90
            $expectedResult[] = array_shift($v);
91
        }
92
93
        $this->assertCacheNonCacheSelectSameFetchModeAreEqual($expectedResult, FetchMode::COLUMN);
94
    }
95
96
    public function testMixingFetch() : void
97
    {
98
        $numExpectedResult = [];
99
        foreach ($this->expectedResult as $v) {
100
            $numExpectedResult[] = array_values($v);
101
        }
102
103
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
104
105
        $data = $this->hydrateStmt($stmt, FetchMode::ASSOCIATIVE);
106
107
        self::assertEquals($this->expectedResult, $data);
108
109
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
110
111
        $data = $this->hydrateStmt($stmt, FetchMode::NUMERIC);
112
113
        self::assertEquals($numExpectedResult, $data);
114
    }
115
116
    public function testIteratorFetch() : void
117
    {
118
        self::assertStandardAndIteratorFetchAreEqual(FetchMode::MIXED);
0 ignored issues
show
Bug Best Practice introduced by
The method Doctrine\DBAL\Tests\Func...IteratorFetchAreEqual() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

118
        self::/** @scrutinizer ignore-call */ 
119
              assertStandardAndIteratorFetchAreEqual(FetchMode::MIXED);
Loading history...
119
        self::assertStandardAndIteratorFetchAreEqual(FetchMode::ASSOCIATIVE);
120
        self::assertStandardAndIteratorFetchAreEqual(FetchMode::NUMERIC);
121
    }
122
123
    private function assertStandardAndIteratorFetchAreEqual(int $fetchMode) : void
124
    {
125
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
126
        $data = $this->hydrateStmt($stmt, $fetchMode);
127
128
        $stmt     = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
129
        $iterator = $this->hydrateStmtIterator($stmt, $fetchMode);
130
131
        self::assertEquals($data, $iterator);
132
    }
133
134
    public function testDontCloseNoCache() : void
135
    {
136
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
137
138
        $data = [];
139
140
        while ($row = $stmt->fetch(FetchMode::ASSOCIATIVE)) {
141
            $data[] = $row;
142
        }
143
144
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
145
146
        $data = [];
147
148
        while ($row = $stmt->fetch(FetchMode::NUMERIC)) {
149
            $data[] = $row;
150
        }
151
152
        self::assertCount(2, $this->sqlLogger->queries);
153
    }
154
155
    public function testDontFinishNoCache() : void
156
    {
157
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
158
159
        $stmt->fetch(FetchMode::ASSOCIATIVE);
160
        $stmt->closeCursor();
161
162
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
163
164
        $this->hydrateStmt($stmt, FetchMode::NUMERIC);
165
166
        self::assertCount(2, $this->sqlLogger->queries);
167
    }
168
169
    public function testFetchAllAndFinishSavesCache() : void
170
    {
171
        $layerCache = new ArrayCache();
172
        $stmt       = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'testcachekey', $layerCache));
173
        $stmt->fetchAll();
174
        $stmt->closeCursor();
175
176
        self::assertCount(1, $layerCache->fetch('testcachekey'));
0 ignored issues
show
It seems like $layerCache->fetch('testcachekey') can also be of type false; however, parameter $haystack of PHPUnit\Framework\Assert::assertCount() does only seem to accept Countable|iterable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

176
        self::assertCount(1, /** @scrutinizer ignore-type */ $layerCache->fetch('testcachekey'));
Loading history...
177
    }
178
179
    public function testFetchAllColumn() : void
180
    {
181
        $query = $this->connection->getDatabasePlatform()
182
            ->getDummySelectSQL('1');
183
184
        $qcp = new QueryCacheProfile(0, null, new ArrayCache());
185
186
        $stmt = $this->connection->executeCacheQuery($query, [], [], $qcp);
187
        $stmt->fetchAll(FetchMode::COLUMN);
188
        $stmt->closeCursor();
189
190
        $stmt = $this->connection->executeCacheQuery($query, [], [], $qcp);
191
192
        self::assertEquals([1], $stmt->fetchAll(FetchMode::COLUMN));
193
    }
194
195
    /**
196
     * @param array<int, mixed> $expectedResult
197
     */
198
    private function assertCacheNonCacheSelectSameFetchModeAreEqual(array $expectedResult, int $fetchMode) : void
199
    {
200
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
201
202
        self::assertEquals(2, $stmt->columnCount());
203
        $data = $this->hydrateStmt($stmt, $fetchMode);
204
        self::assertEquals($expectedResult, $data);
205
206
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
207
208
        self::assertEquals(2, $stmt->columnCount());
209
        $data = $this->hydrateStmt($stmt, $fetchMode);
210
        self::assertEquals($expectedResult, $data);
211
        self::assertCount(1, $this->sqlLogger->queries, 'just one dbal hit');
212
    }
213
214
    public function testEmptyResultCache() : void
215
    {
216
        $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey'));
217
        $this->hydrateStmt($stmt);
218
219
        $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey'));
220
        $this->hydrateStmt($stmt);
221
222
        self::assertCount(1, $this->sqlLogger->queries, 'just one dbal hit');
223
    }
224
225
    public function testChangeCacheImpl() : void
226
    {
227
        $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey'));
228
        $this->hydrateStmt($stmt);
229
230
        $secondCache = new ArrayCache();
231
        $stmt        = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey', $secondCache));
232
        $this->hydrateStmt($stmt);
233
234
        self::assertCount(2, $this->sqlLogger->queries, 'two hits');
235
        self::assertCount(1, $secondCache->fetch('emptycachekey'));
0 ignored issues
show
It seems like $secondCache->fetch('emptycachekey') can also be of type false; however, parameter $haystack of PHPUnit\Framework\Assert::assertCount() does only seem to accept Countable|iterable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

235
        self::assertCount(1, /** @scrutinizer ignore-type */ $secondCache->fetch('emptycachekey'));
Loading history...
236
    }
237
238
    /**
239
     * @return array<int, mixed>
240
     */
241
    private function hydrateStmt(ResultStatement $stmt, int $fetchMode = FetchMode::ASSOCIATIVE) : array
242
    {
243
        $data = [];
244
        while ($row = $stmt->fetch($fetchMode)) {
245
            $data[] = is_array($row) ? array_change_key_case($row, CASE_LOWER) : $row;
246
        }
247
248
        $stmt->closeCursor();
249
250
        return $data;
251
    }
252
253
    /**
254
     * @return array<int, mixed>
255
     */
256
    private function hydrateStmtIterator(ResultStatement $stmt, int $fetchMode = FetchMode::ASSOCIATIVE) : array
257
    {
258
        $data = [];
259
        $stmt->setFetchMode($fetchMode);
260
        foreach ($stmt as $row) {
261
            $data[] = is_array($row) ? array_change_key_case($row, CASE_LOWER) : $row;
262
        }
263
264
        $stmt->closeCursor();
265
266
        return $data;
267
    }
268
}
269