Completed
Push — develop ( a4b136...55bd22 )
by Sergei
21s queued 14s
created

ConnectionTest::testPersistentConnection()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 12
dl 0
loc 22
rs 9.8666
c 0
b 0
f 0
cc 4
nc 4
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\Tests\DBAL\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\SQLServerPlatform;
15
use Doctrine\Tests\DbalFunctionalTestCase;
16
use Doctrine\Tests\TestUtil;
17
use Error;
18
use Exception;
19
use PDO;
20
use RuntimeException;
21
use Throwable;
22
use function in_array;
23
24
class ConnectionTest extends DbalFunctionalTestCase
25
{
26
    protected function setUp() : void
27
    {
28
        $this->resetSharedConn();
29
        parent::setUp();
30
    }
31
32
    protected function tearDown() : void
33
    {
34
        parent::tearDown();
35
        $this->resetSharedConn();
36
    }
37
38
    public function testGetWrappedConnection()
39
    {
40
        self::assertInstanceOf(DriverConnection::class, $this->connection->getWrappedConnection());
41
    }
42
43
    public function testCommitWithRollbackOnlyThrowsException()
44
    {
45
        $this->connection->beginTransaction();
46
        $this->connection->setRollbackOnly();
47
48
        $this->expectException(ConnectionException::class);
49
        $this->connection->commit();
50
    }
51
52
    public function testTransactionNestingBehavior()
53
    {
54
        try {
55
            $this->connection->beginTransaction();
56
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
57
58
            try {
59
                $this->connection->beginTransaction();
60
                self::assertEquals(2, $this->connection->getTransactionNestingLevel());
61
                throw new Exception();
62
                $this->connection->commit(); // never reached
0 ignored issues
show
Unused Code introduced by
$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...
63
            } catch (Throwable $e) {
64
                $this->connection->rollBack();
65
                self::assertEquals(1, $this->connection->getTransactionNestingLevel());
66
                //no rethrow
67
            }
68
            self::assertTrue($this->connection->isRollbackOnly());
69
70
            $this->connection->commit(); // should throw exception
71
            $this->fail('Transaction commit after failed nested transaction should fail.');
72
        } catch (ConnectionException $e) {
73
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
74
            $this->connection->rollBack();
75
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
76
        }
77
    }
78
79
    public function testTransactionNestingBehaviorWithSavepoints()
80
    {
81
        if (! $this->connection->getDatabasePlatform()->supportsSavepoints()) {
82
            $this->markTestSkipped('This test requires the platform to support savepoints.');
83
        }
84
85
        $this->connection->setNestTransactionsWithSavepoints(true);
86
        try {
87
            $this->connection->beginTransaction();
88
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
89
90
            try {
91
                $this->connection->beginTransaction();
92
                self::assertEquals(2, $this->connection->getTransactionNestingLevel());
93
                $this->connection->beginTransaction();
94
                self::assertEquals(3, $this->connection->getTransactionNestingLevel());
95
                $this->connection->commit();
96
                self::assertEquals(2, $this->connection->getTransactionNestingLevel());
97
                throw new Exception();
98
                $this->connection->commit(); // never reached
0 ignored issues
show
Unused Code introduced by
$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...
99
            } catch (Throwable $e) {
100
                $this->connection->rollBack();
101
                self::assertEquals(1, $this->connection->getTransactionNestingLevel());
102
                //no rethrow
103
            }
104
            self::assertFalse($this->connection->isRollbackOnly());
105
            try {
106
                $this->connection->setNestTransactionsWithSavepoints(false);
107
                $this->fail('Should not be able to disable savepoints in usage for nested transactions inside an open transaction.');
108
            } catch (ConnectionException $e) {
109
                self::assertTrue($this->connection->getNestTransactionsWithSavepoints());
110
            }
111
            $this->connection->commit(); // should not throw exception
112
        } catch (ConnectionException $e) {
113
            $this->fail('Transaction commit after failed nested transaction should not fail when using savepoints.');
114
            $this->connection->rollBack();
115
        }
116
    }
117
118
    public function testTransactionNestingBehaviorCantBeChangedInActiveTransaction()
119
    {
120
        if (! $this->connection->getDatabasePlatform()->supportsSavepoints()) {
121
            $this->markTestSkipped('This test requires the platform to support savepoints.');
122
        }
123
124
        $this->connection->beginTransaction();
125
        $this->expectException(ConnectionException::class);
126
        $this->connection->setNestTransactionsWithSavepoints(true);
127
    }
128
129
    public function testSetNestedTransactionsThroughSavepointsNotSupportedThrowsException()
130
    {
131
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
132
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
133
        }
134
135
        $this->expectException(ConnectionException::class);
136
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
137
138
        $this->connection->setNestTransactionsWithSavepoints(true);
139
    }
140
141
    public function testCreateSavepointsNotSupportedThrowsException()
142
    {
143
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
144
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
145
        }
146
147
        $this->expectException(ConnectionException::class);
148
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
149
150
        $this->connection->createSavepoint('foo');
151
    }
152
153
    public function testReleaseSavepointsNotSupportedThrowsException()
154
    {
155
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
156
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
157
        }
158
159
        $this->expectException(ConnectionException::class);
160
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
161
162
        $this->connection->releaseSavepoint('foo');
163
    }
164
165
    public function testRollbackSavepointsNotSupportedThrowsException()
