Completed
Pull Request — master (#3549)
by Sergei
16:54
created

ConnectionTest::testUserProvidedPDOConnection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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