Skip to content

Commit

Permalink
Basic exception handling for IBM DB2
Browse files Browse the repository at this point in the history
  • Loading branch information
morozov committed Oct 24, 2021
1 parent f13897b commit 454fb8a
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 21 deletions.
44 changes: 44 additions & 0 deletions src/Driver/API/IBMDB2/ExceptionConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,60 @@

use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\ConnectionException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Doctrine\DBAL\Exception\InvalidFieldNameException;
use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
use Doctrine\DBAL\Exception\SyntaxErrorException;
use Doctrine\DBAL\Exception\TableExistsException;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\DBAL\Query;

/**
* @internal
*
* @link https://www.ibm.com/docs/en/db2/11.5?topic=messages-sql
*/
final class ExceptionConverter implements ExceptionConverterInterface
{
public function convert(Exception $exception, ?Query $query): DriverException
{
switch ($exception->getCode()) {
case -104:
return new SyntaxErrorException($exception, $query);

case -203:
return new NonUniqueFieldNameException($exception, $query);

case -204:
return new TableNotFoundException($exception, $query);

case -206:
return new InvalidFieldNameException($exception, $query);

case -407:
return new NotNullConstraintViolationException($exception, $query);

case -530:
case -531:
case -532:
case -20356:
return new ForeignKeyConstraintViolationException($exception, $query);

case -601:
return new TableExistsException($exception, $query);

case -803:
return new UniqueConstraintViolationException($exception, $query);

case -1336:
case -30082:
return new ConnectionException($exception, $query);
}

return new DriverException($exception, $query);
}
}
3 changes: 2 additions & 1 deletion src/Driver/IBMDB2/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Doctrine\DBAL\Driver\IBMDB2\Exception\ConnectionError;
use Doctrine\DBAL\Driver\IBMDB2\Exception\ConnectionFailed;
use Doctrine\DBAL\Driver\IBMDB2\Exception\PrepareFailed;
use Doctrine\DBAL\Driver\IBMDB2\Exception\StatementError;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
Expand Down Expand Up @@ -109,7 +110,7 @@ public function exec(string $sql): int
$stmt = @db2_exec($this->conn, $sql);

if ($stmt === false) {
throw ConnectionError::new($this->conn);
throw StatementError::new();
}

return db2_num_rows($stmt);
Expand Down
7 changes: 6 additions & 1 deletion src/Driver/IBMDB2/Exception/ConnectionError.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ final class ConnectionError extends AbstractException
*/
public static function new($connection): self
{
return new self(db2_conn_errormsg($connection), db2_conn_error($connection));
$message = db2_conn_errormsg($connection);
$sqlState = db2_conn_error($connection);

return Factory::create($message, static function (int $code) use ($message, $sqlState): self {
return new self($message, $sqlState, $code);
});
}
}
7 changes: 6 additions & 1 deletion src/Driver/IBMDB2/Exception/ConnectionFailed.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ final class ConnectionFailed extends AbstractException
{
public static function new(): self
{
return new self(db2_conn_errormsg(), db2_conn_error());
$message = db2_conn_errormsg();
$sqlState = db2_conn_error();

return Factory::create($message, static function (int $code) use ($message, $sqlState): self {
return new self($message, $sqlState, $code);
});
}
}
35 changes: 35 additions & 0 deletions src/Driver/IBMDB2/Exception/Factory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Doctrine\DBAL\Driver\IBMDB2\Exception;

use Doctrine\DBAL\Driver\AbstractException;

use function preg_match;

/**
* @internal
*
* @psalm-immutable
*/
final class Factory
{
/**
* @param callable(int): T $constructor
*
* @return T
*
* @template T of AbstractException
*/
public static function create(string $message, callable $constructor): AbstractException
{
$code = 0;

if (preg_match('/ SQL(\d+)N /', $message, $matches) === 1) {
$code = -(int) $matches[1];
}

return $constructor($code);
}
}
16 changes: 13 additions & 3 deletions src/Driver/IBMDB2/Exception/StatementError.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,20 @@
final class StatementError extends AbstractException
{
/**
* @param resource $statement
* @param resource|null $statement
*/
public static function new($statement): self
public static function new($statement = null): self
{
return new self(db2_stmt_errormsg($statement), db2_stmt_error($statement));
if ($statement !== null) {
$message = db2_stmt_errormsg($statement);
$sqlState = db2_stmt_error($statement);
} else {
$message = db2_stmt_errormsg();
$sqlState = db2_stmt_error();
}

return Factory::create($message, static function (int $code) use ($message, $sqlState): self {
return new self($message, $sqlState, $code);
});
}
}
2 changes: 1 addition & 1 deletion src/Driver/IBMDB2/Statement.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public function execute($params = null): ResultInterface
$this->writeStringToStream($source, $target);
}

$result = db2_execute($this->stmt, $params);
$result = @db2_execute($this->stmt, $params);

foreach ($this->lobs as [, $handle]) {
fclose($handle);
Expand Down
16 changes: 2 additions & 14 deletions tests/Functional/ExceptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace Doctrine\DBAL\Tests\Functional;

use Doctrine\DBAL\Driver\AbstractSQLServerDriver;
use Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Platforms\SqlitePlatform;
Expand Down Expand Up @@ -31,17 +30,6 @@
*/
class ExceptionTest extends FunctionalTestCase
{
protected function setUp(): void
{
$driver = $this->connection->getDriver();

if (! $driver instanceof IBMDB2\Driver) {
return;
}

self::markTestSkipped("The IBM DB2 driver currently doesn't instantiate specialized exceptions");
}

public function testPrimaryConstraintViolationException(): void
{
$table = new Table('duplicatekey_table');
Expand Down Expand Up @@ -220,15 +208,15 @@ public function testNotNullConstraintViolationException(): void

$table = $schema->createTable('notnull_table');
$table->addColumn('id', 'integer', []);
$table->addColumn('value', 'integer', ['notnull' => true]);
$table->addColumn('val', 'integer', ['notnull' => true]);
$table->setPrimaryKey(['id']);

foreach ($schema->toSql($this->connection->getDatabasePlatform()) as $sql) {
$this->connection->executeStatement($sql);
}

$this->expectException(Exception\NotNullConstraintViolationException::class);
$this->connection->insert('notnull_table', ['id' => 1, 'value' => null]);
$this->connection->insert('notnull_table', ['id' => 1, 'val' => null]);
}

public function testInvalidFieldNameException(): void
Expand Down

0 comments on commit 454fb8a

Please sign in to comment.