Failed Conditions
Push — develop ( 0ef7d4...776429 )
by Marco
04:49 queued 04:40
created

StatementTest::testExecWithRedundantParameters()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 34
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

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

121
        self::assertSame($contents, stream_get_contents(/** @scrutinizer ignore-type */ $stream));
Loading history...
122
    }
123
124
    public function testIncompletelyFetchedStatementDoesNotBlockConnection()
125
    {
126
        $this->connection->insert('stmt_test', ['id' => 1]);
127
        $this->connection->insert('stmt_test', ['id' => 2]);
128
129
        $stmt1 = $this->connection->prepare('SELECT id FROM stmt_test');
130
        $stmt1->execute();
131
        $stmt1->fetch();
132
        $stmt1->execute();
133
        // fetching only one record out of two
134
        $stmt1->fetch();
135
136
        $stmt2 = $this->connection->prepare('SELECT id FROM stmt_test WHERE id = ?');
137
        $stmt2->execute([1]);
138
        self::assertEquals(1, $stmt2->fetchColumn());
139
    }
140
141
    public function testReuseStatementAfterClosingCursor()
142
    {
143
        $this->connection->insert('stmt_test', ['id' => 1]);
144
        $this->connection->insert('stmt_test', ['id' => 2]);
145
146
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test WHERE id = ?');
147
148
        $stmt->execute([1]);
149
        $id = $stmt->fetchColumn();
150
        self::assertEquals(1, $id);
151
152
        $stmt->closeCursor();
153
154
        $stmt->execute([2]);
155
        $id = $stmt->fetchColumn();
156
        self::assertEquals(2, $id);
157
    }
158
159
    public function testReuseStatementWithParameterBoundByReference()
160
    {
161
        $this->connection->insert('stmt_test', ['id' => 1]);
162
        $this->connection->insert('stmt_test', ['id' => 2]);
163
164
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test WHERE id = ?');
165
        $stmt->bindParam(1, $id);
166
167
        $id = 1;
0 ignored issues
show
Unused Code introduced by
The assignment to $id is dead and can be removed.
Loading history...
168
        $stmt->execute();
169
        self::assertEquals(1, $stmt->fetchColumn());
170
171
        $id = 2;
172
        $stmt->execute();
173
        self::assertEquals(2, $stmt->fetchColumn());
174
    }
175
176
    public function testReuseStatementWithReboundValue()
177
    {
178
        $this->connection->insert('stmt_test', ['id' => 1]);
179
        $this->connection->insert('stmt_test', ['id' => 2]);
180
181
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test WHERE id = ?');
182
183
        $stmt->bindValue(1, 1);
184
        $stmt->execute();
185
        self::assertEquals(1, $stmt->fetchColumn());
186
187
        $stmt->bindValue(1, 2);
188
        $stmt->execute();
189
        self::assertEquals(2, $stmt->fetchColumn());
190
    }
191
192
    public function testReuseStatementWithReboundParam()
193
    {
194
        $this->connection->insert('stmt_test', ['id' => 1]);
195
        $this->connection->insert('stmt_test', ['id' => 2]);
196
197
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test WHERE id = ?');
198
199
        $x = 1;
200
        $stmt->bindParam(1, $x);
201
        $stmt->execute();
202
        self::assertEquals(1, $stmt->fetchColumn());
203
204
        $y = 2;
205
        $stmt->bindParam(1, $y);
206
        $stmt->execute();
207
        self::assertEquals(2, $stmt->fetchColumn());
208
    }
209
210
    /**
211
     * @dataProvider emptyFetchProvider
212
     */
213
    public function testFetchFromNonExecutedStatement(callable $fetch, $expected)
214
    {
215
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test');
216
217
        self::assertSame($expected, $fetch($stmt));
218
    }
219
220
    public function testCloseCursorOnNonExecutedStatement()
221
    {
222
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test');
223
224
        self::assertTrue($stmt->closeCursor());
225
    }
226
227
    /**
228
     * @group DBAL-2637
229
     */
230
    public function testCloseCursorAfterCursorEnd()
231
    {
232
        $stmt = $this->connection->prepare('SELECT name FROM stmt_test');
233
234
        $stmt->execute();
235
        $stmt->fetch();
236
237
        self::assertTrue($stmt->closeCursor());
238
    }
239
240
    /**
241
     * @dataProvider emptyFetchProvider
242
     */
243
    public function testFetchFromNonExecutedStatementWithClosedCursor(callable $fetch, $expected)
244
    {
245
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test');
246
        $stmt->closeCursor();
247
248
        self::assertSame($expected, $fetch($stmt));
249
    }
250
251
    /**
252
     * @dataProvider emptyFetchProvider
253
     */
254
    public function testFetchFromExecutedStatementWithClosedCursor(callable $fetch, $expected)
255
    {
256
        $this->connection->insert('stmt_test', ['id' => 1]);
257
258
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test');
259
        $stmt->execute();
260
        $stmt->closeCursor();
261
262
        self::assertSame($expected, $fetch($stmt));
263
    }
264
265
    public static function emptyFetchProvider()
266
    {
267
        return [
268
            'fetch' => [
269
                static function (Statement $stmt) {
270
                    return $stmt->fetch();
271
                },
272
                false,
273
            ],
274
            'fetch-column' => [
275
                static function (Statement $stmt) {
276
                    return $stmt->fetchColumn();
277
                },
278
                false,
279
            ],
280
            'fetch-all' => [
281
                static function (Statement $stmt) {
282
                    return $stmt->fetchAll();
283
                },
284
                [],
285
            ],
286
        ];
287
    }
288
289
    public function testFetchInColumnMode() : void
290
    {
291
        $platform = $this->connection->getDatabasePlatform();
292
        $query    = $platform->getDummySelectSQL();
293
        $result   = $this->connection->executeQuery($query)->fetch(FetchMode::COLUMN);
294
295
        self::assertEquals(1, $result);
296
    }
297
298
    public function testExecWithRedundantParameters() : void
299
    {
300
        $driver = $this->connection->getDriver()->getName();
301
302
        switch ($driver) {
303
            case 'pdo_mysql':
304
            case 'pdo_oracle':
305
            case 'pdo_sqlsrv':
306
                self::markTestSkipped(sprintf(
307
                    'PDOStatement::execute() implemented in the "%s" driver does not report redundant parameters',
308
                    $driver
309
                ));
310
311
                return;
312
            case 'ibm_db2':
313
                self::markTestSkipped('db2_execute() does not report redundant parameters');
314
315
                return;
316
            case 'sqlsrv':
317
                self::markTestSkipped('sqlsrv_prepare() does not report redundant parameters');
318
319
                return;
320
        }
321
322
        $platform = $this->connection->getDatabasePlatform();
323
        $query    = $platform->getDummySelectSQL();
324
        $stmt     = $this->connection->prepare($query);
325
326
        // we want to make sure the exception is thrown by the DBAL code, not by PHPUnit due to a PHP-level error,
327
        // but the wrapper connection wraps everything in a DBAL exception
328
        $this->iniSet('error_reporting', 0);
329
330
        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

330
        self::/** @scrutinizer ignore-call */ 
331
              expectException(DBALException::class);
Loading history...
331
        $stmt->execute([null]);
332
    }
333
}
334