Failed Conditions
Pull Request — master (#4007)
by Sergei
62:58
created

ResultCacheTest::fetchModeProvider()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 0
dl 0
loc 11
rs 10
c 0
b 0
f 0
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\Logging\DebugStack;
11
use Doctrine\DBAL\Schema\Table;
12
use Doctrine\DBAL\Tests\FunctionalTestCase;
13
use function array_change_key_case;
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 FunctionalTestCase
23
{
24
    /** @var array<int, array<string, 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', [
37
            'length' => 8,
38
            'notnull' => false,
39
        ]);
40
        $table->setPrimaryKey(['test_int']);
41
42
        $sm = $this->connection->getSchemaManager();
43
        $sm->dropAndCreateTable($table);
44
45
        foreach ($this->expectedResult as $row) {
46
            $this->connection->insert('caching', $row);
47
        }
48
49
        $config = $this->connection->getConfiguration();
50
        $config->setSQLLogger($this->sqlLogger = new DebugStack());
51
52
        $cache = new ArrayCache();
53
        $config->setResultCacheImpl($cache);
54
    }
55
56
    public function testCacheFetchAssociative() : void
57
    {
58
        $this->assertCacheNonCacheSelectSameFetchModeAreEqual(
59
            $this->expectedResult,
60
            static function (ResultStatement $stmt) {
61
                return $stmt->fetchAssociative();
62
            }
63
        );
64
    }
65
66
    public function testFetchNumeric() : void
67
    {
68
        $expectedResult = [];
69
        foreach ($this->expectedResult as $v) {
70
            $expectedResult[] = array_values($v);
71
        }
72
73
        $this->assertCacheNonCacheSelectSameFetchModeAreEqual(
74
            $expectedResult,
75
            static function (ResultStatement $stmt) {
76
                return $stmt->fetchNumeric();
77
            }
78
        );
79
    }
80
81
    public function testFetchOne() : void
82
    {
83
        $expectedResult = [];
84
        foreach ($this->expectedResult as $v) {
85
            $expectedResult[] = array_shift($v);
86
        }
87
88
        $this->assertCacheNonCacheSelectSameFetchModeAreEqual(
89
            $expectedResult,
90
            static function (ResultStatement $stmt) {
91
                return $stmt->fetchOne();
92
            }
93
        );
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, static function (ResultStatement $stmt) {
106
            return $stmt->fetchAssociative();
107
        });
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, static function (ResultStatement $stmt) {
114
            return $stmt->fetchNumeric();
115
        });
116
117
        self::assertEquals($numExpectedResult, $data);
118
    }
119
120
    /**
121
     * @dataProvider fetchModeProvider
122
     */
123
    public function testIteratorFetch(callable $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->fetchAssociative()) !== false) {
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->fetchNumeric()) !== false) {
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->fetchAssociative();
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, static function (ResultStatement $stmt) {
165
            return $stmt->fetchNumeric();
166
        });
167
168
        self::assertCount(2, $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->fetchAllAssociative();
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 testfetchColumn() : void
182
    {
183
        $query = $this->connection->getDatabasePlatform()
184
            ->getDummySelectSQL('1');
185
186
        $qcp = new QueryCacheProfile(0, null, new ArrayCache());
187
188
        $stmt = $this->connection->executeCacheQuery($query, [], [], $qcp);
189
        $stmt->fetchColumn();
190
        $stmt->closeCursor();
191
192
        $stmt = $this->connection->executeCacheQuery($query, [], [], $qcp);
193
194
        self::assertEquals([1], $stmt->fetchColumn());
195
    }
196
197
    /**
198
     * @param array<int, mixed> $expectedResult
199
     */
200
    private function assertCacheNonCacheSelectSameFetchModeAreEqual(array $expectedResult, callable $fetchMode) : void
201
    {
202
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
203
204
        self::assertEquals(2, $stmt->columnCount());
205
        $data = $this->hydrateStmt($stmt, $fetchMode);
206
        self::assertEquals($expectedResult, $data);
207
208
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
209
210
        self::assertEquals(2, $stmt->columnCount());
211
        $data = $this->hydrateStmt($stmt, $fetchMode);
212
        self::assertEquals($expectedResult, $data);
213
        self::assertCount(1, $this->sqlLogger->queries, 'just one dbal hit');
214
    }
215
216
    public function testEmptyResultCache() : void
217
    {
218
        $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey'));
219
        $this->hydrateStmt($stmt, static function (ResultStatement $stmt) {
220
            return $stmt->fetchAssociative();
221
        });
222
223
        $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey'));
224
        $this->hydrateStmt($stmt, static function (ResultStatement $stmt) {
225
            return $stmt->fetchAssociative();
226
        });
227
228
        self::assertCount(1, $this->sqlLogger->queries, 'just one dbal hit');
229
    }
230
231
    public function testChangeCacheImpl() : void
232
    {
233
        $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey'));
234
        $this->hydrateStmt($stmt, static function (ResultStatement $stmt) {
235
            return $stmt->fetchAssociative();
236
        });
237
238
        $secondCache = new ArrayCache();
239
        $stmt        = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey', $secondCache));
240
        $this->hydrateStmt($stmt, static function (ResultStatement $stmt) {
241
            return $stmt->fetchAssociative();
242
        });
243
244
        self::assertCount(2, $this->sqlLogger->queries, 'two hits');
245
        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

245
        self::assertCount(1, /** @scrutinizer ignore-type */ $secondCache->fetch('emptycachekey'));
Loading history...
246
    }
247
248
    /**
249
     * @return iterable<string,array<int,mixed>>
250
     */
251
    public static function fetchModeProvider() : iterable
252
    {
253
        yield 'associative' => [
254
            static function (ResultStatement $stmt) {
255
                return $stmt->fetchAssociative();
256
            },
257
        ];
258
259
        yield 'numeric' => [
260
            static function (ResultStatement $stmt) {
261
                return $stmt->fetchNumeric();
262
            },
263
        ];
264
    }
265
266
    /**
267
     * @return array<int, mixed>
268
     */
269
    private function hydrateStmt(ResultStatement $stmt, callable $fetchMode) : array
270
    {
271
        $data = [];
272
        while (($row = $fetchMode($stmt)) !== false) {
273
            $data[] = is_array($row) ? array_change_key_case($row, CASE_LOWER) : $row;
274
        }
275
276
        $stmt->closeCursor();
277
278
        return $data;
279
    }
280
281
    /**
282
     * @return array<int, mixed>
283
     */
284
    private function hydrateStmtIterator(ResultStatement $stmt, callable $fetchMode) : array
285
    {
286
        $data = [];
287
288
        while (($row = $fetchMode($stmt)) !== false) {
289
            $data[] = is_array($row) ? array_change_key_case($row, CASE_LOWER) : $row;
290
        }
291
292
        $stmt->closeCursor();
293
294
        return $data;
295
    }
296
}
297