Failed Conditions
Pull Request — master (#3359)
by Sergei
25:01 queued 22:04
created

StatementTest::testFetchColumnNonExistingIndex()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 8
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 Doctrine\DBAL\DBALException;
6
use Doctrine\DBAL\Driver\Statement;
7
use Doctrine\DBAL\FetchMode;
8
use Doctrine\DBAL\ParameterType;
9
use Doctrine\DBAL\Schema\Table;
10
use Doctrine\DBAL\Types\Type;
11
use Doctrine\Tests\DbalFunctionalTestCase;
12
use function base64_decode;
13
use function stream_get_contents;
14
15
class StatementTest extends DbalFunctionalTestCase
16
{
17
    protected function setUp()
18
    {
19
        parent::setUp();
20
21
        $table = new Table('stmt_test');
22
        $table->addColumn('id', 'integer');
23
        $table->addColumn('name', 'text', ['notnull' => false]);
24
        $this->connection->getSchemaManager()->dropAndCreateTable($table);
25
    }
26
27
    public function testStatementIsReusableAfterClosingCursor()
28
    {
29
        $this->connection->insert('stmt_test', ['id' => 1]);
30
        $this->connection->insert('stmt_test', ['id' => 2]);
31
32
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test ORDER BY id');
33
34
        $stmt->execute();
35
36
        $id = $stmt->fetchColumn();
37
        self::assertEquals(1, $id);
38
39
        $stmt->closeCursor();
40
41
        $stmt->execute();
42
        $id = $stmt->fetchColumn();
43
        self::assertEquals(1, $id);
44
        $id = $stmt->fetchColumn();
45
        self::assertEquals(2, $id);
46
    }
47
48
    public function testReuseStatementWithLongerResults()
49
    {
50
        $sm    = $this->connection->getSchemaManager();
51
        $table = new Table('stmt_longer_results');
52
        $table->addColumn('param', 'string');
53
        $table->addColumn('val', 'text');
54
        $sm->createTable($table);
55
56
        $row1 = [
57
            'param' => 'param1',
58
            'val' => 'X',
59
        ];
60
        $this->connection->insert('stmt_longer_results', $row1);
61
62
        $stmt = $this->connection->prepare('SELECT param, val FROM stmt_longer_results ORDER BY param');
63
        $stmt->execute();
64
        self::assertArraySubset([
65
            ['param1', 'X'],
66
        ], $stmt->fetchAll(FetchMode::NUMERIC));
67
68
        $row2 = [
69
            'param' => 'param2',
70
            'val' => 'A bit longer value',
71
        ];
72
        $this->connection->insert('stmt_longer_results', $row2);
73
74
        $stmt->execute();
75
        self::assertArraySubset([
76
            ['param1', 'X'],
77
            ['param2', 'A bit longer value'],
78
        ], $stmt->fetchAll(FetchMode::NUMERIC));
79
    }
80
81
    public function testFetchLongBlob()
82
    {
83
        // make sure memory limit is large enough to not cause false positives,
84
        // but is still not enough to store a LONGBLOB of the max possible size
85
        $this->iniSet('memory_limit', '4G');
86
87
        $sm    = $this->connection->getSchemaManager();
88
        $table = new Table('stmt_long_blob');
89
        $table->addColumn('contents', 'blob', ['length' => 0xFFFFFFFF]);
90
        $sm->createTable($table);
91
92
        $contents = base64_decode(<<<EOF
93
H4sICJRACVgCA2RvY3RyaW5lLmljbwDtVNtLFHEU/ia1i9fVzVWxvJSrZmoXS6pd0zK7QhdNc03z
94
lrpppq1pWqJCFERZkUFEDybYBQqJhB6iUOqhh+whgl4qkF6MfGh+s87O7GVmO6OlBfUfdIZvznxn
95
fpzznW9gAI4unQ50XwirH2AAkEygEuIwU58ODnPBzXGv14sEq4BrwzKKL4sY++SGTz6PodcutN5x
96
IPvsFCa+K9CXMfS/cOL5OxesN0Wceygho0WAXVLwcUJBdDVDaqOAij4Rrz640XlXQmAxQ16PHU63
97
iqdvXbg4JOHLpILBUSdM7XZEVDDcfuZEbI2ASaYguUGAroSh97GMngcSeFFFerMdI+/dyGy1o+GW
98
Ax5FxfAbFwoviajuc+DCIwn+RTwGRmRIThXxdQJyu+z4/NUDYz2DKCsILuERWsoQfoQhqpLhyhMZ
99
XfcknBmU0NLvQArpTm0SsI5mqKqKuFoGc8cUcjrtqLohom1AgtujQnapmJJU+BbwCLIwhJXyiKlh
100
MB4TkFgvIK3JjrRmAefJm+77Eiqvi+SvCq/qJahQyWuVuEpcIa7QLh7Kbsourb9b66/pZdAd1voz
101
fCNfwsp46OnZQPojSX9UFcNy+mYJNDeJPHtJfqeR/nSaPTzmwlXar5dQ1adpd+B//I9/hi0xuCPQ
102
Nkvb5um37Wtc+auQXZsVxEVYD5hnCilxTaYYjsuxLlsxXUitzd2hs3GWHLM5UOM7Fy8t3xiat4fb
103
sneNxmNb/POO1pRXc7vnF2nc13Rq0cFWiyXkuHmzxuOtzUYfC7fEmK/3mx4QZd5u4E7XJWz6+dey
104
Za4tXHUiPyB8Vm781oaT+3fN6Y/eUFDfPkcNWetNxb+tlxEZsPqPdZMOzS4rxwJ8CDC+ABj1+Tu0
105
d+N0hqezcjblboJ3Bj8ARJilHX4FAAA=
106
EOF
107
        );
108
109
        $this->connection->insert('stmt_long_blob', ['contents' => $contents], [ParameterType::LARGE_OBJECT]);
110
111
        $stmt = $this->connection->prepare('SELECT contents FROM stmt_long_blob');
112
        $stmt->execute();
113
114
        $stream = Type::getType('blob')
115
            ->convertToPHPValue(
116
                $stmt->fetchColumn(),
117
                $this->connection->getDatabasePlatform()
118
            );
119
120
        self::assertSame($contents, stream_get_contents($stream));
0 ignored issues
show
Bug introduced by
It seems like $stream can also be of type false; however, parameter $handle of stream_get_contents() does only seem to accept resource, 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

120
        self::assertSame($contents, stream_get_contents(/** @scrutinizer ignore-type */ $stream));
Loading history...
121
    }
122
123
    public function testIncompletelyFetchedStatementDoesNotBlockConnection()
124
    {
125
        $this->connection->insert('stmt_test', ['id' => 1]);
126
        $this->connection->insert('stmt_test', ['id' => 2]);
127
128
        $stmt1 = $this->connection->prepare('SELECT id FROM stmt_test');
129
        $stmt1->execute();
130
        $stmt1->fetch();
131
        $stmt1->execute();
132
        // fetching only one record out of two
133
        $stmt1->fetch();
134
135
        $stmt2 = $this->connection->prepare('SELECT id FROM stmt_test WHERE id = ?');
136
        $stmt2->execute([1]);
137
        self::assertEquals(1, $stmt2->fetchColumn());
138
    }
139
140
    public function testReuseStatementAfterClosingCursor()
141
    {
142
        $this->connection->insert('stmt_test', ['id' => 1]);
143
        $this->connection->insert('stmt_test', ['id' => 2]);
144
145
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test WHERE id = ?');
146
147
        $stmt->execute([1]);
148
        $id = $stmt->fetchColumn();
149
        self::assertEquals(1, $id);
150
151
        $stmt->closeCursor();
152
153
        $stmt->execute([2]);
154
        $id = $stmt->fetchColumn();
155
        self::assertEquals(2, $id);
156
    }
157
158
    public function testReuseStatementWithParameterBoundByReference()
159
    {
160
        $this->connection->insert('stmt_test', ['id' => 1]);
161
        $this->connection->insert('stmt_test', ['id' => 2]);
162
163
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test WHERE id = ?');
164
        $stmt->bindParam(1, $id);
165
166
        $id = 1;
0 ignored issues
show
Unused Code introduced by
The assignment to $id is dead and can be removed.
Loading history...
167
        $stmt->execute();
168
        self::assertEquals(1, $stmt->fetchColumn());
169
170
        $id = 2;
171
        $stmt->execute();
172
        self::assertEquals(2, $stmt->fetchColumn());
173
    }
174
175
    public function testReuseStatementWithReboundValue()
176
    {
177
        $this->connection->insert('stmt_test', ['id' => 1]);
178
        $this->connection->insert('stmt_test', ['id' => 2]);
179
180
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test WHERE id = ?');
181
182
        $stmt->bindValue(1, 1);
183
        $stmt->execute();
184
        self::assertEquals(1, $stmt->fetchColumn());
185
186
        $stmt->bindValue(1, 2);
187
        $stmt->execute();
188
        self::assertEquals(2, $stmt->fetchColumn());
189
    }
190
191
    public function testReuseStatementWithReboundParam()
192
    {
193
        $this->connection->insert('stmt_test', ['id' => 1]);
194
        $this->connection->insert('stmt_test', ['id' => 2]);
195
196
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test WHERE id = ?');
197
198
        $x = 1;
199
        $stmt->bindParam(1, $x);
200
        $stmt->execute();
201
        self::assertEquals(1, $stmt->fetchColumn());
202
203
        $y = 2;
204
        $stmt->bindParam(1, $y);
205
        $stmt->execute();
206
        self::assertEquals(2, $stmt->fetchColumn());
207
    }
208
209
    /**
210
     * @dataProvider emptyFetchProvider
211
     */
212
    public function testFetchFromNonExecutedStatement(callable $fetch, $expected)
213
    {
214
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test');
215
216
        self::assertSame($expected, $fetch($stmt));
217
    }
218
219
    public function testCloseCursorOnNonExecutedStatement()
220
    {
221
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test');
222
223
        self::assertTrue($stmt->closeCursor());
224
    }
225
226
    /**
227
     * @group DBAL-2637
228
     */
229
    public function testCloseCursorAfterCursorEnd()
230
    {
231
        $stmt = $this->connection->prepare('SELECT name FROM stmt_test');
232
233
        $stmt->execute();
234
        $stmt->fetch();
235
236
        self::assertTrue($stmt->closeCursor());
237
    }
238
239
    /**
240
     * @dataProvider emptyFetchProvider
241
     */
242
    public function testFetchFromNonExecutedStatementWithClosedCursor(callable $fetch, $expected)
243
    {
244
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test');
245
        $stmt->closeCursor();
246
247
        self::assertSame($expected, $fetch($stmt));
248
    }
249
250
    /**
251
     * @dataProvider emptyFetchProvider
252
     */
253
    public function testFetchFromExecutedStatementWithClosedCursor(callable $fetch, $expected)
254
    {
255
        $this->connection->insert('stmt_test', ['id' => 1]);
256
257
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test');
258
        $stmt->execute();
259
        $stmt->closeCursor();
260
261
        self::assertSame($expected, $fetch($stmt));
262
    }
263
264
    public static function emptyFetchProvider()
265
    {
266
        return [
267
            'fetch' => [
268
                static function (Statement $stmt) {
269
                    return $stmt->fetch();
270
                },
271
                false,
272
            ],
273
            'fetch-column' => [
274
                static function (Statement $stmt) {
275
                    return $stmt->fetchColumn();
276
                },
277
                false,
278
            ],
279
            'fetch-all' => [
280
                static function (Statement $stmt) {
281
                    return $stmt->fetchAll();
282
                },
283
                [],
284
            ],
285
        ];
286
    }
287
288
    public function testFetchInColumnMode() : void
289
    {
290
        $platform = $this->connection->getDatabasePlatform();
291
        $query    = $platform->getDummySelectSQL();
292
        $result   = $this->connection->executeQuery($query)->fetch(FetchMode::COLUMN);
293
294
        self::assertEquals(1, $result);
295
    }
296
297
    public function testFetchColumnNonExistingIndex() : void
298
    {
299
        $platform = $this->connection->getDatabasePlatform();
300
        $query    = $platform->getDummySelectSQL();
301
        $stmt     = $this->connection->query($query);
302
303
        self::expectException(DBALException::class);
0 ignored issues
show
Bug Best Practice introduced by
The method PHPUnit\Framework\TestCase::expectException() 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

303
        self::/** @scrutinizer ignore-call */ 
304
              expectException(DBALException::class);
Loading history...
304
        $stmt->fetchColumn(1);
305
    }
306
}
307