Move the exception conversion logic under a separate interface

parent 5bca2b72
# Upgrade to 3.0 # Upgrade to 3.0
## BC BREAK: Changes in driver-level exception handling
1. The `convertException()` method has been removed from the `Driver` interface. The logic of exception conversion has been moved to the `ExceptionConverter` interface. The drivers now must implement the `getExceptionConverter()` method.
2. The `driverException()` and `driverExceptionDuringQuery()` factory methods have been removed from the `DBALException` class.
## BC BREAK: More driver-level methods are allowed to throw a Driver\Exception. ## BC BREAK: More driver-level methods are allowed to throw a Driver\Exception.
The following driver-level methods are allowed to throw a Driver\Exception: The following driver-level methods are allowed to throw a Driver\Exception:
......
...@@ -169,7 +169,7 @@ class DBALException extends Exception ...@@ -169,7 +169,7 @@ class DBALException extends Exception
} }
if ($driverEx instanceof TheDriverException) { if ($driverEx instanceof TheDriverException) {
return $driver->convertException($msg, $driverEx); return $driver->getExceptionConverter()->convert($msg, $driverEx);
} }
return new self($msg, 0, $driverEx); return new self($msg, 0, $driverEx);
......
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
namespace Doctrine\DBAL; namespace Doctrine\DBAL;
use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Driver\Connection as DriverConnection; use Doctrine\DBAL\Driver\Connection as DriverConnection;
use Doctrine\DBAL\Driver\Exception; use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\AbstractSchemaManager;
...@@ -42,15 +42,7 @@ interface Driver ...@@ -42,15 +42,7 @@ interface Driver
public function getSchemaManager(Connection $conn); public function getSchemaManager(Connection $conn);
/** /**
* Converts a given driver-level exception into a DBAL-level driver exception. * Gets the ExceptionConverter that can be used to convert driver-level exceptions into DBAL exceptions.
*
* Implementors should use the vendor-specific error code and SQLSTATE of the exception
* and instantiate the most appropriate specialized {@link DriverException} subclass.
*
* @param string $message The exception message to use.
* @param Exception $exception The driver exception to convert.
*
* @return DriverException An instance of {@link DriverException} or one of its subclasses.
*/ */
public function convertException($message, Exception $exception); public function getExceptionConverter(): ExceptionConverter;
} }
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\API;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\DriverException;
final class DefaultExceptionConverter implements ExceptionConverter
{
public function convert(string $message, Exception $exception): DriverException
{
return new DriverException($message, $exception);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\API;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\DriverException;
interface ExceptionConverter
{
/**
* Converts a given driver-level exception into a DBAL-level driver exception.
*
* Implementors should use the vendor-specific error code and SQLSTATE of the exception
* and instantiate the most appropriate specialized {@link DriverException} subclass.
*
* @param string $message The exception message to use.
* @param Exception $exception The driver exception to convert.
*
* @return DriverException An instance of {@link DriverException} or one of its subclasses.
*/
public function convert(string $message, Exception $exception): DriverException;
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\API\MySQL;
use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\ConnectionException;
use Doctrine\DBAL\Exception\ConnectionLost;
use Doctrine\DBAL\Exception\DeadlockException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Doctrine\DBAL\Exception\InvalidFieldNameException;
use Doctrine\DBAL\Exception\LockWaitTimeoutException;
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;
final class ExceptionConverter implements ExceptionConverterInterface
{
/**
* @link https://dev.mysql.com/doc/refman/8.0/en/client-error-reference.html
* @link https://dev.mysql.com/doc/refman/8.0/en/server-error-reference.html
*/
public function convert(string $message, Exception $exception): DriverException
{
switch ($exception->getCode()) {
case 1213:
return new DeadlockException($message, $exception);
case 1205:
return new LockWaitTimeoutException($message, $exception);
case 1050:
return new TableExistsException($message, $exception);
case 1051:
case 1146:
return new TableNotFoundException($message, $exception);
case 1216:
case 1217:
case 1451:
case 1452:
case 1701:
return new ForeignKeyConstraintViolationException($message, $exception);
case 1062:
case 1557:
case 1569:
case 1586:
return new UniqueConstraintViolationException($message, $exception);
case 1054:
case 1166:
case 1611:
return new InvalidFieldNameException($message, $exception);
case 1052:
case 1060:
case 1110:
return new NonUniqueFieldNameException($message, $exception);
case 1064:
case 1149:
case 1287:
case 1341:
case 1342:
case 1343:
case 1344:
case 1382:
case 1479:
case 1541:
case 1554:
case 1626:
return new SyntaxErrorException($message, $exception);
case 1044:
case 1045:
case 1046:
case 1049:
case 1095:
case 1142:
case 1143:
case 1227:
case 1370:
case 1429:
case 2002:
case 2005:
return new ConnectionException($message, $exception);
case 2006:
return new ConnectionLost($message, $exception);
case 1048:
case 1121:
case 1138:
case 1171:
case 1252:
case 1263:
case 1364:
case 1566:
return new NotNullConstraintViolationException($message, $exception);
}
return new DriverException($message, $exception);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\API\OCI;
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;
final class ExceptionConverter implements ExceptionConverterInterface
{
/**
* @link http://www.dba-oracle.com/t_error_code_list.htm
*/
public function convert(string $message, Exception $exception): DriverException
{
switch ($exception->getCode()) {
case 1:
case 2299:
case 38911:
return new UniqueConstraintViolationException($message, $exception);
case 904:
return new InvalidFieldNameException($message, $exception);
case 918:
case 960:
return new NonUniqueFieldNameException($message, $exception);
case 923:
return new SyntaxErrorException($message, $exception);
case 942:
return new TableNotFoundException($message, $exception);
case 955:
return new TableExistsException($message, $exception);
case 1017:
case 12545:
return new ConnectionException($message, $exception);
case 1400:
return new NotNullConstraintViolationException($message, $exception);
case 2266:
case 2291:
case 2292:
return new ForeignKeyConstraintViolationException($message, $exception);
}
return new DriverException($message, $exception);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\API\PostgreSQL;
use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\ConnectionException;
use Doctrine\DBAL\Exception\DeadlockException;
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 function strpos;
final class ExceptionConverter implements ExceptionConverterInterface
{
/**
* @link http://www.postgresql.org/docs/9.4/static/errcodes-appendix.html
*/
public function convert(string $message, Exception $exception): DriverException
{
switch ($exception->getSQLState()) {
case '40001':
case '40P01':
return new DeadlockException($message, $exception);
case '0A000':
// Foreign key constraint violations during a TRUNCATE operation
// are considered "feature not supported" in PostgreSQL.
if (strpos($exception->getMessage(), 'truncate') !== false) {
return new ForeignKeyConstraintViolationException($message, $exception);
}
break;
case '23502':
return new NotNullConstraintViolationException($message, $exception);
case '23503':
return new ForeignKeyConstraintViolationException($message, $exception);
case '23505':
return new UniqueConstraintViolationException($message, $exception);
case '42601':
return new SyntaxErrorException($message, $exception);
case '42702':
return new NonUniqueFieldNameException($message, $exception);
case '42703':
return new InvalidFieldNameException($message, $exception);
case '42P01':
return new TableNotFoundException($message, $exception);
case '42P07':
return new TableExistsException($message, $exception);
}
// In some case (mainly connection errors) the PDO exception does not provide a SQLSTATE via its code.
// The exception code is always set to 7 here.
// We have to match against the SQLSTATE in the error message in these cases.
if ($exception->getCode() === 7 && strpos($exception->getMessage(), 'SQLSTATE[08006]') !== false) {
return new ConnectionException($message, $exception);
}
return new DriverException($message, $exception);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\API\SQLite;
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\LockWaitTimeoutException;
use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
use Doctrine\DBAL\Exception\ReadOnlyException;
use Doctrine\DBAL\Exception\SyntaxErrorException;
use Doctrine\DBAL\Exception\TableExistsException;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use function strpos;
final class ExceptionConverter implements ExceptionConverterInterface
{
/**
* @link http://www.sqlite.org/c3ref/c_abort.html
*/
public function convert(string $message, Exception $exception): DriverException
{
if (strpos($exception->getMessage(), 'database is locked') !== false) {
return new LockWaitTimeoutException($message, $exception);
}
if (
strpos($exception->getMessage(), 'must be unique') !== false ||
strpos($exception->getMessage(), 'is not unique') !== false ||
strpos($exception->getMessage(), 'are not unique') !== false ||
strpos($exception->getMessage(), 'UNIQUE constraint failed') !== false
) {
return new UniqueConstraintViolationException($message, $exception);
}
if (
strpos($exception->getMessage(), 'may not be NULL') !== false ||
strpos($exception->getMessage(), 'NOT NULL constraint failed') !== false
) {
return new NotNullConstraintViolationException($message, $exception);
}
if (strpos($exception->getMessage(), 'no such table:') !== false) {
return new TableNotFoundException($message, $exception);
}
if (strpos($exception->getMessage(), 'already exists') !== false) {
return new TableExistsException($message, $exception);
}
if (strpos($exception->getMessage(), 'has no column named') !== false) {
return new InvalidFieldNameException($message, $exception);
}
if (strpos($exception->getMessage(), 'ambiguous column name') !== false) {
return new NonUniqueFieldNameException($message, $exception);
}
if (strpos($exception->getMessage(), 'syntax error') !== false) {
return new SyntaxErrorException($message, $exception);
}
if (strpos($exception->getMessage(), 'attempt to write a readonly database') !== false) {
return new ReadOnlyException($message, $exception);
}
if (strpos($exception->getMessage(), 'unable to open database file') !== false) {
return new ConnectionException($message, $exception);
}
if (strpos($exception->getMessage(), 'FOREIGN KEY constraint failed') !== false) {
return new ForeignKeyConstraintViolationException($message, $exception);
}
return new DriverException($message, $exception);
}
}
...@@ -4,7 +4,8 @@ namespace Doctrine\DBAL\Driver; ...@@ -4,7 +4,8 @@ namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Exception\DriverException; use Doctrine\DBAL\Driver\API\DefaultExceptionConverter;
use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Platforms\DB2Platform; use Doctrine\DBAL\Platforms\DB2Platform;
use Doctrine\DBAL\Schema\DB2SchemaManager; use Doctrine\DBAL\Schema\DB2SchemaManager;
...@@ -29,13 +30,8 @@ abstract class AbstractDB2Driver implements Driver ...@@ -29,13 +30,8 @@ abstract class AbstractDB2Driver implements Driver
return new DB2SchemaManager($conn); return new DB2SchemaManager($conn);
} }
/** public function getExceptionConverter(): ExceptionConverter
* @param string $message
*
* @return DriverException
*/
public function convertException($message, Exception $exception)
{ {
return new DriverException($message, $exception); return new DefaultExceptionConverter();
} }
} }
...@@ -4,19 +4,8 @@ namespace Doctrine\DBAL\Driver; ...@@ -4,19 +4,8 @@ namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException; use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Exception\ConnectionException; use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Exception\ConnectionLost; use Doctrine\DBAL\Driver\API\MySQL;
use Doctrine\DBAL\Exception\DeadlockException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Doctrine\DBAL\Exception\InvalidFieldNameException;
use Doctrine\DBAL\Exception\LockWaitTimeoutException;
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\Platforms\MariaDb1027Platform; use Doctrine\DBAL\Platforms\MariaDb1027Platform;
use Doctrine\DBAL\Platforms\MySQL57Platform; use Doctrine\DBAL\Platforms\MySQL57Platform;
use Doctrine\DBAL\Platforms\MySQL80Platform; use Doctrine\DBAL\Platforms\MySQL80Platform;
...@@ -33,96 +22,6 @@ use function version_compare; ...@@ -33,96 +22,6 @@ use function version_compare;
*/ */
abstract class AbstractMySQLDriver implements VersionAwarePlatformDriver abstract class AbstractMySQLDriver implements VersionAwarePlatformDriver
{ {
/**
* {@inheritdoc}
*
* @link https://dev.mysql.com/doc/refman/8.0/en/client-error-reference.html
* @link https://dev.mysql.com/doc/refman/8.0/en/server-error-reference.html
*/
public function convertException($message, Exception $exception)
{
switch ($exception->getCode()) {
case 1213:
return new DeadlockException($message, $exception);
case 1205:
return new LockWaitTimeoutException($message, $exception);
case 1050:
return new TableExistsException($message, $exception);
case 1051:
case 1146:
return new TableNotFoundException($message, $exception);
case 1216:
case 1217:
case 1451:
case 1452:
case 1701:
return new ForeignKeyConstraintViolationException($message, $exception);
case 1062:
case 1557:
case 1569:
case 1586:
return new UniqueConstraintViolationException($message, $exception);
case 1054:
case 1166:
case 1611:
return new InvalidFieldNameException($message, $exception);
case 1052:
case 1060:
case 1110:
return new NonUniqueFieldNameException($message, $exception);
case 1064:
case 1149:
case 1287:
case 1341:
case 1342:
case 1343:
case 1344:
case 1382:
case 1479:
case 1541:
case 1554:
case 1626:
return new SyntaxErrorException($message, $exception);
case 1044:
case 1045:
case 1046:
case 1049:
case 1095:
case 1142:
case 1143:
case 1227:
case 1370:
case 1429:
case 2002:
case 2005:
return new ConnectionException($message, $exception);
case 2006:
return new ConnectionLost($message, $exception);
case 1048:
case 1121:
case 1138:
case 1171:
case 1252:
case 1263:
case 1364:
case 1566:
return new NotNullConstraintViolationException($message, $exception);
}
return new DriverException($message, $exception);
}
/** /**
* {@inheritdoc} * {@inheritdoc}
* *
...@@ -228,4 +127,9 @@ abstract class AbstractMySQLDriver implements VersionAwarePlatformDriver ...@@ -228,4 +127,9 @@ abstract class AbstractMySQLDriver implements VersionAwarePlatformDriver
{ {
return new MySqlSchemaManager($conn); return new MySqlSchemaManager($conn);
} }
public function getExceptionConverter(): ExceptionConverter
{
return new MySQL\ExceptionConverter();
}
} }
...@@ -5,16 +5,8 @@ namespace Doctrine\DBAL\Driver; ...@@ -5,16 +5,8 @@ namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\AbstractOracleDriver\EasyConnectString; use Doctrine\DBAL\Driver\AbstractOracleDriver\EasyConnectString;
use Doctrine\DBAL\Exception\ConnectionException; use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Exception\DriverException; use Doctrine\DBAL\Driver\API\OCI;
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\Platforms\OraclePlatform; use Doctrine\DBAL\Platforms\OraclePlatform;
use Doctrine\DBAL\Schema\OracleSchemaManager; use Doctrine\DBAL\Schema\OracleSchemaManager;
...@@ -23,49 +15,6 @@ use Doctrine\DBAL\Schema\OracleSchemaManager; ...@@ -23,49 +15,6 @@ use Doctrine\DBAL\Schema\OracleSchemaManager;
*/ */
abstract class AbstractOracleDriver implements Driver abstract class AbstractOracleDriver implements Driver
{ {
/**
* {@inheritdoc}
*/
public function convertException($message, Exception $exception)
{
switch ($exception->getCode()) {
case 1:
case 2299:
case 38911:
return new UniqueConstraintViolationException($message, $exception);
case 904:
return new InvalidFieldNameException($message, $exception);
case 918:
case 960:
return new NonUniqueFieldNameException($message, $exception);
case 923:
return new SyntaxErrorException($message, $exception);
case 942:
return new TableNotFoundException($message, $exception);
case 955:
return new TableExistsException($message, $exception);
case 1017:
case 12545:
return new ConnectionException($message, $exception);
case 1400:
return new NotNullConstraintViolationException($message, $exception);
case 2266:
case 2291:
case 2292:
return new ForeignKeyConstraintViolationException($message, $exception);
}
return new DriverException($message, $exception);
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
...@@ -82,6 +31,11 @@ abstract class AbstractOracleDriver implements Driver ...@@ -82,6 +31,11 @@ abstract class AbstractOracleDriver implements Driver
return new OracleSchemaManager($conn); return new OracleSchemaManager($conn);
} }
public function getExceptionConverter(): ExceptionConverter
{
return new OCI\ExceptionConverter();
}
/** /**
* Returns an appropriate Easy Connect String for the given parameters. * Returns an appropriate Easy Connect String for the given parameters.
* *
......
...@@ -4,24 +4,14 @@ namespace Doctrine\DBAL\Driver; ...@@ -4,24 +4,14 @@ namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException; use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Exception\ConnectionException; use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Exception\DeadlockException; use Doctrine\DBAL\Driver\API\PostgreSQL;
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\Platforms\PostgreSQL100Platform; use Doctrine\DBAL\Platforms\PostgreSQL100Platform;
use Doctrine\DBAL\Platforms\PostgreSQL94Platform; use Doctrine\DBAL\Platforms\PostgreSQL94Platform;
use Doctrine\DBAL\Schema\PostgreSqlSchemaManager; use Doctrine\DBAL\Schema\PostgreSqlSchemaManager;
use Doctrine\DBAL\VersionAwarePlatformDriver; use Doctrine\DBAL\VersionAwarePlatformDriver;
use function preg_match; use function preg_match;
use function strpos;
use function version_compare; use function version_compare;
/** /**
...@@ -29,62 +19,6 @@ use function version_compare; ...@@ -29,62 +19,6 @@ use function version_compare;
*/ */
abstract class AbstractPostgreSQLDriver implements VersionAwarePlatformDriver abstract class AbstractPostgreSQLDriver implements VersionAwarePlatformDriver
{ {
/**
* {@inheritdoc}
*
* @link http://www.postgresql.org/docs/9.4/static/errcodes-appendix.html
*/
public function convertException($message, Exception $exception)
{
switch ($exception->getSQLState()) {
case '40001':
case '40P01':
return new DeadlockException($message, $exception);
case '0A000':
// Foreign key constraint violations during a TRUNCATE operation
// are considered "feature not supported" in PostgreSQL.
if (strpos($exception->getMessage(), 'truncate') !== false) {
return new ForeignKeyConstraintViolationException($message, $exception);
}
break;
case '23502':
return new NotNullConstraintViolationException($message, $exception);
case '23503':
return new ForeignKeyConstraintViolationException($message, $exception);
case '23505':
return new UniqueConstraintViolationException($message, $exception);
case '42601':
return new SyntaxErrorException($message, $exception);
case '42702':
return new NonUniqueFieldNameException($message, $exception);
case '42703':
return new InvalidFieldNameException($message, $exception);
case '42P01':
return new TableNotFoundException($message, $exception);
case '42P07':
return new TableExistsException($message, $exception);
}
// In some case (mainly connection errors) the PDO exception does not provide a SQLSTATE via its code.
// The exception code is always set to 7 here.
// We have to match against the SQLSTATE in the error message in these cases.
if ($exception->getCode() === 7 && strpos($exception->getMessage(), 'SQLSTATE[08006]') !== false) {
return new ConnectionException($message, $exception);
}
return new DriverException($message, $exception);
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
...@@ -124,4 +58,9 @@ abstract class AbstractPostgreSQLDriver implements VersionAwarePlatformDriver ...@@ -124,4 +58,9 @@ abstract class AbstractPostgreSQLDriver implements VersionAwarePlatformDriver
{ {
return new PostgreSqlSchemaManager($conn); return new PostgreSqlSchemaManager($conn);
} }
public function getExceptionConverter(): ExceptionConverter
{
return new PostgreSQL\ExceptionConverter();
}
} }
...@@ -4,7 +4,8 @@ namespace Doctrine\DBAL\Driver; ...@@ -4,7 +4,8 @@ namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Exception\DriverException; use Doctrine\DBAL\Driver\API\DefaultExceptionConverter;
use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Platforms\SQLServer2012Platform; use Doctrine\DBAL\Platforms\SQLServer2012Platform;
use Doctrine\DBAL\Schema\SQLServerSchemaManager; use Doctrine\DBAL\Schema\SQLServerSchemaManager;
...@@ -29,13 +30,8 @@ abstract class AbstractSQLServerDriver implements Driver ...@@ -29,13 +30,8 @@ abstract class AbstractSQLServerDriver implements Driver
return new SQLServerSchemaManager($conn); return new SQLServerSchemaManager($conn);
} }
/** public function getExceptionConverter(): ExceptionConverter
* @param string $message
*
* @return DriverException
*/
public function convertException($message, Exception $exception)
{ {
return new DriverException($message, $exception); return new DefaultExceptionConverter();
} }
} }
...@@ -4,90 +4,16 @@ namespace Doctrine\DBAL\Driver; ...@@ -4,90 +4,16 @@ namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Exception\ConnectionException; use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Exception\DriverException; use Doctrine\DBAL\Driver\API\SQLite;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Doctrine\DBAL\Exception\InvalidFieldNameException;
use Doctrine\DBAL\Exception\LockWaitTimeoutException;
use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
use Doctrine\DBAL\Exception\ReadOnlyException;
use Doctrine\DBAL\Exception\SyntaxErrorException;
use Doctrine\DBAL\Exception\TableExistsException;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\DBAL\Schema\SqliteSchemaManager; use Doctrine\DBAL\Schema\SqliteSchemaManager;
use function strpos;
/** /**
* Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for SQLite based drivers. * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for SQLite based drivers.
*/ */
abstract class AbstractSQLiteDriver implements Driver abstract class AbstractSQLiteDriver implements Driver
{ {
/**
* {@inheritdoc}
*
* @link http://www.sqlite.org/c3ref/c_abort.html
*/
public function convertException($message, Exception $exception)
{
if (strpos($exception->getMessage(), 'database is locked') !== false) {
return new LockWaitTimeoutException($message, $exception);
}
if (
strpos($exception->getMessage(), 'must be unique') !== false ||
strpos($exception->getMessage(), 'is not unique') !== false ||
strpos($exception->getMessage(), 'are not unique') !== false ||
strpos($exception->getMessage(), 'UNIQUE constraint failed') !== false
) {
return new UniqueConstraintViolationException($message, $exception);
}
if (
strpos($exception->getMessage(), 'may not be NULL') !== false ||
strpos($exception->getMessage(), 'NOT NULL constraint failed') !== false
) {
return new NotNullConstraintViolationException($message, $exception);
}
if (strpos($exception->getMessage(), 'no such table:') !== false) {
return new TableNotFoundException($message, $exception);
}
if (strpos($exception->getMessage(), 'already exists') !== false) {
return new TableExistsException($message, $exception);
}
if (strpos($exception->getMessage(), 'has no column named') !== false) {
return new InvalidFieldNameException($message, $exception);
}
if (strpos($exception->getMessage(), 'ambiguous column name') !== false) {
return new NonUniqueFieldNameException($message, $exception);
}
if (strpos($exception->getMessage(), 'syntax error') !== false) {
return new SyntaxErrorException($message, $exception);
}
if (strpos($exception->getMessage(), 'attempt to write a readonly database') !== false) {
return new ReadOnlyException($message, $exception);
}
if (strpos($exception->getMessage(), 'unable to open database file') !== false) {
return new ConnectionException($message, $exception);
}
if (strpos($exception->getMessage(), 'FOREIGN KEY constraint failed') !== false) {
return new ForeignKeyConstraintViolationException($message, $exception);
}
return new DriverException($message, $exception);
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
...@@ -103,4 +29,9 @@ abstract class AbstractSQLiteDriver implements Driver ...@@ -103,4 +29,9 @@ abstract class AbstractSQLiteDriver implements Driver
{ {
return new SqliteSchemaManager($conn); return new SqliteSchemaManager($conn);
} }
public function getExceptionConverter(): ExceptionConverter
{
return new SQLite\ExceptionConverter();
}
} }
...@@ -93,7 +93,7 @@ abstract class AbstractDriverTest extends TestCase ...@@ -93,7 +93,7 @@ abstract class AbstractDriverTest extends TestCase
); );
$dbalMessage = 'DBAL exception message'; $dbalMessage = 'DBAL exception message';
$dbalException = $this->driver->convertException($dbalMessage, $driverException); $dbalException = $this->driver->getExceptionConverter()->convert($dbalMessage, $driverException);
self::assertInstanceOf($expectedClass, $dbalException); self::assertInstanceOf($expectedClass, $dbalException);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment