Failed Conditions
Push — master ( ff1501...ac0e13 )
by Sergei
22s queued 17s
created

tests/Functional/ConnectionTest.php (3 issues)

Severity
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\DBAL\Tests\Functional;
6
7
use Doctrine\DBAL\Connection;
8
use Doctrine\DBAL\ConnectionException;
9
use Doctrine\DBAL\Driver\Connection as DriverConnection;
10
use Doctrine\DBAL\Driver\PDOConnection;
11
use Doctrine\DBAL\DriverManager;
12
use Doctrine\DBAL\Platforms\AbstractPlatform;
13
use Doctrine\DBAL\Platforms\SqlitePlatform;
14
use Doctrine\DBAL\Platforms\SQLServer2012Platform;
15
use Doctrine\DBAL\Tests\FunctionalTestCase;
16
use Doctrine\DBAL\Tests\TestUtil;
17
use Error;
18
use Exception;
19
use PDO;
20
use RuntimeException;
21
use Throwable;
22
use function file_exists;
23
use function in_array;
24
use function unlink;
25
26
class ConnectionTest extends FunctionalTestCase
27
{
28
    protected function setUp() : void
29
    {
30
        $this->resetSharedConn();
31
        parent::setUp();
32
    }
33
34
    protected function tearDown() : void
35
    {
36
        if (file_exists('/tmp/test_nesting.sqlite')) {
37
            unlink('/tmp/test_nesting.sqlite');
38
        }
39
40
        parent::tearDown();
41
        $this->resetSharedConn();
42
    }
43
44
    public function testGetWrappedConnection() : void
45
    {
46
        self::assertInstanceOf(DriverConnection::class, $this->connection->getWrappedConnection());
47
    }
48
49
    public function testCommitWithRollbackOnlyThrowsException() : void
50
    {
51
        $this->connection->beginTransaction();
52
        $this->connection->setRollbackOnly();
53
54
        $this->expectException(ConnectionException::class);
55
        $this->connection->commit();
56
    }
57
58
    public function testTransactionNestingBehavior() : void
59
    {
60
        try {
61
            $this->connection->beginTransaction();
62
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
63
64
            try {
65
                $this->connection->beginTransaction();
66
                self::assertEquals(2, $this->connection->getTransactionNestingLevel());
67
68
                throw new Exception();
69
70
                $this->connection->commit(); // never reached
0 ignored issues
show
$this->connection->commit() is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
71
            } catch (Throwable $e) {
72
                $this->connection->rollBack();
73
                self::assertEquals(1, $this->connection->getTransactionNestingLevel());
74
                //no rethrow
75
            }
76
77
            self::assertTrue($this->connection->isRollbackOnly());
78
79
            $this->connection->commit(); // should throw exception
80
            $this->fail('Transaction commit after failed nested transaction should fail.');
81
        } catch (ConnectionException $e) {
82
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
83
            $this->connection->rollBack();
84
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
85
        }
86
87
        $this->connection->beginTransaction();
88
        $this->connection->close();
89
        $this->connection->beginTransaction();
90
        self::assertEquals(1, $this->connection->getTransactionNestingLevel());
91
    }
92
93
    public function testTransactionNestingLevelIsResetOnReconnect() : void
94
    {
95
        if ($this->connection->getDatabasePlatform()->getName() === 'sqlite') {
96
            $params           = $this->connection->getParams();
97
            $params['memory'] = false;
98
            $params['path']   = '/tmp/test_nesting.sqlite';
99
100
            $connection = DriverManager::getConnection(
101
                $params,
102
                $this->connection->getConfiguration(),
103
                $this->connection->getEventManager()
104
            );
105
        } else {
106
            $connection = $this->connection;
107
        }
108
109
        $connection->executeQuery('CREATE TABLE test_nesting(test int not null)');
110
111
        $this->connection->beginTransaction();
112
        $this->connection->beginTransaction();
113
        $connection->close(); // connection closed in runtime (for example if lost or another application logic)
114
115
        $connection->beginTransaction();
116
        $connection->executeQuery('insert into test_nesting values (33)');
117
        $connection->rollback();
118
119
        self::assertEquals(0, $connection->fetchColumn('select count(*) from test_nesting'));
120
    }
121
122
    public function testTransactionNestingBehaviorWithSavepoints() : void
123
    {
124
        if (! $this->connection->getDatabasePlatform()->supportsSavepoints()) {
125
            $this->markTestSkipped('This test requires the platform to support savepoints.');
126
        }
127
128
        $this->connection->setNestTransactionsWithSavepoints(true);
129
        try {
130
            $this->connection->beginTransaction();
131
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
132
133
            try {
134
                $this->connection->beginTransaction();
135
                self::assertEquals(2, $this->connection->getTransactionNestingLevel());
136
                $this->connection->beginTransaction();
137
                self::assertEquals(3, $this->connection->getTransactionNestingLevel());
138
                $this->connection->commit();
139
                self::assertEquals(2, $this->connection->getTransactionNestingLevel());
140
141
                throw new Exception();
142
143
                $this->connection->commit(); // never reached
0 ignored issues
show
$this->connection->commit() is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
144
            } catch (Throwable $e) {
145
                $this->connection->rollBack();
146
                self::assertEquals(1, $this->connection->getTransactionNestingLevel());
147
                //no rethrow
148
            }
149
150
            self::assertFalse($this->connection->isRollbackOnly());
151
            try {
152
                $this->connection->setNestTransactionsWithSavepoints(false);
153
                $this->fail('Should not be able to disable savepoints in usage for nested transactions inside an open transaction.');
154
            } catch (ConnectionException $e) {
155
                self::assertTrue($this->connection->getNestTransactionsWithSavepoints());
156
            }
157
158
            $this->connection->commit(); // should not throw exception
159
        } catch (ConnectionException $e) {
160
            $this->fail('Transaction commit after failed nested transaction should not fail when using savepoints.');
161
            $this->connection->rollBack();
162
        }
163
    }
164
165
    public function testTransactionNestingBehaviorCantBeChangedInActiveTransaction() : void
166
    {
167
        if (! $this->connection->getDatabasePlatform()->supportsSavepoints()) {
168
            $this->markTestSkipped('This test requires the platform to support savepoints.');
169
        }
170
171
        $this->connection->beginTransaction();
172
        $this->expectException(ConnectionException::class);
173
        $this->connection->setNestTransactionsWithSavepoints(true);
174
    }
175
176
    public function testSetNestedTransactionsThroughSavepointsNotSupportedThrowsException() : void
177
    {
178
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
179
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
180
        }
181
182
        $this->expectException(ConnectionException::class);
183
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
184
185
        $this->connection->setNestTransactionsWithSavepoints(true);
186
    }
