ExceptionTest.php 13.4 KB
Newer Older
1
<?php
Sergei Morozov's avatar
Sergei Morozov committed
2

3 4
namespace Doctrine\Tests\DBAL\Functional;

5
use Doctrine\DBAL\Driver\ExceptionConverterDriver;
Sergei Morozov's avatar
Sergei Morozov committed
6
use Doctrine\DBAL\DriverManager;
Luís Cobucci's avatar
Luís Cobucci committed
7
use Doctrine\DBAL\Exception;
Sergei Morozov's avatar
Sergei Morozov committed
8
use Doctrine\DBAL\Schema\Schema;
9
use Doctrine\DBAL\Schema\Table;
Sergei Morozov's avatar
Sergei Morozov committed
10 11
use Doctrine\Tests\DbalFunctionalTestCase;
use Throwable;
12 13 14 15 16 17 18 19
use function array_merge;
use function chmod;
use function defined;
use function file_exists;
use function sprintf;
use function sys_get_temp_dir;
use function touch;
use function unlink;
20

Sergei Morozov's avatar
Sergei Morozov committed
21
class ExceptionTest extends DbalFunctionalTestCase
22
{
23
    protected function setUp()
24 25 26
    {
        parent::setUp();

Sergei Morozov's avatar
Sergei Morozov committed
27
        if ($this->connection->getDriver() instanceof ExceptionConverterDriver) {
Sergei Morozov's avatar
Sergei Morozov committed
28
            return;
29
        }
Sergei Morozov's avatar
Sergei Morozov committed
30 31

        $this->markTestSkipped('Driver does not support special exception handling.');
32 33
    }

34
    public function testPrimaryConstraintViolationException()
35
    {
Sergei Morozov's avatar
Sergei Morozov committed
36 37 38
        $table = new Table('duplicatekey_table');
        $table->addColumn('id', 'integer', []);
        $table->setPrimaryKey(['id']);
39

Sergei Morozov's avatar
Sergei Morozov committed
40
        $this->connection->getSchemaManager()->createTable($table);
41

Sergei Morozov's avatar
Sergei Morozov committed
42
        $this->connection->insert('duplicatekey_table', ['id' => 1]);
43

Luís Cobucci's avatar
Luís Cobucci committed
44
        $this->expectException(Exception\UniqueConstraintViolationException::class);
Sergei Morozov's avatar
Sergei Morozov committed
45
        $this->connection->insert('duplicatekey_table', ['id' => 1]);
46
    }
47

48
    public function testTableNotFoundException()
49
    {
Sergei Morozov's avatar
Sergei Morozov committed
50
        $sql = 'SELECT * FROM unknown_table';
51

Luís Cobucci's avatar
Luís Cobucci committed
52
        $this->expectException(Exception\TableNotFoundException::class);
Sergei Morozov's avatar
Sergei Morozov committed
53
        $this->connection->executeQuery($sql);
54
    }
55

56
    public function testTableExistsException()
57
    {
Sergei Morozov's avatar
Sergei Morozov committed
58
        $schemaManager = $this->connection->getSchemaManager();
Sergei Morozov's avatar
Sergei Morozov committed
59 60 61
        $table         = new Table('alreadyexist_table');
        $table->addColumn('id', 'integer', []);
        $table->setPrimaryKey(['id']);
62

Luís Cobucci's avatar
Luís Cobucci committed
63
        $this->expectException(Exception\TableExistsException::class);
64 65
        $schemaManager->createTable($table);
        $schemaManager->createTable($table);
66
    }
67

Possum's avatar
Possum committed
68
    public function testForeignKeyConstraintViolationExceptionOnInsert()
69
    {
Sergei Morozov's avatar
Sergei Morozov committed
70
        if (! $this->connection->getDatabasePlatform()->supportsForeignKeyConstraints()) {
Sergei Morozov's avatar
Sergei Morozov committed
71
            $this->markTestSkipped('Only fails on platforms with foreign key constraints.');
72 73
        }

74
        $this->setUpForeignKeyConstraintViolationExceptionTest();
75

76
        try {
Sergei Morozov's avatar
Sergei Morozov committed
77 78
            $this->connection->insert('constraint_error_table', ['id' => 1]);
            $this->connection->insert('owning_table', ['id' => 1, 'constraint_id' => 1]);
Sergei Morozov's avatar
Sergei Morozov committed
79
        } catch (Throwable $exception) {
80 81 82 83
            $this->tearDownForeignKeyConstraintViolationExceptionTest();

            throw $exception;
        }
84

Luís Cobucci's avatar
Luís Cobucci committed
85
        $this->expectException(Exception\ForeignKeyConstraintViolationException::class);
86 87

        try {
Sergei Morozov's avatar
Sergei Morozov committed
88
            $this->connection->insert('owning_table', ['id' => 2, 'constraint_id' => 2]);
Luís Cobucci's avatar
Luís Cobucci committed
89
        } catch (Exception\ForeignKeyConstraintViolationException $exception) {
90 91
            $this->tearDownForeignKeyConstraintViolationExceptionTest();

92
            throw $exception;
Sergei Morozov's avatar
Sergei Morozov committed
93
        } catch (Throwable $exception) {
94 95
            $this->tearDownForeignKeyConstraintViolationExceptionTest();

96 97
            throw $exception;
        }
98

99 100 101
        $this->tearDownForeignKeyConstraintViolationExceptionTest();
    }

Possum's avatar
Possum committed
102
    public function testForeignKeyConstraintViolationExceptionOnUpdate()
103
    {
Sergei Morozov's avatar
Sergei Morozov committed
104
        if (! $this->connection->getDatabasePlatform()->supportsForeignKeyConstraints()) {
Sergei Morozov's avatar
Sergei Morozov committed
105
            $this->markTestSkipped('Only fails on platforms with foreign key constraints.');
106 107
        }

108 109
        $this->setUpForeignKeyConstraintViolationExceptionTest();

110
        try {
Sergei Morozov's avatar
Sergei Morozov committed
111 112
            $this->connection->insert('constraint_error_table', ['id' => 1]);
            $this->connection->insert('owning_table', ['id' => 1, 'constraint_id' => 1]);
Sergei Morozov's avatar
Sergei Morozov committed
113
        } catch (Throwable $exception) {
114 115 116 117
            $this->tearDownForeignKeyConstraintViolationExceptionTest();

            throw $exception;
        }
118

Luís Cobucci's avatar
Luís Cobucci committed
119
        $this->expectException(Exception\ForeignKeyConstraintViolationException::class);
120 121

        try {
Sergei Morozov's avatar
Sergei Morozov committed
122
            $this->connection->update('constraint_error_table', ['id' => 2], ['id' => 1]);
Luís Cobucci's avatar
Luís Cobucci committed
123
        } catch (Exception\ForeignKeyConstraintViolationException $exception) {
124 125
            $this->tearDownForeignKeyConstraintViolationExceptionTest();

126
            throw $exception;
Sergei Morozov's avatar
Sergei Morozov committed
127
        } catch (Throwable $exception) {
128 129
            $this->tearDownForeignKeyConstraintViolationExceptionTest();

130 131
            throw $exception;
        }
132 133 134 135

        $this->tearDownForeignKeyConstraintViolationExceptionTest();
    }

Possum's avatar
Possum committed
136
    public function testForeignKeyConstraintViolationExceptionOnDelete()
137
    {
Sergei Morozov's avatar
Sergei Morozov committed
138
        if (! $this->connection->getDatabasePlatform()->supportsForeignKeyConstraints()) {
Sergei Morozov's avatar
Sergei Morozov committed
139
            $this->markTestSkipped('Only fails on platforms with foreign key constraints.');
140 141 142 143
        }

        $this->setUpForeignKeyConstraintViolationExceptionTest();

144
        try {
Sergei Morozov's avatar
Sergei Morozov committed
145 146
            $this->connection->insert('constraint_error_table', ['id' => 1]);
            $this->connection->insert('owning_table', ['id' => 1, 'constraint_id' => 1]);
Sergei Morozov's avatar
Sergei Morozov committed
147
        } catch (Throwable $exception) {
148 149 150 151
            $this->tearDownForeignKeyConstraintViolationExceptionTest();

            throw $exception;
        }
152

Luís Cobucci's avatar
Luís Cobucci committed
153
        $this->expectException(Exception\ForeignKeyConstraintViolationException::class);
154 155

        try {
Sergei Morozov's avatar
Sergei Morozov committed
156
            $this->connection->delete('constraint_error_table', ['id' => 1]);
Luís Cobucci's avatar
Luís Cobucci committed
157
        } catch (Exception\ForeignKeyConstraintViolationException $exception) {
158 159
            $this->tearDownForeignKeyConstraintViolationExceptionTest();

160
            throw $exception;
Sergei Morozov's avatar
Sergei Morozov committed
161
        } catch (Throwable $exception) {
162 163
            $this->tearDownForeignKeyConstraintViolationExceptionTest();

164 165
            throw $exception;
        }
166 167 168 169

        $this->tearDownForeignKeyConstraintViolationExceptionTest();
    }

Possum's avatar
Possum committed
170
    public function testForeignKeyConstraintViolationExceptionOnTruncate()
171
    {
Sergei Morozov's avatar
Sergei Morozov committed
172
        $platform = $this->connection->getDatabasePlatform();
173

Sergei Morozov's avatar
Sergei Morozov committed
174 175
        if (! $platform->supportsForeignKeyConstraints()) {
            $this->markTestSkipped('Only fails on platforms with foreign key constraints.');
176 177 178 179
        }

        $this->setUpForeignKeyConstraintViolationExceptionTest();

180
        try {
Sergei Morozov's avatar
Sergei Morozov committed
181 182
            $this->connection->insert('constraint_error_table', ['id' => 1]);
            $this->connection->insert('owning_table', ['id' => 1, 'constraint_id' => 1]);
Sergei Morozov's avatar
Sergei Morozov committed
183
        } catch (Throwable $exception) {
184 185 186 187
            $this->tearDownForeignKeyConstraintViolationExceptionTest();

            throw $exception;
        }
188

Luís Cobucci's avatar
Luís Cobucci committed
189
        $this->expectException(Exception\ForeignKeyConstraintViolationException::class);
190 191

        try {
Sergei Morozov's avatar
Sergei Morozov committed
192
            $this->connection->executeUpdate($platform->getTruncateTableSQL('constraint_error_table'));
Luís Cobucci's avatar
Luís Cobucci committed
193
        } catch (Exception\ForeignKeyConstraintViolationException $exception) {
194 195
            $this->tearDownForeignKeyConstraintViolationExceptionTest();

196
            throw $exception;
Sergei Morozov's avatar
Sergei Morozov committed
197
        } catch (Throwable $exception) {
198 199
            $this->tearDownForeignKeyConstraintViolationExceptionTest();

200 201
            throw $exception;
        }
202 203

        $this->tearDownForeignKeyConstraintViolationExceptionTest();
204
    }
205

206
    public function testNotNullConstraintViolationException()
207
    {
Sergei Morozov's avatar
Sergei Morozov committed
208
        $schema = new Schema();
209

Sergei Morozov's avatar
Sergei Morozov committed
210 211 212 213
        $table = $schema->createTable('notnull_table');
        $table->addColumn('id', 'integer', []);
        $table->addColumn('value', 'integer', ['notnull' => true]);
        $table->setPrimaryKey(['id']);
214

Sergei Morozov's avatar
Sergei Morozov committed
215 216
        foreach ($schema->toSql($this->connection->getDatabasePlatform()) as $sql) {
            $this->connection->exec($sql);
217 218
        }

Luís Cobucci's avatar
Luís Cobucci committed
219
        $this->expectException(Exception\NotNullConstraintViolationException::class);
Sergei Morozov's avatar
Sergei Morozov committed
220
        $this->connection->insert('notnull_table', ['id' => 1, 'value' => null]);
221
    }
222

223
    public function testInvalidFieldNameException()
224
    {
Sergei Morozov's avatar
Sergei Morozov committed
225
        $schema = new Schema();
226

Sergei Morozov's avatar
Sergei Morozov committed
227 228
        $table = $schema->createTable('bad_fieldname_table');
        $table->addColumn('id', 'integer', []);
229

Sergei Morozov's avatar
Sergei Morozov committed
230 231
        foreach ($schema->toSql($this->connection->getDatabasePlatform()) as $sql) {
            $this->connection->exec($sql);
232 233
        }

Luís Cobucci's avatar
Luís Cobucci committed
234
        $this->expectException(Exception\InvalidFieldNameException::class);
Sergei Morozov's avatar
Sergei Morozov committed
235
        $this->connection->insert('bad_fieldname_table', ['name' => 5]);
236 237 238 239
    }

    public function testNonUniqueFieldNameException()
    {
Sergei Morozov's avatar
Sergei Morozov committed
240
        $schema = new Schema();
241

Sergei Morozov's avatar
Sergei Morozov committed
242
        $table = $schema->createTable('ambiguous_list_table');
243 244
        $table->addColumn('id', 'integer');

Sergei Morozov's avatar
Sergei Morozov committed
245
        $table2 = $schema->createTable('ambiguous_list_table_2');
246 247
        $table2->addColumn('id', 'integer');

Sergei Morozov's avatar
Sergei Morozov committed
248 249
        foreach ($schema->toSql($this->connection->getDatabasePlatform()) as $sql) {
            $this->connection->exec($sql);
250 251 252
        }

        $sql = 'SELECT id FROM ambiguous_list_table, ambiguous_list_table_2';
Luís Cobucci's avatar
Luís Cobucci committed
253
        $this->expectException(Exception\NonUniqueFieldNameException::class);
Sergei Morozov's avatar
Sergei Morozov committed
254
        $this->connection->executeQuery($sql);
255 256
    }

257
    public function testUniqueConstraintViolationException()
258
    {
Sergei Morozov's avatar
Sergei Morozov committed
259
        $schema = new Schema();
260

Sergei Morozov's avatar
Sergei Morozov committed
261
        $table = $schema->createTable('unique_field_table');
262
        $table->addColumn('id', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
263
        $table->addUniqueIndex(['id']);
264

Sergei Morozov's avatar
Sergei Morozov committed
265 266
        foreach ($schema->toSql($this->connection->getDatabasePlatform()) as $sql) {
            $this->connection->exec($sql);
267
        }
268

Sergei Morozov's avatar
Sergei Morozov committed
269
        $this->connection->insert('unique_field_table', ['id' => 5]);
Luís Cobucci's avatar
Luís Cobucci committed
270
        $this->expectException(Exception\UniqueConstraintViolationException::class);
Sergei Morozov's avatar
Sergei Morozov committed
271
        $this->connection->insert('unique_field_table', ['id' => 5]);
272 273
    }

274 275
    public function testSyntaxErrorException()
    {
Sergei Morozov's avatar
Sergei Morozov committed
276 277 278
        $table = new Table('syntax_error_table');
        $table->addColumn('id', 'integer', []);
        $table->setPrimaryKey(['id']);
279

Sergei Morozov's avatar
Sergei Morozov committed
280
        $this->connection->getSchemaManager()->createTable($table);
281 282

        $sql = 'SELECT id FRO syntax_error_table';
Luís Cobucci's avatar
Luís Cobucci committed
283
        $this->expectException(Exception\SyntaxErrorException::class);
Sergei Morozov's avatar
Sergei Morozov committed
284
        $this->connection->executeQuery($sql);
285 286
    }

287 288 289
    /**
     * @dataProvider getSqLiteOpenConnection
     */
290
    public function testConnectionExceptionSqLite($mode, $exceptionClass)
291
    {
Sergei Morozov's avatar
Sergei Morozov committed
292
        if ($this->connection->getDatabasePlatform()->getName() !== 'sqlite') {
Sergei Morozov's avatar
Sergei Morozov committed
293
            $this->markTestSkipped('Only fails this way on sqlite');
294 295
        }

Sergei Morozov's avatar
Sergei Morozov committed
296
        $filename = sprintf('%s/%s', sys_get_temp_dir(), 'doctrine_failed_connection_' . $mode . '.db');
297 298

        if (file_exists($filename)) {
299
            chmod($filename, 0200); // make the file writable again, so it can be removed on Windows
300 301
            unlink($filename);
        }
302 303 304 305

        touch($filename);
        chmod($filename, $mode);

Sergei Morozov's avatar
Sergei Morozov committed
306
        $params = [
307 308
            'driver' => 'pdo_sqlite',
            'path'   => $filename,
Sergei Morozov's avatar
Sergei Morozov committed
309 310
        ];
        $conn   = DriverManager::getConnection($params);
311

Sergei Morozov's avatar
Sergei Morozov committed
312 313
        $schema = new Schema();
        $table  = $schema->createTable('no_connection');
314 315
        $table->addColumn('id', 'integer');

Luís Cobucci's avatar
Luís Cobucci committed
316
        $this->expectException($exceptionClass);
jeroendedauw's avatar
jeroendedauw committed
317
        foreach ($schema->toSql($conn->getDatabasePlatform()) as $sql) {
318
            $conn->exec($sql);
319
        }
320 321
    }

322 323
    public function getSqLiteOpenConnection()
    {
Sergei Morozov's avatar
Sergei Morozov committed
324
        return [
325
            // mode 0 is considered read-only on Windows
Sergei Morozov's avatar
Sergei Morozov committed
326 327 328
            [0000, defined('PHP_WINDOWS_VERSION_BUILD') ? Exception\ReadOnlyException::class : Exception\ConnectionException::class],
            [0444, Exception\ReadOnlyException::class],
        ];
329 330
    }

331 332 333
    /**
     * @dataProvider getConnectionParams
     */
334
    public function testConnectionException($params)
335
    {
Sergei Morozov's avatar
Sergei Morozov committed
336
        if ($this->connection->getDatabasePlatform()->getName() === 'sqlite') {
Sergei Morozov's avatar
Sergei Morozov committed
337
            $this->markTestSkipped('Only skipped if platform is not sqlite');
338 339
        }

Sergei Morozov's avatar
Sergei Morozov committed
340
        if ($this->connection->getDatabasePlatform()->getName() === 'drizzle') {
Sergei Morozov's avatar
Sergei Morozov committed
341
            $this->markTestSkipped('Drizzle does not always support authentication');
342 343
        }

Sergei Morozov's avatar
Sergei Morozov committed
344
        if ($this->connection->getDatabasePlatform()->getName() === 'postgresql' && isset($params['password'])) {
Sergei Morozov's avatar
Sergei Morozov committed
345
            $this->markTestSkipped('Does not work on Travis');
Benjamin Eberlei's avatar
Benjamin Eberlei committed
346 347
        }

Sergei Morozov's avatar
Sergei Morozov committed
348
        $defaultParams = $this->connection->getParams();
Sergei Morozov's avatar
Sergei Morozov committed
349
        $params        = array_merge($defaultParams, $params);
350

Sergei Morozov's avatar
Sergei Morozov committed
351
        $conn = DriverManager::getConnection($params);
352

Sergei Morozov's avatar
Sergei Morozov committed
353 354
        $schema = new Schema();
        $table  = $schema->createTable('no_connection');
355 356
        $table->addColumn('id', 'integer');

Luís Cobucci's avatar
Luís Cobucci committed
357
        $this->expectException(Exception\ConnectionException::class);
358

jeroendedauw's avatar
jeroendedauw committed
359
        foreach ($schema->toSql($conn->getDatabasePlatform()) as $sql) {
360
            $conn->exec($sql);
361
        }
362 363 364 365
    }

    public function getConnectionParams()
    {
Sergei Morozov's avatar
Sergei Morozov committed
366 367 368 369 370
        return [
            [['user' => 'not_existing']],
            [['password' => 'really_not']],
            [['host' => 'localnope']],
        ];
371
    }
372

373 374
    private function setUpForeignKeyConstraintViolationExceptionTest()
    {
Sergei Morozov's avatar
Sergei Morozov committed
375
        $schemaManager = $this->connection->getSchemaManager();
376

Sergei Morozov's avatar
Sergei Morozov committed
377 378 379
        $table = new Table('constraint_error_table');
        $table->addColumn('id', 'integer', []);
        $table->setPrimaryKey(['id']);
380

Sergei Morozov's avatar
Sergei Morozov committed
381 382 383 384 385
        $owningTable = new Table('owning_table');
        $owningTable->addColumn('id', 'integer', []);
        $owningTable->addColumn('constraint_id', 'integer', []);
        $owningTable->setPrimaryKey(['id']);
        $owningTable->addForeignKeyConstraint($table, ['constraint_id'], ['id']);
386 387 388 389 390 391 392

        $schemaManager->createTable($table);
        $schemaManager->createTable($owningTable);
    }

    private function tearDownForeignKeyConstraintViolationExceptionTest()
    {
Sergei Morozov's avatar
Sergei Morozov committed
393
        $schemaManager = $this->connection->getSchemaManager();
394 395 396 397 398

        $schemaManager->dropTable('owning_table');
        $schemaManager->dropTable('constraint_error_table');
    }
}