ResultCacheTest.php 8.29 KB
Newer Older
1 2 3
<?php

namespace Doctrine\Tests\DBAL\Functional;
4

Sergei Morozov's avatar
Sergei Morozov committed
5
use Doctrine\Common\Cache\ArrayCache;
6
use Doctrine\DBAL\Cache\QueryCacheProfile;
7
use Doctrine\DBAL\FetchMode;
Gabriel Caruso's avatar
Gabriel Caruso committed
8
use Doctrine\DBAL\Logging\DebugStack;
Sergei Morozov's avatar
Sergei Morozov committed
9 10
use Doctrine\DBAL\Schema\Table;
use Doctrine\Tests\DbalFunctionalTestCase;
11 12 13 14 15 16
use const CASE_LOWER;
use function array_change_key_case;
use function array_merge;
use function array_shift;
use function array_values;
use function is_array;
17 18 19 20

/**
 * @group DDC-217
 */
Sergei Morozov's avatar
Sergei Morozov committed
21
class ResultCacheTest extends DbalFunctionalTestCase
22
{
Sergei Morozov's avatar
Sergei Morozov committed
23 24 25 26
    /** @var int[][]|string[][] */
    private $expectedResult = [['test_int' => 100, 'test_string' => 'foo'], ['test_int' => 200, 'test_string' => 'bar'], ['test_int' => 300, 'test_string' => 'baz']];

    /** @var DebugStack */
27 28
    private $sqlLogger;

29
    protected function setUp()
30 31 32
    {
        parent::setUp();

Sergei Morozov's avatar
Sergei Morozov committed
33
        $table = new Table('caching');
34
        $table->addColumn('test_int', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
35 36
        $table->addColumn('test_string', 'string', ['notnull' => false]);
        $table->setPrimaryKey(['test_int']);
37

Sergei Morozov's avatar
Sergei Morozov committed
38
        $sm = $this->connection->getSchemaManager();
39
        $sm->createTable($table);
40

jeroendedauw's avatar
jeroendedauw committed
41
        foreach ($this->expectedResult as $row) {
Sergei Morozov's avatar
Sergei Morozov committed
42
            $this->connection->insert('caching', $row);
43 44
        }

Sergei Morozov's avatar
Sergei Morozov committed
45
        $config                                = $this->connection->getConfiguration();
Sergei Morozov's avatar
Sergei Morozov committed
46
        $config->setSQLLogger($this->sqlLogger = new DebugStack());
47

Sergei Morozov's avatar
Sergei Morozov committed
48
        $cache = new ArrayCache();
49
        $config->setResultCacheImpl($cache);
50 51
    }

52 53
    protected function tearDown()
    {
Sergei Morozov's avatar
Sergei Morozov committed
54
        $this->connection->getSchemaManager()->dropTable('caching');
55 56 57 58

        parent::tearDown();
    }

59 60
    public function testCacheFetchAssoc()
    {
61 62 63 64
        self::assertCacheNonCacheSelectSameFetchModeAreEqual(
            $this->expectedResult,
            FetchMode::ASSOCIATIVE
        );
65 66 67 68
    }

    public function testFetchNum()
    {
Sergei Morozov's avatar
Sergei Morozov committed
69
        $expectedResult = [];
jeroendedauw's avatar
jeroendedauw committed
70
        foreach ($this->expectedResult as $v) {
71 72
            $expectedResult[] = array_values($v);
        }
73 74

        self::assertCacheNonCacheSelectSameFetchModeAreEqual($expectedResult, FetchMode::NUMERIC);
75 76 77 78
    }

    public function testFetchBoth()
    {
Sergei Morozov's avatar
Sergei Morozov committed
79
        $expectedResult = [];
jeroendedauw's avatar
jeroendedauw committed
80
        foreach ($this->expectedResult as $v) {
81 82
            $expectedResult[] = array_merge($v, array_values($v));
        }
83 84

        self::assertCacheNonCacheSelectSameFetchModeAreEqual($expectedResult, FetchMode::MIXED);
85
    }
86

87 88
    public function testFetchColumn()
    {
Sergei Morozov's avatar
Sergei Morozov committed
89
        $expectedResult = [];
jeroendedauw's avatar
jeroendedauw committed
90
        foreach ($this->expectedResult as $v) {
91 92
            $expectedResult[] = array_shift($v);
        }
93 94

        self::assertCacheNonCacheSelectSameFetchModeAreEqual($expectedResult, FetchMode::COLUMN);
95
    }
96 97 98

    public function testMixingFetch()
    {
Sergei Morozov's avatar
Sergei Morozov committed
99
        $numExpectedResult = [];
jeroendedauw's avatar
jeroendedauw committed
100
        foreach ($this->expectedResult as $v) {
101 102
            $numExpectedResult[] = array_values($v);
        }
Sergei Morozov's avatar
Sergei Morozov committed
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

Sergei Morozov's avatar
Sergei Morozov committed
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 117
    public function testIteratorFetch()
    {
118 119 120
        self::assertStandardAndIteratorFetchAreEqual(FetchMode::MIXED);
        self::assertStandardAndIteratorFetchAreEqual(FetchMode::ASSOCIATIVE);
        self::assertStandardAndIteratorFetchAreEqual(FetchMode::NUMERIC);
121 122
    }

123
    public function assertStandardAndIteratorFetchAreEqual($fetchMode)
124
    {
Sergei Morozov's avatar
Sergei Morozov committed
125
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
126
        $data = $this->hydrateStmt($stmt, $fetchMode);
127

Sergei Morozov's avatar
Sergei Morozov committed
128
        $stmt          = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
129
        $data_iterator = $this->hydrateStmtIterator($stmt, $fetchMode);
130

131
        self::assertEquals($data, $data_iterator);
132 133
    }

134 135
    public function testDontCloseNoCache()
    {
Sergei Morozov's avatar
Sergei Morozov committed
136
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
137

Sergei Morozov's avatar
Sergei Morozov committed
138
        $data = [];
139 140

        while ($row = $stmt->fetch(FetchMode::ASSOCIATIVE)) {
141 142 143
            $data[] = $row;
        }

Sergei Morozov's avatar
Sergei Morozov committed
144
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
145

Sergei Morozov's avatar
Sergei Morozov committed
146
        $data = [];
147 148

        while ($row = $stmt->fetch(FetchMode::NUMERIC)) {
149 150 151
            $data[] = $row;
        }

Gabriel Caruso's avatar
Gabriel Caruso committed
152
        self::assertCount(2, $this->sqlLogger->queries);
153 154 155 156
    }

    public function testDontFinishNoCache()
    {
Sergei Morozov's avatar
Sergei Morozov committed
157
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
158

159
        $stmt->fetch(FetchMode::ASSOCIATIVE);
160 161
        $stmt->closeCursor();

Sergei Morozov's avatar
Sergei Morozov committed
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

Gabriel Caruso's avatar
Gabriel Caruso committed
166
        self::assertCount(2, $this->sqlLogger->queries);
167 168
    }

169 170 171 172 173 174 175 176 177 178
    public function testFetchAllAndFinishSavesCache()
    {
        $layerCache = new ArrayCache();
        $stmt       = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'testcachekey', $layerCache));
        $stmt->fetchAll();
        $stmt->closeCursor();

        self::assertCount(1, $layerCache->fetch('testcachekey'));
    }

179
    public function assertCacheNonCacheSelectSameFetchModeAreEqual($expectedResult, $fetchMode)
180
    {
Sergei Morozov's avatar
Sergei Morozov committed
181
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
182

183
        self::assertEquals(2, $stmt->columnCount());
184
        $data = $this->hydrateStmt($stmt, $fetchMode);
185
        self::assertEquals($expectedResult, $data);
186

Sergei Morozov's avatar
Sergei Morozov committed
187
        $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey'));
188

189
        self::assertEquals(2, $stmt->columnCount());
190
        $data = $this->hydrateStmt($stmt, $fetchMode);
191
        self::assertEquals($expectedResult, $data);
Sergei Morozov's avatar
Sergei Morozov committed
192
        self::assertCount(1, $this->sqlLogger->queries, 'just one dbal hit');
193 194 195 196
    }

    public function testEmptyResultCache()
    {
Sergei Morozov's avatar
Sergei Morozov committed
197
        $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey'));
198 199
        $data = $this->hydrateStmt($stmt);

Sergei Morozov's avatar
Sergei Morozov committed
200
        $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey'));
201 202
        $data = $this->hydrateStmt($stmt);

Sergei Morozov's avatar
Sergei Morozov committed
203
        self::assertCount(1, $this->sqlLogger->queries, 'just one dbal hit');
204
    }
205

206 207
    public function testChangeCacheImpl()
    {
Sergei Morozov's avatar
Sergei Morozov committed
208
        $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey'));
209 210
        $data = $this->hydrateStmt($stmt);

Sergei Morozov's avatar
Sergei Morozov committed
211
        $secondCache = new ArrayCache();
Sergei Morozov's avatar
Sergei Morozov committed
212
        $stmt        = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'emptycachekey', $secondCache));
