Completed
Pull Request — 2.10.x (#3997)
by
unknown
62:42
created

ResultCacheTest::setUp()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 21
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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

120
        self::/** @scrutinizer ignore-call */ 
121
              assertStandardAndIteratorFetchAreEqual(FetchMode::MIXED);
Loading history...
121
        self::assertStandardAndIteratorFetchAreEqual(FetchMode::ASSOCIATIVE);
122
        self::assertStandardAndIteratorFetchAreEqual(FetchMode::NUMERIC);
123
    }
124
125
    private function assertStandardAndIteratorFetchAreEqual(int $fetchMode) : void
126
    {
127
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
128
        $data = $this->hydrateStmt($stmt, $fetchMode);
129
130
        $stmt         = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
131
        $dataIterator = $this->hydrateStmtIterator($stmt, $fetchMode);
132
133
        self::assertEquals($data, $dataIterator);
134
    }
135
136
    public function testDontCloseNoCache() : void
137
    {
138
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
139
140
        $data = [];
141
142
        while ($row = $stmt->fetch(FetchMode::ASSOCIATIVE)) {
143
            $data[] = $row;
144
        }
145
146
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
147
148
        $data = [];
149
150
        while ($row = $stmt->fetch(FetchMode::NUMERIC)) {
151
            $data[] = $row;
152
        }
153
154
        self::assertCount(2, $this->sqlLogger->queries);
155
    }
156
157
    public function testDontFinishNoCache() : void
158
    {
159
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
160
161
        $stmt->fetch(FetchMode::ASSOCIATIVE);
162
        $stmt->closeCursor();
163
164
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
165
166
        $this->hydrateStmt($stmt, FetchMode::NUMERIC);
167
168
        self::assertCount(1, $this->sqlLogger->queries);
169
    }
170
171
    public function testFetchAllAndFinishSavesCache() : void
172
    {
173
        $layerCache = new ArrayCache();
174
        $stmt       = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'testcachekey', $layerCache));
175
        $stmt->fetchAll();
176
        $stmt->closeCursor();
177
178
        self::assertCount(1, $layerCache->fetch('testcachekey'));
0 ignored issues
show
Bug introduced by
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

178
        self::assertCount(1, /** @scrutinizer ignore-type */ $layerCache->fetch('testcachekey'));
Loading history...
179
    }
180
181
    public function testFetchAllColumn() : void
182
    {
183
        $query = $this->connection->getDatabasePlatform()
184
            ->getDummySelectSQL('1');
185
186
        $qcp = new QueryCacheProfile(0, 0, new ArrayCache());
187
188
        $stmt = $this->connection->executeCacheQuery($query, [], [], $qcp);
189
        $stmt->fetchAll(FetchMode::COLUMN);
190
        $stmt->closeCursor();
191
192
        $stmt = $this->connection->executeCacheQuery($query, [], [], $qcp);
193
194
        self::assertEquals([1], $stmt->fetchAll(FetchMode::COLUMN));
195
    }
196
197
    public function testGetIterator() : void
198
    {
199
        $query = $this->connection->getDatabasePlatform()
200
            ->getDummySelectSQL('1');
201
202
        $qcp = new QueryCacheProfile(0, 0, new ArrayCache());
203
204
        $stmt = $this->connection->executeCacheQuery($query, [], [], $qcp);
205
        $stmt->getIterator();
206
        $stmt->closeCursor();
207
208
        $stmt = $this->connection->executeCacheQuery($query, [], [], $qcp);
209
210
        $iterator = $stmt->getIterator();
211
212
        self::assertCount(1, $iterator);
213
    }
214
215
    /**
216
     * @param array<int, array<int, int|string>> $expectedResult
217
     */
218
    private function assertCacheNonCacheSelectSameFetchModeAreEqual(array $expectedResult, int $fetchMode) : void
219
    {
220
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
221
222
        self::assertEquals(2, $stmt->columnCount());
223
        $data = $this->hydrateStmt($stmt, $fetchMode);
224
        self::assertEquals($expectedResult, $data);
225
226
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
227
228
        self::assertEquals(2, $stmt->columnCount());
229
        $data = $this->hydrateStmt($stmt, $fetchMode);
230
        self::assertEquals($expectedResult, $data);
231
        self::assertCount(1, $this->sqlLogger->queries, 'just one dbal hit');
232
    }
233
234
    public function testEmptyResultCache() : void
235
    {
236
        $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey'));
237
        $data = $this->hydrateStmt($stmt);
0 ignored issues
show
Unused Code introduced by
The assignment to $data is dead and can be removed.
Loading history...
238
239
        $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey'));
240
        $data = $this->hydrateStmt($stmt);
241
242
        self::assertCount(1, $this->sqlLogger->queries, 'just one dbal hit');
243
    }
244
245
    public function testChangeCacheImpl() : void
246
    {
247
        $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey'));
248
        $data = $this->hydrateStmt($stmt);
0 ignored issues
show
Unused Code introduced by
The assignment to $data is dead and can be removed.
Loading history...
249
250
        $secondCache = new ArrayCache();
251
        $stmt        = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey', $secondCache));
252
        $data        = $this->hydrateStmt($stmt);
253
254
        self::assertCount(2, $this->sqlLogger->queries, 'two hits');
255
        self::assertCount(1, $secondCache->fetch('emptycachekey'));
0 ignored issues
show
Bug introduced by
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

255
        self::assertCount(1, /** @scrutinizer ignore-type */ $secondCache->fetch('emptycachekey'));
Loading history...
256
    }
257
258
    /**
259
     * @return array<int, mixed>
260
     */
261
    private function hydrateStmt(ResultStatement $stmt, int $fetchMode = FetchMode::ASSOCIATIVE) : array
262
    {
263
        $data = [];
264
        while ($row = $stmt->fetch($fetchMode)) {
265
            $data[] = is_array($row) ? array_change_key_case($row, CASE_LOWER) : $row;
266
        }
267
268
        $stmt->closeCursor();
269
270
        return $data;
271
    }
272
273
    /**
274
     * @return array<int, mixed>
275
     */
276
    private function hydrateStmtIterator(ResultStatement $stmt, int $fetchMode = FetchMode::ASSOCIATIVE) : array
277
    {
278
        $data = [];
279
        $stmt->setFetchMode($fetchMode);
280
        foreach ($stmt as $row) {
281
            $data[] = is_array($row) ? array_change_key_case($row, CASE_LOWER) : $row;
282
        }
283
284
        $stmt->closeCursor();
285
286
        return $data;
287
    }
288
}
289