ConnectionTest.php 11.1 KB
Newer Older
1 2 3 4
<?php

namespace Doctrine\Tests\DBAL\Functional;

Sergei Morozov's avatar
Sergei Morozov committed
5
use Doctrine\DBAL\Connection;
6
use Doctrine\DBAL\ConnectionException;
Sergei Morozov's avatar
Sergei Morozov committed
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\Type;
Sergei Morozov's avatar
Sergei Morozov committed
12 13 14 15 16
use Doctrine\Tests\DbalFunctionalTestCase;
use Error;
use Exception;
use RuntimeException;
use Throwable;
17
use function in_array;
18

Sergei Morozov's avatar
Sergei Morozov committed
19
class ConnectionTest extends DbalFunctionalTestCase
20
{
21
    protected function setUp()
22 23 24 25 26
    {
        $this->resetSharedConn();
        parent::setUp();
    }

27
    protected function tearDown()
28 29 30 31 32
    {
        parent::tearDown();
        $this->resetSharedConn();
    }

33 34
    public function testGetWrappedConnection()
    {
Sergei Morozov's avatar
Sergei Morozov committed
35
        self::assertInstanceOf(DriverConnection::class, $this->connection->getWrappedConnection());
36 37 38 39
    }

    public function testCommitWithRollbackOnlyThrowsException()
    {
Sergei Morozov's avatar
Sergei Morozov committed
40 41
        $this->connection->beginTransaction();
        $this->connection->setRollbackOnly();
Luís Cobucci's avatar
Luís Cobucci committed
42 43

        $this->expectException(ConnectionException::class);
Sergei Morozov's avatar
Sergei Morozov committed
44
        $this->connection->commit();
45 46
    }

47 48 49
    public function testTransactionNestingBehavior()
    {
        try {
Sergei Morozov's avatar
Sergei Morozov committed
50 51
            $this->connection->beginTransaction();
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
52

53
            try {
Sergei Morozov's avatar
Sergei Morozov committed
54 55
                $this->connection->beginTransaction();
                self::assertEquals(2, $this->connection->getTransactionNestingLevel());
Sergei Morozov's avatar
Sergei Morozov committed
56
                throw new Exception();
Sergei Morozov's avatar
Sergei Morozov committed
57
                $this->connection->commit(); // never reached
Sergei Morozov's avatar
Sergei Morozov committed
58
            } catch (Throwable $e) {
Sergei Morozov's avatar
Sergei Morozov committed
59 60
                $this->connection->rollBack();
                self::assertEquals(1, $this->connection->getTransactionNestingLevel());
61
                //no rethrow
62
            }
Sergei Morozov's avatar
Sergei Morozov committed
63
            self::assertTrue($this->connection->isRollbackOnly());
64

Sergei Morozov's avatar
Sergei Morozov committed
65
            $this->connection->commit(); // should throw exception
66
            $this->fail('Transaction commit after failed nested transaction should fail.');
67
        } catch (ConnectionException $e) {
Sergei Morozov's avatar
Sergei Morozov committed
68 69 70
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
            $this->connection->rollBack();
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
71 72
        }
    }
73 74 75

    public function testTransactionNestingBehaviorWithSavepoints()
    {
Sergei Morozov's avatar
Sergei Morozov committed
76
        if (! $this->connection->getDatabasePlatform()->supportsSavepoints()) {
77 78 79
            $this->markTestSkipped('This test requires the platform to support savepoints.');
        }

Sergei Morozov's avatar
Sergei Morozov committed
80
        $this->connection->setNestTransactionsWithSavepoints(true);
81
        try {
Sergei Morozov's avatar
Sergei Morozov committed
82 83
            $this->connection->beginTransaction();
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
84

85
            try {
Sergei Morozov's avatar
Sergei Morozov committed
86 87 88 89 90 91
                $this->connection->beginTransaction();
                self::assertEquals(2, $this->connection->getTransactionNestingLevel());
                $this->connection->beginTransaction();
                self::assertEquals(3, $this->connection->getTransactionNestingLevel());
                $this->connection->commit();
                self::assertEquals(2, $this->connection->getTransactionNestingLevel());
Sergei Morozov's avatar
Sergei Morozov committed
92
                throw new Exception();
Sergei Morozov's avatar
Sergei Morozov committed
93
                $this->connection->commit(); // never reached
Sergei Morozov's avatar
Sergei Morozov committed
94
            } catch (Throwable $e) {
Sergei Morozov's avatar
Sergei Morozov committed
95 96
                $this->connection->rollBack();
                self::assertEquals(1, $this->connection->getTransactionNestingLevel());
97 98
                //no rethrow
            }
Sergei Morozov's avatar
Sergei Morozov committed
99
            self::assertFalse($this->connection->isRollbackOnly());
100
            try {
Sergei Morozov's avatar
Sergei Morozov committed
101
                $this->connection->setNestTransactionsWithSavepoints(false);
102
                $this->fail('Should not be able to disable savepoints in usage for nested transactions inside an open transaction.');
103
            } catch (ConnectionException $e) {
Sergei Morozov's avatar
Sergei Morozov committed
104
                self::assertTrue($this->connection->getNestTransactionsWithSavepoints());
105
            }
Sergei Morozov's avatar
Sergei Morozov committed
106
            $this->connection->commit(); // should not throw exception
107 108
        } catch (ConnectionException $e) {
            $this->fail('Transaction commit after failed nested transaction should not fail when using savepoints.');
Sergei Morozov's avatar
Sergei Morozov committed
109
            $this->connection->rollBack();
110 111 112
        }
    }

113 114
    public function testTransactionNestingBehaviorCantBeChangedInActiveTransaction()
    {
Sergei Morozov's avatar
Sergei Morozov committed
115
        if (! $this->connection->getDatabasePlatform()->supportsSavepoints()) {
116 117 118
            $this->markTestSkipped('This test requires the platform to support savepoints.');
        }

Sergei Morozov's avatar
Sergei Morozov committed
119
        $this->connection->beginTransaction();
Luís Cobucci's avatar
Luís Cobucci committed
120
        $this->expectException(ConnectionException::class);
Sergei Morozov's avatar
Sergei Morozov committed
121
        $this->connection->setNestTransactionsWithSavepoints(true);
122 123 124 125
    }

    public function testSetNestedTransactionsThroughSavepointsNotSupportedThrowsException()
    {
Sergei Morozov's avatar
Sergei Morozov committed
126
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
127 128 129
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
        }

Luís Cobucci's avatar
Luís Cobucci committed
130
        $this->expectException(ConnectionException::class);
Sergei Morozov's avatar
Sergei Morozov committed
131
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
132

Sergei Morozov's avatar
Sergei Morozov committed
133
        $this->connection->setNestTransactionsWithSavepoints(true);
134 135 136 137
    }

    public function testCreateSavepointsNotSupportedThrowsException()
    {
Sergei Morozov's avatar
Sergei Morozov committed
138
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
139 140 141
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
        }