187
188
    public function testCreateSavepointsNotSupportedThrowsException() : void
189
    {
190
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
191
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
192
        }
193
194
        $this->expectException(ConnectionException::class);
195
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
196
197
        $this->connection->createSavepoint('foo');
198
    }
199
200
    public function testReleaseSavepointsNotSupportedThrowsException() : void
201
    {
202
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
203
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
204
        }
205
206
        $this->expectException(ConnectionException::class);
207
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
208
209
        $this->connection->releaseSavepoint('foo');
210
    }
211
212
    public function testRollbackSavepointsNotSupportedThrowsException() : void
213
    {
214
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
215
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
216
        }
217
218
        $this->expectException(ConnectionException::class);
219
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
220
221
        $this->connection->rollbackSavepoint('foo');
222
    }
223
224
    public function testTransactionBehaviorWithRollback() : void
225
    {
226
        try {
227
            $this->connection->beginTransaction();
228
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
229
230
            throw new Exception();
231
232
            $this->connection->commit(); // never reached
0 ignored issues
show
$this->connection->commit() is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
233
        } catch (Throwable $e) {
234
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
235
            $this->connection->rollBack();
236
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
237
        }
238
    }
239
240
    public function testTransactionBehaviour() : void
241
    {
242
        try {
243
            $this->connection->beginTransaction();
244
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
245
            $this->connection->commit();
246
        } catch (Throwable $e) {
247
            $this->connection->rollBack();
248
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
249
        }
250
251
        self::assertEquals(0, $this->connection->getTransactionNestingLevel());
252
    }
253
254
    public function testTransactionalWithException() : void
