Remove DriverException::getErrorCode()

parent 92ba48ce
# Upgrade to 3.0
## BC BREAK Changes in driver exceptions
1. The `Doctrine\DBAL\Driver\DriverException::getErrorCode()` method is removed. In order to obtain the driver error code, please use `::getCode()` or `::getSQLState()`.
2. The value returned by `Doctrine\DBAL\Driver\PDOException::getSQLState()` no longer falls back to the driver error code.
## BC BREAK: Changes in `OracleSchemaManager::createDatabase()`
The `$database` argument is no longer nullable or optional.
......
......@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
use Exception as BaseException;
use Throwable;
/**
* Base implementation of the {@link Exception} interface.
......@@ -15,13 +16,6 @@ use Exception as BaseException;
*/
abstract class AbstractException extends BaseException implements DriverException
{
/**
* The driver specific error code.
*
* @var int|string|null
*/
private $errorCode;
/**
* The SQLSTATE of the driver.
*
......@@ -30,24 +24,16 @@ abstract class AbstractException extends BaseException implements DriverExceptio
private $sqlState;
/**
* @param string $message The driver error message.
* @param string|null $sqlState The SQLSTATE the driver is in at the time the error occurred, if any.
* @param int|string|null $errorCode The driver specific error code if any.
* @param string $message The driver error message.
* @param string|null $sqlState The SQLSTATE the driver is in at the time the error occurred, if any.
* @param int $code The driver specific error code if any.
* @param Throwable|null $previous The previous throwable used for the exception chaining.
*/
public function __construct($message, $sqlState = null, $errorCode = null)
public function __construct($message, $sqlState = null, $code = 0, ?Throwable $previous = null)
{
parent::__construct($message);
$this->errorCode = $errorCode;
$this->sqlState = $sqlState;
}
parent::__construct($message, $code, $previous);
/**
* {@inheritdoc}
*/
public function getErrorCode()
{
return $this->errorCode;
$this->sqlState = $sqlState;
}
/**
......
......@@ -41,79 +41,79 @@ abstract class AbstractMySQLDriver implements ExceptionConverterDriver, VersionA
*/
public function convertException($message, DeprecatedDriverException $exception)
{
switch ($exception->getErrorCode()) {
case '1213':
switch ($exception->getCode()) {
case 1213:
return new DeadlockException($message, $exception);
case '1205':
case 1205:
return new LockWaitTimeoutException($message, $exception);
case '1050':
case 1050:
return new TableExistsException($message, $exception);
case '1051':
case '1146':
case 1051:
case 1146:
return new TableNotFoundException($message, $exception);
case '1216':
case '1217':
case '1451':
case '1452':
case '1701':
case 1216:
case 1217:
case 1451:
case 1452:
case 1701:
return new ForeignKeyConstraintViolationException($message, $exception);
case '1062':
case '1557':
case '1569':
case '1586':
case 1062:
case 1557:
case 1569:
case 1586:
return new UniqueConstraintViolationException($message, $exception);
case '1054':
case '1166':
case '1611':
case 1054:
case 1166:
case 1611:
return new InvalidFieldNameException($message, $exception);
case '1052':
case '1060':
case '1110':
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':
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':
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 '1048':
case '1121':
case '1138':
case '1171':
case '1252':
case '1263':
case '1364':
case '1566':
case 1048:
case 1121:
case 1138:
case 1171:
case 1252:
case 1263:
case 1364:
case 1566:
return new NotNullConstraintViolationException($message, $exception);
}
......
......@@ -29,38 +29,38 @@ abstract class AbstractOracleDriver implements Driver, ExceptionConverterDriver
*/
public function convertException($message, DeprecatedDriverException $exception)
{
switch ($exception->getErrorCode()) {
case '1':
case '2299':
case '38911':
switch ($exception->getCode()) {
case 1:
case 2299:
case 38911:
return new UniqueConstraintViolationException($message, $exception);
case '904':
case 904:
return new InvalidFieldNameException($message, $exception);
case '918':
case '960':
case 918:
case 960:
return new NonUniqueFieldNameException($message, $exception);
case '923':
case 923:
return new SyntaxErrorException($message, $exception);
case '942':
case 942:
return new TableNotFoundException($message, $exception);
case '955':
case 955:
return new TableExistsException($message, $exception);
case '1017':
case '12545':
case 1017:
case 12545:
return new ConnectionException($message, $exception);
case '1400':
case 1400:
return new NotNullConstraintViolationException($message, $exception);
case '2266':
case '2291':
case '2292':
case 2266:
case 2291:
case 2292:
return new ForeignKeyConstraintViolationException($message, $exception);
}
......
......@@ -74,16 +74,13 @@ abstract class AbstractPostgreSQLDriver implements ExceptionConverterDriver, Ver
case '42P07':
return new TableExistsException($message, $exception);
}
case '7':
// 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 (strpos($exception->getMessage(), 'SQLSTATE[08006]') !== false) {
return new ConnectionException($message, $exception);
}
break;
// 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);
......
......@@ -11,16 +11,6 @@ use Throwable;
*/
interface Exception extends Throwable
{
/**
* Returns the driver specific error code if available.
*
* Returns null if no driver specific error code is available
* for the error raised by the driver.
*
* @return int|string|null
*/
public function getErrorCode();
/**
* Returns the SQLSTATE the driver was in at the time the error occurred.
*
......
......@@ -7,48 +7,20 @@ namespace Doctrine\DBAL\Driver;
*
* @psalm-immutable
*/
class PDOException extends \PDOException implements DriverException
class PDOException extends AbstractDriverException
{
/**
* The driver specific error code.
*
* @var int|string|null
*/
private $errorCode;
/**
* The SQLSTATE of the driver.
*
* @var string|null
*/
private $sqlState;
/**
* @param \PDOException $exception The PDO exception to wrap.
*/
public function __construct(\PDOException $exception)
{
parent::__construct($exception->getMessage(), 0, $exception);
$this->code = $exception->getCode();
$this->errorInfo = $exception->errorInfo;
$this->errorCode = $exception->errorInfo[1] ?? $exception->getCode();
$this->sqlState = $exception->errorInfo[0] ?? $exception->getCode();
}
/**
* {@inheritdoc}
*/
public function getErrorCode()
{
return $this->errorCode;
}
if ($exception->errorInfo !== null) {
[$sqlState, $code] = $exception->errorInfo;
} else {
$code = $exception->getCode();
$sqlState = null;
}
/**
* {@inheritdoc}
*/
public function getSQLState()
{
return $this->sqlState;
parent::__construct($exception->getMessage(), $sqlState, $code, $exception);
}
}
......@@ -20,9 +20,9 @@ final class Error extends SQLSrvException
{
public static function new(): self
{
$message = '';
$sqlState = null;
$errorCode = null;
$message = '';
$sqlState = null;
$code = 0;
foreach ((array) sqlsrv_errors(SQLSRV_ERR_ERRORS) as $error) {
$message .= 'SQLSTATE [' . $error['SQLSTATE'] . ', ' . $error['code'] . ']: ' . $error['message'] . "\n";
......@@ -31,17 +31,17 @@ final class Error extends SQLSrvException
$sqlState = $error['SQLSTATE'];
}
if ($errorCode !== null) {
if ($code !== 0) {
continue;
}
$errorCode = $error['code'];
$code = $error['code'];
}
if ($message === '') {
$message = 'SQL Server error occurred but no error message was retrieved from driver.';
}
return new self(rtrim($message), $sqlState, $errorCode);
return new self(rtrim($message), $sqlState, $code);
}
}
......@@ -4,60 +4,33 @@ namespace Doctrine\DBAL\Exception;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\DriverException as DeprecatedDriverException;
use Exception;
use function assert;
/**
* Base class for all errors detected in the driver.
*
* @psalm-immutable
*/
class DriverException extends DBALException
class DriverException extends DBALException implements DeprecatedDriverException
{
/**
* The previous DBAL driver exception.
*
* @var DeprecatedDriverException
*/
private $driverException;
/**
* @param string $message The exception message.
* @param DeprecatedDriverException $driverException The DBAL driver exception to chain.
*/
public function __construct($message, DeprecatedDriverException $driverException)
{
$exception = null;
if ($driverException instanceof Exception) {
$exception = $driverException;
}
parent::__construct($message, 0, $exception);
$this->driverException = $driverException;
parent::__construct($message, $driverException->getCode(), $driverException);
}
/**
* Returns the driver specific error code if given.
*
* Returns null if no error code was given by the driver.
*
* @return int|string|null
*/
public function getErrorCode()
{
return $this->driverException->getErrorCode();
}
/**
* Returns the SQLSTATE the driver was in at the time the error occurred, if given.
*
* Returns null if no SQLSTATE was given by the driver.
*
* @return string|null
* {@inheritDoc}
*/
public function getSQLState()
{
return $this->driverException->getSQLState();
$previous = $this->getPrevious();
assert($previous instanceof DeprecatedDriverException);
return $previous->getSQLState();
}
}
......@@ -45,7 +45,7 @@ class OracleSchemaManager extends AbstractSchemaManager
// because of active connections on the database.
// To force dropping the database, we first have to close all active connections
// on that database and issue the drop database operation again.
if ($exception->getErrorCode() !== 1940) {
if ($exception->getCode() !== 1940) {
throw $exception;
}
......
......@@ -42,7 +42,7 @@ class SQLServerSchemaManager extends AbstractSchemaManager
// because of active connections on the database.
// To force dropping the database, we first have to close all active connections
// on that database and issue the drop database operation again.
if ($exception->getErrorCode() !== 3702) {
if ($exception->getCode() !== 3702) {
throw $exception;
}
......
......@@ -70,21 +70,21 @@ abstract class AbstractDriverTest extends TestCase
}
/**
* @param int|string $errorCode
*
* @dataProvider exceptionConversionProvider
*/
public function testConvertsException($errorCode, ?string $sqlState, ?string $message, string $expectedClass): void
{
public function testConvertsException(
string $expectedClass,
int $errorCode,
?string $sqlState = null,
string $message = ''
): void {
if (! $this->driver instanceof ExceptionConverterDriver) {
self::markTestSkipped('This test is only intended for exception converter drivers.');
}
$driverException = $this->getMockBuilder(DriverExceptionInterface::class)
->setConstructorArgs([$message])
->setConstructorArgs([$message, $errorCode])
->getMock();
$driverException->method('getErrorCode')
->willReturn($errorCode);
$driverException->method('getSQLState')
->willReturn($sqlState);
......@@ -93,7 +93,7 @@ abstract class AbstractDriverTest extends TestCase
self::assertInstanceOf($expectedClass, $dbalException);
self::assertSame($driverException->getErrorCode(), $dbalException->getErrorCode());
self::assertSame($driverException->getCode(), $dbalException->getCode());
self::assertSame($driverException->getSQLState(), $dbalException->getSQLState());
self::assertSame($driverException, $dbalException->getPrevious());
self::assertSame($dbalMessage, $dbalException->getMessage());
......@@ -206,11 +206,11 @@ abstract class AbstractDriverTest extends TestCase
{
foreach (static::getExceptionConversionData() as $expectedClass => $items) {
foreach ($items as $item) {
yield array_merge($item, [$expectedClass]);
yield array_merge([$expectedClass], $item);
}
}
yield ['foo', 'bar', 'baz', self::EXCEPTION_DRIVER];
yield [self::EXCEPTION_DRIVER, 1, 'HY000', 'The message'];
}
/**
......
......@@ -63,76 +63,76 @@ class AbstractMySQLDriverTest extends AbstractDriverTest
{
return [
self::EXCEPTION_CONNECTION => [
['1044', null, null],
['1045', null, null],
['1046', null, null],
['1049', null, null],
['1095', null, null],
['1142', null, null],
['1143', null, null],
['1227', null, null],
['1370', null, null],
['2002', null, null],
['2005', null, null],
[1044],
[1045],
[1046],
[1049],
[1095],
[1142],
[1143],
[1227],
[1370],
[2002],
[2005],
],
self::EXCEPTION_FOREIGN_KEY_CONSTRAINT_VIOLATION => [
['1216', null, null],
['1217', null, null],
['1451', null, null],
['1452', null, null],
[1216],
[1217],
[1451],
[1452],
],
self::EXCEPTION_INVALID_FIELD_NAME => [
['1054', null, null],
['1166', null, null],
['1611', null, null],
[1054],
[1166],
[1611],
],
self::EXCEPTION_NON_UNIQUE_FIELD_NAME => [
['1052', null, null],
['1060', null, null],
['1110', null, null],
[1052],
[1060],
[1110],
],
self::EXCEPTION_NOT_NULL_CONSTRAINT_VIOLATION => [
['1048', null, null],
['1121', null, null],
['1138', null, null],
['1171', null, null],
['1252', null, null],
['1263', null, null],
['1364', null, null],
['1566', null, null],
[1048],
[1121],
[1138],
[1171],
[1252],
[1263],
[1364],
[1566],
],
self::EXCEPTION_SYNTAX_ERROR => [
['1064', null, null],
['1149', null, null],
['1287', null, null],
['1341', null, null],
['1342', null, null],
['1343', null, null],
['1344', null, null],
['1382', null, null],
['1479', null, null],
['1541', null, null],
['1554', null, null],
['1626', null, null],
[1064],
[1149],
[1287],
[1341],
[1342],
[1343],
[1344],
[1382],
[1479],
[1541],
[1554],
[1626],
],
self::EXCEPTION_TABLE_EXISTS => [
['1050', null, null],
[1050],
],
self::EXCEPTION_TABLE_NOT_FOUND => [
['1051', null, null],
['1146', null, null],
[1051],
[1146],
],
self::EXCEPTION_UNIQUE_CONSTRAINT_VIOLATION => [
['1062', null, null],
['1557', null, null],
['1569', null, null],
['1586', null, null],
[1062],
[1557],
[1569],
[1586],
],
self::EXCEPTION_DEADLOCK => [
['1213', null, null],
[1213],
],
self::EXCEPTION_LOCK_WAIT_TIMEOUT => [
['1205', null, null],
[1205],
],
];
}
......
......@@ -34,35 +34,35 @@ class AbstractOracleDriverTest extends AbstractDriverTest
{
return [
self::EXCEPTION_CONNECTION => [
['1017', null, null],
['12545', null, null],
[1017],
[12545],
],
self::EXCEPTION_FOREIGN_KEY_CONSTRAINT_VIOLATION => [
['2292', null, null],
[2292],
],
self::EXCEPTION_INVALID_FIELD_NAME => [
['904', null, null],
[904],
],
self::EXCEPTION_NON_UNIQUE_FIELD_NAME => [
['918', null, null],
['960', null, null],
[918],
[960],
],
self::EXCEPTION_NOT_NULL_CONSTRAINT_VIOLATION => [
['1400', null, null],
[1400],
],
self::EXCEPTION_SYNTAX_ERROR => [
['923', null, null],
[923],
],
self::EXCEPTION_TABLE_EXISTS => [
['955', null, null],
[955],
],
self::EXCEPTION_TABLE_NOT_FOUND => [
['942', null, null],
[942],
],
self::EXCEPTION_UNIQUE_CONSTRAINT_VIOLATION => [
['1', null, null],
['2299', null, null],
['38911', null, null],
[1],
[2299],
[38911],
],
];
}
......
......@@ -48,35 +48,35 @@ class AbstractPostgreSQLDriverTest extends AbstractDriverTest
{
return [
self::EXCEPTION_CONNECTION => [
[null, '7', 'SQLSTATE[08006]'],
[7, null, 'SQLSTATE[08006]'],
],
self::EXCEPTION_FOREIGN_KEY_CONSTRAINT_VIOLATION => [
[null, '23503', null],
[0, '23503'],
],
self::EXCEPTION_INVALID_FIELD_NAME => [
[null, '42703', null],
[0, '42703'],
],
self::EXCEPTION_NON_UNIQUE_FIELD_NAME => [
[null, '42702', null],
[0, '42702'],
],
self::EXCEPTION_NOT_NULL_CONSTRAINT_VIOLATION => [
[null, '23502', null],
[0, '23502'],
],
self::EXCEPTION_SYNTAX_ERROR => [
[null, '42601', null],
[0, '42601'],
],
self::EXCEPTION_TABLE_EXISTS => [
[null, '42P07', null],
[0, '42P07'],
],
self::EXCEPTION_TABLE_NOT_FOUND => [
[null, '42P01', null],
[0, '42P01'],
],
self::EXCEPTION_UNIQUE_CONSTRAINT_VIOLATION => [
[null, '23505', null],
[0, '23505'],
],
self::EXCEPTION_DEADLOCK => [
[null, '40001', null],
[null, '40P01', null],
[0, '40001'],
[0, '40P01'],
],
];
}
......
......@@ -34,36 +34,36 @@ class AbstractSQLiteDriverTest extends AbstractDriverTest
{
return [
self::EXCEPTION_CONNECTION => [
[null, null, 'unable to open database file'],
[0, null, 'unable to open database file'],
],
self::EXCEPTION_INVALID_FIELD_NAME => [
[null, null, 'has no column named'],
[0, null, 'has no column named'],
],
self::EXCEPTION_NON_UNIQUE_FIELD_NAME => [
[null, null, 'ambiguous column name'],
[0, null, 'ambiguous column name'],
],
self::EXCEPTION_NOT_NULL_CONSTRAINT_VIOLATION => [
[null, null, 'may not be NULL'],
[0, null, 'may not be NULL'],
],
self::EXCEPTION_READ_ONLY => [
[null, null, 'attempt to write a readonly database'],
[0, null, 'attempt to write a readonly database'],
],
self::EXCEPTION_SYNTAX_ERROR => [
[null, null, 'syntax error'],
[0, null, 'syntax error'],
],
self::EXCEPTION_TABLE_EXISTS => [
[null, null, 'already exists'],
[0, null, 'already exists'],
],
self::EXCEPTION_TABLE_NOT_FOUND => [
[null, null, 'no such table:'],
[0, null, 'no such table:'],
],
self::EXCEPTION_UNIQUE_CONSTRAINT_VIOLATION => [
[null, null, 'must be unique'],
[null, null, 'is not unique'],
[null, null, 'are not unique'],
[0, null, 'must be unique'],
[0, null, 'is not unique'],
[0, null, 'are not unique'],
],
self::EXCEPTION_LOCK_WAIT_TIMEOUT => [
[null, null, 'database is locked'],
[0, null, 'database is locked'],
],
];
}
......
......@@ -16,7 +16,7 @@ class ExceptionTest extends TestCase
public const MESSAGE = 'PDO Exception';
public const SQLSTATE = 28000;
public const SQLSTATE = 'HY000';
/**
* The PDO exception wrapper under test.
......@@ -36,7 +36,7 @@ class ExceptionTest extends TestCase
{
parent::setUp();
$this->wrappedException = new PDOException(self::MESSAGE, self::SQLSTATE);
$this->wrappedException = new PDOException(self::MESSAGE);
$this->wrappedException->errorInfo = [self::SQLSTATE, self::ERROR_CODE];
......@@ -45,12 +45,7 @@ class ExceptionTest extends TestCase
public function testReturnsCode(): void
{
self::assertSame(self::SQLSTATE, $this->exception->getCode());
}
public function testReturnsErrorCode(): void
{
self::assertSame(self::ERROR_CODE, $this->exception->getErrorCode());
self::assertSame(self::ERROR_CODE, $this->exception->getCode());
}
public function testReturnsMessage(): void
......
......@@ -350,7 +350,7 @@ class WriteTest extends FunctionalTestCase
try {
return $this->connection->lastInsertId($name);
} catch (Exception $e) {
if ($e->getCode() === 'IM001') {
if ($e->getSQLState() === 'IM001') {
self::markTestSkipped($e->getMessage());
}
......
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