Failed Conditions
Pull Request — 2.10.x (#3997)
by
unknown
63:16
created

ResultCacheTest::testDontFinishNoCache()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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

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

179
        self::assertCount(1, /** @scrutinizer ignore-type */ $layerCache->fetch('testcachekey'));
Loading history...
180
    }
181
182
    public function testFetchAllColumn() : void
183
    {
184
        $query = $this->connection->getDatabasePlatform()
185
            ->getDummySelectSQL('1');
186
187
        $qcp = new QueryCacheProfile(0, 0, new ArrayCache());
188
189
        $stmt = $this->connection->executeCacheQuery($query, [], [], $qcp);
190
        $stmt->fetchAll(FetchMode::COLUMN);
191
        $stmt->closeCursor();
192
193
        $stmt = $this->connection->executeCacheQuery($query, [], [], $qcp);
194
195
        self::assertEquals([1], $stmt->fetchAll(FetchMode::COLUMN));
196
    }
197
198
    public function testGetIterator() : void
199
    {
200
        $query = $this->connection->getDatabasePlatform()
201
            ->getDummySelectSQL('1');
202
203
        $qcp = new QueryCacheProfile(0, 0, new ArrayCache());
204
205
        $stmt = $this->connection->executeCacheQuery($query, [], [], $qcp);
206
        $stmt->getIterator();
0 ignored issues
show
Bug introduced by
The method getIterator() does not exist on Doctrine\DBAL\Driver\ResultStatement. It seems like you code against a sub-type of said class. However, the method does not exist in Doctrine\DBAL\Driver\Statement or Doctrine\DBAL\Driver\PDOStatement or Doctrine\DBAL\Driver\PDOSqlsrv\Statement. Are you sure you never get one of those? ( Ignorable by Annotation )

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

206
        $stmt->/** @scrutinizer ignore-call */ 
207
               getIterator();
Loading history...
207
        $stmt->closeCursor();
208
209
        $stmt = $this->connection->executeCacheQuery($query, [], [], $qcp);
210
211
        $iterator = $stmt->getIterator();
212
213
        self::assertTrue($iterator instanceof ArrayIterator);
214
215
        self::assertEquals(1, $iterator->count());
216
    }
217
218
    /**
219
     * @param array<int, array<int, int|string>> $expectedResult
220
     */
221
    private function assertCacheNonCacheSelectSameFetchModeAreEqual(array $expectedResult, int $fetchMode) : void
222
    {
223
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
224
225
        self::assertEquals(2, $stmt->columnCount());
226
        $data = $this->hydrateStmt($stmt, $fetchMode);
227
        self::assertEquals($expectedResult, $data);
228
229
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
230
231
        self::assertEquals(2, $stmt->columnCount());
232
        $data = $this->hydrateStmt($stmt, $fetchMode);
233
        self::assertEquals($expectedResult, $data);
234
        self::assertCount(1, $this->sqlLogger->queries, 'just one dbal hit');
235
    }
236
237
    public function testEmptyResultCache() : void
238
    {
239
        $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey'));
240
        $data = $this->hydrateStmt($stmt);
0 ignored issues
show
Unused Code introduced by
The assignment to $data is dead and can be removed.
Loading history...
241
242
        $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey'));
243
        $data = $this->hydrateStmt($stmt);
244
245
        self::assertCount(1, $this->sqlLogger->queries, 'just one dbal hit');
246
    }
247
248
    public function testChangeCacheImpl() : void
249
    {
250
        $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey'));
251
        $data = $this->hydrateStmt($stmt);
0 ignored issues
show
Unused Code introduced by
The assignment to $data is dead and can be removed.
Loading history...
252
253
        $secondCache = new ArrayCache();
254
        $stmt        = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey', $secondCache));
255
        $data        = $this->hydrateStmt($stmt);
256
257
        self::assertCount(2, $this->sqlLogger->queries, 'two hits');
258
        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

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