Luís Cobucci's avatar
Luís Cobucci committed
142
        $this->expectException(ConnectionException::class);
Sergei Morozov's avatar
Sergei Morozov committed
143
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
144

Sergei Morozov's avatar
Sergei Morozov committed
145
        $this->connection->createSavepoint('foo');
146 147 148 149
    }

    public function testReleaseSavepointsNotSupportedThrowsException()
    {
Sergei Morozov's avatar
Sergei Morozov committed
150
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
151 152 153
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
        }

Luís Cobucci's avatar
Luís Cobucci committed
154
        $this->expectException(ConnectionException::class);
Sergei Morozov's avatar
Sergei Morozov committed
155
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
156

Sergei Morozov's avatar
Sergei Morozov committed
157
        $this->connection->releaseSavepoint('foo');
158 159 160 161
    }

    public function testRollbackSavepointsNotSupportedThrowsException()
    {
Sergei Morozov's avatar
Sergei Morozov committed
162
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
163 164 165
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
        }

Luís Cobucci's avatar
Luís Cobucci committed
166
        $this->expectException(ConnectionException::class);
Sergei Morozov's avatar
Sergei Morozov committed
167
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
168

Sergei Morozov's avatar
Sergei Morozov committed
169
        $this->connection->rollbackSavepoint('foo');
170 171
    }

172
    public function testTransactionBehaviorWithRollback()
173 174
    {
        try {
Sergei Morozov's avatar
Sergei Morozov committed
175 176
            $this->connection->beginTransaction();
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
177

Sergei Morozov's avatar
Sergei Morozov committed
178
            throw new Exception();
179

Sergei Morozov's avatar
Sergei Morozov committed
180
            $this->connection->commit(); // never reached
Sergei Morozov's avatar
Sergei Morozov committed
181
        } catch (Throwable $e) {
Sergei Morozov's avatar
Sergei Morozov committed
182 183 184
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
            $this->connection->rollBack();
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
185
        }
186 187 188 189
    }

    public function testTransactionBehaviour()
    {
190
        try {
Sergei Morozov's avatar
Sergei Morozov committed
191 192 193
            $this->connection->beginTransaction();
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
            $this->connection->commit();
Sergei Morozov's avatar
Sergei Morozov committed
194
        } catch (Throwable $e) {
Sergei Morozov's avatar
Sergei Morozov committed
195 196
            $this->connection->rollBack();
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
197
        }
198

Sergei Morozov's avatar
Sergei Morozov committed
199
        self::assertEquals(0, $this->connection->getTransactionNestingLevel());
200 201 202 203
    }

    public function testTransactionalWithException()
    {
204
        try {
Sergei Morozov's avatar
Sergei Morozov committed
205
            $this->connection->transactional(static function ($conn) {
Sergei Morozov's avatar
Sergei Morozov committed
206
                /** @var Connection $conn */
207
                $conn->executeQuery($conn->getDatabasePlatform()->getDummySelectSQL());
Sergei Morozov's avatar
Sergei Morozov committed
208
                throw new RuntimeException('Ooops!');
209
            });
210
            $this->fail('Expected exception');
Sergei Morozov's avatar
Sergei Morozov committed
211
        } catch (RuntimeException $expected) {
Sergei Morozov's avatar
Sergei Morozov committed
212
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
213
        }
214
    }
