Completed
Pull Request — develop (#3348)
by Sergei
114:01 queued 50:53
created

ConnectionTest::testQuote()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
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() : void
39
    {
40
        self::assertInstanceOf(DriverConnection::class, $this->connection->getWrappedConnection());
41
    }
42
43
    public function testCommitWithRollbackOnlyThrowsException() : void
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() : void
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() : void
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() : void
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() : void
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() : void
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() : void
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() : void
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() : void
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() : void
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() : void
208
    {
209
        try {
210
            $this->connection->transactional(static function ($conn) : void {
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() : void
222
    {
223
        try {
224
            $this->connection->transactional(static function ($conn) : void {
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() : void
236
    {
237
        $res = $this->connection->transactional(static function ($conn) : void {
238
            /** @var Connection $conn */
239
            $conn->executeQuery($conn->getDatabasePlatform()->getDummySelectSQL());
240
        });
241
242
        self::assertNull($res);
243
    }
244
245
    public function testTransactionalReturnValue() : void
246
    {
247
        $res = $this->connection->transactional(static function () {
248
            return 42;
249
        });
250
251
        self::assertEquals(42, $res);
252
    }
253
254
    public function testPingDoesTriggersConnect() : void
255
    {
256
        $this->connection->close();
257
        self::assertFalse($this->connection->isConnected());
258
259
        $this->connection->ping();
260
        self::assertTrue($this->connection->isConnected());
261
    }
262
263
    /**
264
     * @group DBAL-1025
265
     */
266
    public function testConnectWithoutExplicitDatabaseName() : void
267
    {
268
        if (in_array($this->connection->getDatabasePlatform()->getName(), ['oracle', 'db2'], true)) {
269
            $this->markTestSkipped('Platform does not support connecting without database name.');
270
        }
271
272
        $params = $this->connection->getParams();
273
        unset($params['dbname']);
274
275
        $connection = DriverManager::getConnection(
276
            $params,
277
            $this->connection->getConfiguration(),
278
            $this->connection->getEventManager()
279
        );
280
281
        $connection->connect();
282
283
        self::assertTrue($connection->isConnected());
284
285
        $connection->close();
286
    }
287
288
    /**
289
     * @group DBAL-990
290
     */
291
    public function testDeterminesDatabasePlatformWhenConnectingToNonExistentDatabase() : void
292
    {
293
        if (in_array($this->connection->getDatabasePlatform()->getName(), ['oracle', 'db2'], true)) {
294
            $this->markTestSkipped('Platform does not support connecting without database name.');
295
        }
296
297
        $params = $this->connection->getParams();
298
299
        $params['dbname'] = 'foo_bar';
300
301
        $connection = DriverManager::getConnection(
302
            $params,
303
            $this->connection->getConfiguration(),
304
            $this->connection->getEventManager()
305
        );
306
307
        self::assertInstanceOf(AbstractPlatform::class, $connection->getDatabasePlatform());
308
        self::assertFalse($connection->isConnected());
309
        self::assertSame($params, $connection->getParams());
310
311
        $connection->close();
312
    }
313
314
    public function testPersistentConnection() : void
315
    {
316
        $platform = $this->connection->getDatabasePlatform();
317
318
        if ($platform instanceof SqlitePlatform
319
            || $platform instanceof SQLServerPlatform) {
320
            self::markTestSkipped('The platform does not support persistent connections');
321
        }
322
323
        $params               = TestUtil::getConnectionParams();
324
        $params['persistent'] = true;
325
326
        $connection       = DriverManager::getConnection($params);
327
        $driverConnection = $connection->getWrappedConnection();
328
329
        if (! $driverConnection instanceof PDOConnection) {
330
            self::markTestSkipped('Unable to test if the connection is persistent');
331
        }
332
333
        $pdo = $driverConnection->getWrappedConnection();
334
335
        self::assertTrue($pdo->getAttribute(PDO::ATTR_PERSISTENT));
336
    }
337
}
338