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