StatementTest.php 9.8 KB
Newer Older
1 2
<?php

3
namespace Doctrine\DBAL\Tests\Functional;
4

5
use Doctrine\DBAL\Driver\Exception;
6
use Doctrine\DBAL\Driver\PDOOracle\Driver as PDOOracleDriver;
7
use Doctrine\DBAL\Driver\Result;
8
use Doctrine\DBAL\ParameterType;
9
use Doctrine\DBAL\Schema\Table;
10
use Doctrine\DBAL\Tests\FunctionalTestCase;
11
use Doctrine\DBAL\Types\Type;
12

13 14
use function base64_decode;
use function stream_get_contents;
15

16
class StatementTest extends FunctionalTestCase
17
{
18
    protected function setUp(): void
19
    {
20 21 22
        parent::setUp();

        $table = new Table('stmt_test');
23
        $table->addColumn('id', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
24
        $table->addColumn('name', 'text', ['notnull' => false]);
Sergei Morozov's avatar
Sergei Morozov committed
25
        $this->connection->getSchemaManager()->dropAndCreateTable($table);
26
    }
27

28
    public function testStatementIsReusableAfterFreeingResult(): void
29
    {
30
        if ($this->connection->getDriver() instanceof PDOOracleDriver) {
31
            self::markTestIncomplete('See https://bugs.php.net/bug.php?id=77181');
32 33
        }

Sergei Morozov's avatar
Sergei Morozov committed
34 35
        $this->connection->insert('stmt_test', ['id' => 1]);
        $this->connection->insert('stmt_test', ['id' => 2]);
36

Sergei Morozov's avatar
Sergei Morozov committed
37
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test ORDER BY id');
38

39
        $result = $stmt->execute();
40

41
        $id = $result->fetchOne();
42
        self::assertEquals(1, $id);
43

44
        $result->free();
45

46 47 48
        $result = $stmt->execute();
        self::assertEquals(1, $result->fetchOne());
        self::assertEquals(2, $result->fetchOne());
49 50
    }

51
    public function testReuseStatementWithLongerResults(): void
52
    {
53
        if ($this->connection->getDriver() instanceof PDOOracleDriver) {
54
            self::markTestIncomplete('PDO_OCI doesn\'t support fetching blobs via PDOStatement::fetchAll()');
55 56
        }

Sergei Morozov's avatar
Sergei Morozov committed
57
        $sm    = $this->connection->getSchemaManager();
58
        $table = new Table('stmt_longer_results');
59 60 61 62
        $table->addColumn('param', 'string');
        $table->addColumn('val', 'text');
        $sm->createTable($table);

Sergei Morozov's avatar
Sergei Morozov committed
63
        $row1 = [
64 65
            'param' => 'param1',
            'val' => 'X',
Sergei Morozov's avatar
Sergei Morozov committed
66
        ];
Sergei Morozov's avatar
Sergei Morozov committed
67
        $this->connection->insert('stmt_longer_results', $row1);
68

69 70
        $stmt   = $this->connection->prepare('SELECT param, val FROM stmt_longer_results ORDER BY param');
        $result = $stmt->execute();
71
        self::assertEquals([
Sergei Morozov's avatar
Sergei Morozov committed
72
            ['param1', 'X'],
73
        ], $result->fetchAllNumeric());
74

Sergei Morozov's avatar
Sergei Morozov committed
75
        $row2 = [
76 77
            'param' => 'param2',
            'val' => 'A bit longer value',
Sergei Morozov's avatar
Sergei Morozov committed
78
        ];
Sergei Morozov's avatar
Sergei Morozov committed
79
        $this->connection->insert('stmt_longer_results', $row2);
80

81
        $result = $stmt->execute();
82
        self::assertEquals([
Sergei Morozov's avatar
Sergei Morozov committed
83 84
            ['param1', 'X'],
            ['param2', 'A bit longer value'],
85
        ], $result->fetchAllNumeric());
86
    }
87

88
    public function testFetchLongBlob(): void
89
    {
90 91 92
        if ($this->connection->getDriver() instanceof PDOOracleDriver) {
            // inserting BLOBs as streams on Oracle requires Oracle-specific SQL syntax which is currently not supported
            // see http://php.net/manual/en/pdo.lobs.php#example-1035
93
            self::markTestSkipped('DBAL doesn\'t support storing LOBs represented as streams using PDO_OCI');
94 95
        }

96 97 98 99
        // make sure memory limit is large enough to not cause false positives,
        // but is still not enough to store a LONGBLOB of the max possible size
        $this->iniSet('memory_limit', '4G');

Sergei Morozov's avatar
Sergei Morozov committed
100
        $sm    = $this->connection->getSchemaManager();
101
        $table = new Table('stmt_long_blob');
Sergei Morozov's avatar
Sergei Morozov committed
102
        $table->addColumn('contents', 'blob', ['length' => 0xFFFFFFFF]);
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
        $sm->createTable($table);

        $contents = base64_decode(<<<EOF
H4sICJRACVgCA2RvY3RyaW5lLmljbwDtVNtLFHEU/ia1i9fVzVWxvJSrZmoXS6pd0zK7QhdNc03z
lrpppq1pWqJCFERZkUFEDybYBQqJhB6iUOqhh+whgl4qkF6MfGh+s87O7GVmO6OlBfUfdIZvznxn
fpzznW9gAI4unQ50XwirH2AAkEygEuIwU58ODnPBzXGv14sEq4BrwzKKL4sY++SGTz6PodcutN5x
IPvsFCa+K9CXMfS/cOL5OxesN0Wceygho0WAXVLwcUJBdDVDaqOAij4Rrz640XlXQmAxQ16PHU63
iqdvXbg4JOHLpILBUSdM7XZEVDDcfuZEbI2ASaYguUGAroSh97GMngcSeFFFerMdI+/dyGy1o+GW
Ax5FxfAbFwoviajuc+DCIwn+RTwGRmRIThXxdQJyu+z4/NUDYz2DKCsILuERWsoQfoQhqpLhyhMZ
XfcknBmU0NLvQArpTm0SsI5mqKqKuFoGc8cUcjrtqLohom1AgtujQnapmJJU+BbwCLIwhJXyiKlh
MB4TkFgvIK3JjrRmAefJm+77Eiqvi+SvCq/qJahQyWuVuEpcIa7QLh7Kbsourb9b66/pZdAd1voz
fCNfwsp46OnZQPojSX9UFcNy+mYJNDeJPHtJfqeR/nSaPTzmwlXar5dQ1adpd+B//I9/hi0xuCPQ
Nkvb5um37Wtc+auQXZsVxEVYD5hnCilxTaYYjsuxLlsxXUitzd2hs3GWHLM5UOM7Fy8t3xiat4fb
sneNxmNb/POO1pRXc7vnF2nc13Rq0cFWiyXkuHmzxuOtzUYfC7fEmK/3mx4QZd5u4E7XJWz6+dey
Za4tXHUiPyB8Vm781oaT+3fN6Y/eUFDfPkcNWetNxb+tlxEZsPqPdZMOzS4rxwJ8CDC+ABj1+Tu0
d+N0hqezcjblboJ3Bj8ARJilHX4FAAA=
EOF
120
        , true);
121

Sergei Morozov's avatar
Sergei Morozov committed
122
        $this->connection->insert('stmt_long_blob', ['contents' => $contents], [ParameterType::LARGE_OBJECT]);
123

124 125
        $result = $this->connection->prepare('SELECT contents FROM stmt_long_blob')
            ->execute();
126 127 128

        $stream = Type::getType('blob')
            ->convertToPHPValue(
129
                $result->fetchOne(),
Sergei Morozov's avatar
Sergei Morozov committed
130
                $this->connection->getDatabasePlatform()
131 132
            );

133
        self::assertSame($contents, stream_get_contents($stream));
134
    }
135

136
    public function testIncompletelyFetchedStatementDoesNotBlockConnection(): void
137
    {
Sergei Morozov's avatar
Sergei Morozov committed
138 139
        $this->connection->insert('stmt_test', ['id' => 1]);
        $this->connection->insert('stmt_test', ['id' => 2]);
140

141 142 143 144 145
        $stmt1  = $this->connection->prepare('SELECT id FROM stmt_test');
        $result = $stmt1->execute();
        $result->fetchAssociative();

        $result = $stmt1->execute();
146
        // fetching only one record out of two
147
        $result->fetchAssociative();
148

149 150 151
        $stmt2  = $this->connection->prepare('SELECT id FROM stmt_test WHERE id = ?');
        $result = $stmt2->execute([1]);
        self::assertEquals(1, $result->fetchOne());
152 153
    }

154
    public function testReuseStatementAfterFreeingResult(): void
155
    {
156
        if ($this->connection->getDriver() instanceof PDOOracleDriver) {
157
            self::markTestIncomplete('See https://bugs.php.net/bug.php?id=77181');
158 159
        }

Sergei Morozov's avatar
Sergei Morozov committed
160 161
        $this->connection->insert('stmt_test', ['id' => 1]);
        $this->connection->insert('stmt_test', ['id' => 2]);
162

Sergei Morozov's avatar
Sergei Morozov committed
163
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test WHERE id = ?');
164

165 166 167
        $result = $stmt->execute([1]);

        $id = $result->fetchOne();
168
        self::assertEquals(1, $id);
169

170 171 172
        $result->free();

        $result = $stmt->execute([2]);
173

174
        $id = $result->fetchOne();
175
        self::assertEquals(2, $id);
176
    }
177

178
    public function testReuseStatementWithParameterBoundByReference(): void
179
    {
Sergei Morozov's avatar
Sergei Morozov committed
180 181
        $this->connection->insert('stmt_test', ['id' => 1]);
        $this->connection->insert('stmt_test', ['id' => 2]);
182

Sergei Morozov's avatar
Sergei Morozov committed
183
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test WHERE id = ?');
184 185 186
        $stmt->bindParam(1, $id);

        $id = 1;
187 188 189

        $result = $stmt->execute();
        self::assertEquals(1, $result->fetchOne());
190 191

        $id = 2;
192 193 194

        $result = $stmt->execute();
        self::assertEquals(2, $result->fetchOne());
195 196
    }

197
    public function testReuseStatementWithReboundValue(): void
198
    {
Sergei Morozov's avatar
Sergei Morozov committed
199 200
        $this->connection->insert('stmt_test', ['id' => 1]);
        $this->connection->insert('stmt_test', ['id' => 2]);
201

Sergei Morozov's avatar
Sergei Morozov committed
202
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test WHERE id = ?');
203 204

        $stmt->bindValue(1, 1);
205 206
        $result = $stmt->execute();
        self::assertEquals(1, $result->fetchOne());
207 208

        $stmt->bindValue(1, 2);
209 210
        $result = $stmt->execute();
        self::assertEquals(2, $result->fetchOne());
211 212
    }

213
    public function testReuseStatementWithReboundParam(): void
214
    {
Sergei Morozov's avatar
Sergei Morozov committed
215 216
        $this->connection->insert('stmt_test', ['id' => 1]);
        $this->connection->insert('stmt_test', ['id' => 2]);
217

Sergei Morozov's avatar
Sergei Morozov committed
218
        $stmt = $this->connection->prepare('SELECT id FROM stmt_test WHERE id = ?');
219 220 221

        $x = 1;
        $stmt->bindParam(1, $x);
222 223
        $result = $stmt->execute();
        self::assertEquals(1, $result->fetchOne());
224 225 226

        $y = 2;
        $stmt->bindParam(1, $y);
227 228
        $result = $stmt->execute();
        self::assertEquals(2, $result->fetchOne());
229 230
    }

231
    /**
232 233
     * @param mixed $expected
     *
234 235
     * @dataProvider emptyFetchProvider
     */
236
    public function testFetchFromExecutedStatementWithFreedResult(callable $fetch, $expected): void
237
    {
238
        $this->connection->insert('stmt_test', ['id' => 1]);
239

240 241 242
        $stmt   = $this->connection->prepare('SELECT id FROM stmt_test');
        $result = $stmt->execute();
        $result->free();
243

244
        try {
245 246 247 248
            // some drivers will trigger a PHP error here which, if not suppressed,
            // would be converted to a PHPUnit exception prior to DBAL throwing its own one
            $value = @$fetch($result);
        } catch (Exception $e) {
249 250
            // The drivers that enforce the command sequencing internally will throw an exception
            $this->expectNotToPerformAssertions();
251

252 253
            return;
        }
254

255 256
        // Other drivers will silently return an empty result
        self::assertSame($expected, $value);
257 258
    }

259 260 261
    /**
     * @return mixed[][]
     */
262
    public static function emptyFetchProvider(): iterable
263
    {
Sergei Morozov's avatar
Sergei Morozov committed
264 265
        return [
            'fetch' => [
266 267
                static function (Result $result) {
                    return $result->fetchAssociative();
268 269
                },
                false,
Sergei Morozov's avatar
Sergei Morozov committed
270 271
            ],
            'fetch-column' => [
272 273
                static function (Result $result) {
                    return $result->fetchOne();
274 275
                },
                false,
Sergei Morozov's avatar
Sergei Morozov committed
276 277
            ],
            'fetch-all' => [
278 279
                static function (Result $result): array {
                    return $result->fetchAllAssociative();
280
                },
Sergei Morozov's avatar
Sergei Morozov committed
281 282 283
                [],
            ],
        ];
284
    }
285

286
    public function testFetchInColumnMode(): void
287
    {
Sergei Morozov's avatar
Sergei Morozov committed
288
        $platform = $this->connection->getDatabasePlatform();
289
        $query    = $platform->getDummySelectSQL();
290
        $result   = $this->connection->executeQuery($query)->fetchOne();
291 292 293

        self::assertEquals(1, $result);
    }
294
}