Completed
Pull Request — master (#3751)
by
unknown
64:07
created

testTypeConversionWithNumericalParams()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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