215

216 217 218
    public function testTransactionalWithThrowable()
    {
        try {
Sergei Morozov's avatar
Sergei Morozov committed
219
            $this->connection->transactional(static function ($conn) {
Sergei Morozov's avatar
Sergei Morozov committed
220
                /** @var Connection $conn */
221
                $conn->executeQuery($conn->getDatabasePlatform()->getDummySelectSQL());
Sergei Morozov's avatar
Sergei Morozov committed
222
                throw new Error('Ooops!');
223 224
            });
            $this->fail('Expected exception');
Sergei Morozov's avatar
Sergei Morozov committed
225
        } catch (Error $expected) {
Sergei Morozov's avatar
Sergei Morozov committed
226
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
227 228 229
        }
    }

230 231
    public function testTransactional()
    {
Sergei Morozov's avatar
Sergei Morozov committed
232
        $res = $this->connection->transactional(static function ($conn) {
Sergei Morozov's avatar
Sergei Morozov committed
233
            /** @var Connection $conn */
234
            $conn->executeQuery($conn->getDatabasePlatform()->getDummySelectSQL());
235
        });
Luís Cobucci's avatar
Luís Cobucci committed
236 237

        self::assertNull($res);
238
    }
239

240 241
    public function testTransactionalReturnValue()
    {
Sergei Morozov's avatar
Sergei Morozov committed
242
        $res = $this->connection->transactional(static function () {
243 244
            return 42;
        });
Luís Cobucci's avatar
Luís Cobucci committed
245

246
        self::assertEquals(42, $res);
247 248
    }

249 250 251 252 253
    /**
     * Tests that the quote function accepts DBAL and PDO types.
     */
    public function testQuote()
    {
254
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
255 256
            $this->connection->quote('foo', Type::STRING),
            $this->connection->quote('foo', ParameterType::STRING)
257
        );
258
    }
259

260
    public function testPingDoesTriggersConnect()
261
    {
Sergei Morozov's avatar
Sergei Morozov committed
262 263
        self::assertTrue($this->connection->ping());
        self::assertTrue($this->connection->isConnected());
264
    }
265 266 267 268 269 270

    /**
     * @group DBAL-1025
     */
    public function testConnectWithoutExplicitDatabaseName()
    {
Sergei Morozov's avatar
Sergei Morozov committed
271
        if (in_array($this->connection->getDatabasePlatform()->getName(), ['oracle', 'db2'], true)) {
272 273 274
            $this->markTestSkipped('Platform does not support connecting without database name.');
        }

Sergei Morozov's avatar
Sergei Morozov committed
275
        $params = $this->connection->getParams();
276 277 278 279
        unset($params['dbname']);

        $connection = DriverManager::getConnection(
            $params,
Sergei Morozov's avatar
Sergei Morozov committed
280 281
            $this->connection->getConfiguration(),
            $this->connection->getEventManager()
282 283
        );

284
        self::assertTrue($connection->connect());
285 286

        $connection->close();
287
    }
288 289 290 291 292 293

    /**
     * @group DBAL-990
     */
    public function testDeterminesDatabasePlatformWhenConnectingToNonExistentDatabase()
    {
Sergei Morozov's avatar
Sergei Morozov committed
294
        if (in_array($this->connection->getDatabasePlatform()->getName(), ['oracle', 'db2'], true)) {
295 296 297
            $this->markTestSkipped('Platform does not support connecting without database name.');
        }

Sergei Morozov's avatar
Sergei Morozov committed
298 299
        $params = $this->connection->getParams();

300 301 302 303
        $params['dbname'] = 'foo_bar';

        $connection = DriverManager::getConnection(
            $params,
Sergei Morozov's avatar
Sergei Morozov committed
304 305
            $this->connection->getConfiguration(),
            $this->connection->getEventManager()
306 307
        );

308 309 310
        self::assertInstanceOf(AbstractPlatform::class, $connection->getDatabasePlatform());
        self::assertFalse($connection->isConnected());
        self::assertSame($params, $connection->getParams());
311 312 313

        $connection->close();
    }
314
}