Sergei Morozov's avatar
Sergei Morozov committed
213
        $data        = $this->hydrateStmt($stmt);
214

Sergei Morozov's avatar
Sergei Morozov committed
215 216
        self::assertCount(2, $this->sqlLogger->queries, 'two hits');
        self::assertCount(1, $secondCache->fetch('emptycachekey'));
217 218
    }

219
    private function hydrateStmt($stmt, $fetchMode = FetchMode::ASSOCIATIVE)
220
    {
Sergei Morozov's avatar
Sergei Morozov committed
221
        $data = [];
222
        while ($row = $stmt->fetch($fetchMode)) {
223
            $data[] = is_array($row) ? array_change_key_case($row, CASE_LOWER) : $row;
224 225
        }
        $stmt->closeCursor();
226
        return $data;
227
    }
228

229
    private function hydrateStmtIterator($stmt, $fetchMode = FetchMode::ASSOCIATIVE)
230
    {
Sergei Morozov's avatar
Sergei Morozov committed
231
        $data = [];
232
        $stmt->setFetchMode($fetchMode);
233
        foreach ($stmt as $row) {
234
            $data[] = is_array($row) ? array_change_key_case($row, CASE_LOWER) : $row;
235 236 237 238
        }
        $stmt->closeCursor();
        return $data;
    }
239
}