166
    {
167
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
168
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
169
        }
170
171
        $this->expectException(ConnectionException::class);
172
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
173
174
        $this->connection->rollbackSavepoint('foo');
175
    }
176
177
    public function testTransactionBehaviorWithRollback()
178
    {
179
        try {
180
            $this->connection->beginTransaction();
181
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
182
183
            throw new Exception();
184
185
            $this->connection->commit(); // never reached
0 ignored issues
show
Unused Code introduced by
$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...
186
        } catch (Throwable $e) {
187
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
188
            $this->connection->rollBack();
189
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
190
        }
191
    }
192
193
    public function testTransactionBehaviour()
194
    {
195
        try {
196
            $this->connection->beginTransaction();
197
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
198
            $this->connection->commit();
199
        } catch (Throwable $e) {
200
            $this->connection->rollBack();
201
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
202
        }
203
204
        self::assertEquals(0, $this->connection->getTransactionNestingLevel());
205
    }
206
207
    public function testTransactionalWithException()
208
    {
209
        try {
210
            $this->connection->transactional(static function ($conn) {
211
                /** @var Connection $conn */
212
                $conn->executeQuery($conn->getDatabasePlatform()->getDummySelectSQL());
213
                throw new RuntimeException('Ooops!');
214
            });
215
            $this->fail('Expected exception');
216
        } catch (RuntimeException $expected) {
217
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
218
        }
219
    }
220
221
    public function testTransactionalWithThrowable()
222
    {
223
        try {
224
            $this->connection->transactional(static function ($conn) {
225
                /** @var Connection $conn */
226
                $conn->executeQuery($conn->getDatabasePlatform()->getDummySelectSQL());
227
                throw new Error('Ooops!');
228
            });
229
            $this->fail('Expected exception');
230
        } catch (Error $expected) {
231
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
232
        }
233
    }
234
235
    public function testTransactional()
236
    {
237
        $res = $this->connection->transactional(static function ($conn) {
238
            /** @var Connection $conn */
239
            $conn->executeQuery($conn->getDatabasePlatform()->getDummySelectSQL());
240
        });
241
242
        self::assertNull($res);
243
    }
244
245
    public function testTransactionalReturnValue()
246
    {
247
        $res = $this->connection->transactional(static function () {
248
            return 42;
249
        });
250
251
        self::assertEquals(42, $res);
252
    }
253
254
    /**
255
     * Tests that the quote function accepts DBAL and PDO types.
256
     */
257
    public function testQuote()
258
    {
259
        self::assertEquals(
260
            $this->connection->quote('foo'),
261
            $this->connection->quote('foo')
262
        );
263
    }
264
265
    public function testPingDoesTriggersConnect()
266
    {
267
        self::assertTrue($this->connection->ping());
268
        self::assertTrue($this->connection->isConnected());
269
    }
270
271
    /**
272
     * @group DBAL-1025
273
     */
274
    public function testConnectWithoutExplicitDatabaseName()
275
    {
276
        if (in_array($this->connection->getDatabasePlatform()->getName(), ['oracle', 'db2'], true)) {
277
            $this->markTestSkipped('Platform does not support connecting without database name.');
278
        }
279
280
        $params = $this->connection->getParams();
281
        unset($params['dbname']);
282
283
        $connection = DriverManager::getConnection(
284
            $params,
285
            $this->connection->getConfiguration(),
286
            $this->connection->getEventManager()
287
        );
288
289
        $connection->connect();
290
291
        self::assertTrue($connection->isConnected());
292
293
        $connection->close();
294
    }
295
296
    /**
297
     * @group DBAL-990
298
     */
299
    public function testDeterminesDatabasePlatformWhenConnectingToNonExistentDatabase()
300
    {
301
        if (in_array($this->connection->getDatabasePlatform()->getName(), ['oracle', 'db2'], true)) {
302
            $this->markTestSkipped('Platform does not support connecting without database name.');
303
        }
304
305
        $params = $this->connection->getParams();
306
307
        $params['dbname'] = 'foo_bar';
308
309
        $connection = DriverManager::getConnection(
310
            $params,
311
            $this->connection->getConfiguration(),
312
            $this->connection->getEventManager()
313
        );
314
315
        self::assertInstanceOf(AbstractPlatform::class, $connection->getDatabasePlatform());
316
        self::assertFalse($connection->isConnected());
317
        self::assertSame($params, $connection->getParams());
318
319
        $connection->close();
320
    }
321
322
    public function testPersistentConnection() : void
323
    {
324
        $platform = $this->connection->getDatabasePlatform();
325
326
        if ($platform instanceof SqlitePlatform
327
            || $platform instanceof SQLServerPlatform) {
328
            self::markTestSkipped('The platform does not support persistent connections');
329
        }
330
331
        $params               = TestUtil::getConnectionParams();
332
        $params['persistent'] = true;
333
334
        $connection       = DriverManager::getConnection($params);
335
        $driverConnection = $connection->getWrappedConnection();
336
337
        if (! $driverConnection instanceof PDOConnection) {
338
            self::markTestSkipped('Unable to test if the connection is persistent');
339
        }
340
341
        $pdo = $driverConnection->getWrappedConnection();
342
343
        self::assertTrue($pdo->getAttribute(PDO::ATTR_PERSISTENT));
344
    }
345
}
346