255
    {
256
        try {
257
            $this->connection->transactional(static function ($conn) : void {
258
                /** @var Connection $conn */
259
                $conn->executeQuery($conn->getDatabasePlatform()->getDummySelectSQL());
260
261
                throw new RuntimeException('Ooops!');
262
            });
263
            $this->fail('Expected exception');
264
        } catch (RuntimeException $expected) {
265
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
266
        }
267
    }
268
269
    public function testTransactionalWithThrowable() : void
270
    {
271
        try {
272
            $this->connection->transactional(static function ($conn) : void {
273
                /** @var Connection $conn */
274
                $conn->executeQuery($conn->getDatabasePlatform()->getDummySelectSQL());
275
276
                throw new Error('Ooops!');
277
            });
278
            $this->fail('Expected exception');
279
        } catch (Error $expected) {
280
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
281
        }
282
    }
283
284
    public function testTransactional() : void
285
    {
286
        $res = $this->connection->transactional(static function ($conn) : void {
287
            /** @var Connection $conn */
288
            $conn->executeQuery($conn->getDatabasePlatform()->getDummySelectSQL());
289
        });
290
291
        self::assertNull($res);
292
    }
293
294
    public function testTransactionalReturnValue() : void
295
    {
296
        $res = $this->connection->transactional(static function () {
297
            return 42;
298
        });
299
300
        self::assertEquals(42, $res);
301
    }
302
303
    public function testPingDoesTriggersConnect() : void
304
    {
305
        $this->connection->close();
306
        self::assertFalse($this->connection->isConnected());
307
308
        $this->connection->ping();
309
        self::assertTrue($this->connection->isConnected());
310
    }
311
312
    /**
313
     * @group DBAL-1025
314
     */
315
    public function testConnectWithoutExplicitDatabaseName() : void
316
    {
317
        if (in_array($this->connection->getDatabasePlatform()->getName(), ['oracle', 'db2'], true)) {
318
            $this->markTestSkipped('Platform does not support connecting without database name.');
319
        }
320
321
        $params = $this->connection->getParams();
322
        unset($params['dbname']);
323
324
        $connection = DriverManager::getConnection(
325
            $params,
326
            $this->connection->getConfiguration(),
327
            $this->connection->getEventManager()
328
        );
329
330
        $connection->connect();
331
332
        self::assertTrue($connection->isConnected());
333
334
        $connection->close();
335
    }
336
337
    /**
338
     * @group DBAL-990
339
     */
340
    public function testDeterminesDatabasePlatformWhenConnectingToNonExistentDatabase() : void
341
    {
342
        if (in_array($this->connection->getDatabasePlatform()->getName(), ['oracle', 'db2'], true)) {
343
            $this->markTestSkipped('Platform does not support connecting without database name.');
344
        }
345
346
        $params = $this->connection->getParams();
347
348
        $params['dbname'] = 'foo_bar';
349
350
        $connection = DriverManager::getConnection(
351
            $params,
352
            $this->connection->getConfiguration(),
353
            $this->connection->getEventManager()
354
        );
355
356
        self::assertInstanceOf(AbstractPlatform::class, $connection->getDatabasePlatform());
357
        self::assertFalse($connection->isConnected());
358
        self::assertSame($params, $connection->getParams());
359
360
        $connection->close();
361
    }
362
363
    public function testPersistentConnection() : void
364
    {
365
        $platform = $this->connection->getDatabasePlatform();
366
367
        if ($platform instanceof SqlitePlatform
368
            || $platform instanceof SQLServer2012Platform) {
369
            self::markTestSkipped('The platform does not support persistent connections');
370
        }
371
372
        $params               = TestUtil::getConnectionParams();
373
        $params['persistent'] = true;
374
375
        $connection       = DriverManager::getConnection($params);
376
        $driverConnection = $connection->getWrappedConnection();
377
378
        if (! $driverConnection instanceof PDOConnection) {
379
            self::markTestSkipped('Unable to test if the connection is persistent');
380
        }
381
382
        $pdo = $driverConnection->getWrappedConnection();
383
384
        self::assertTrue($pdo->getAttribute(PDO::ATTR_PERSISTENT));
385
    }
386
}
387