Move the logic of wrapping driver exceptions to the connection

parent 14b30841
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
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. 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. 2. The `driverException()` and `driverExceptionDuringQuery()` factory methods have been removed from the `DBALException` class.
3. Non-driver exceptions (e.g. exceptions of type `Error`) are no longer wrapped in a `DBALException`.
## 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.
......
...@@ -8,10 +8,6 @@ parameters: ...@@ -8,10 +8,6 @@ parameters:
reportUnmatchedIgnoredErrors: false reportUnmatchedIgnoredErrors: false
checkMissingIterableValueType: false checkMissingIterableValueType: false
checkGenericClassInNonGenericObjectType: false checkGenericClassInNonGenericObjectType: false
earlyTerminatingMethodCalls:
Doctrine\DBAL\Connection:
- handleDriverException
- handleExceptionDuringQuery
ignoreErrors: ignoreErrors:
# removing it would be BC break # removing it would be BC break
- '~^Constructor of class Doctrine\\DBAL\\Schema\\Table has an unused parameter \$idGeneratorType\.\z~' - '~^Constructor of class Doctrine\\DBAL\\Schema\\Table has an unused parameter \$idGeneratorType\.\z~'
......
This diff is collapsed.
...@@ -232,7 +232,7 @@ class PrimaryReadReplicaConnection extends Connection ...@@ -232,7 +232,7 @@ class PrimaryReadReplicaConnection extends Connection
try { try {
return $this->_driver->connect($connectionParams); return $this->_driver->connect($connectionParams);
} catch (DriverException $e) { } catch (DriverException $e) {
throw DBALException::driverException($this->_driver, $e); throw $this->convertException($e);
} }
} }
......
...@@ -2,24 +2,14 @@ ...@@ -2,24 +2,14 @@
namespace Doctrine\DBAL; namespace Doctrine\DBAL;
use Doctrine\DBAL\Driver\Exception as TheDriverException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Types\Type;
use Exception; use Exception;
use Throwable;
use function array_map;
use function bin2hex;
use function count;
use function get_class; use function get_class;
use function gettype; use function gettype;
use function implode; use function implode;
use function is_object; use function is_object;
use function is_resource;
use function is_string;
use function json_encode;
use function preg_replace;
use function spl_object_hash; use function spl_object_hash;
use function sprintf; use function sprintf;
...@@ -133,74 +123,6 @@ class DBALException extends Exception ...@@ -133,74 +123,6 @@ class DBALException extends Exception
'Doctrine currently supports only the following drivers: ' . implode(', ', $knownDrivers)); 'Doctrine currently supports only the following drivers: ' . implode(', ', $knownDrivers));
} }
/**
* @param string $sql
* @param mixed[] $params
*
* @return self
*/
public static function driverExceptionDuringQuery(Driver $driver, Throwable $driverEx, $sql, array $params = [])
{
$msg = "An exception occurred while executing '" . $sql . "'";
if (count($params) > 0) {
$msg .= ' with params ' . self::formatParameters($params);
}
$msg .= ":\n\n" . $driverEx->getMessage();
return static::wrapException($driver, $driverEx, $msg);
}
/**
* @return self
*/
public static function driverException(Driver $driver, Throwable $driverEx)
{
return static::wrapException($driver, $driverEx, 'An exception occurred in driver: ' . $driverEx->getMessage());
}
/**
* @return self
*/
private static function wrapException(Driver $driver, Throwable $driverEx, string $msg)
{
if ($driverEx instanceof DriverException) {
return $driverEx;
}
if ($driverEx instanceof TheDriverException) {
return $driver->getExceptionConverter()->convert($msg, $driverEx);
}
return new self($msg, 0, $driverEx);
}
/**
* Returns a human-readable representation of an array of parameters.
* This properly handles binary data by returning a hex representation.
*
* @param mixed[] $params
*
* @return string
*/
private static function formatParameters(array $params)
{
return '[' . implode(', ', array_map(static function ($param): string {
if (is_resource($param)) {
return (string) $param;
}
$json = @json_encode($param);
if (! is_string($json) || $json === 'null' && is_string($param)) {
// JSON encoding failed, this is not a UTF-8 string.
return sprintf('"%s"', preg_replace('/.{2}/', '\\x$0', bin2hex($param)));
}
return $json;
}, $params)) . ']';
}
/** /**
* @param string $wrapperClass * @param string $wrapperClass
* *
......
...@@ -33,7 +33,7 @@ final class Result implements ResultInterface ...@@ -33,7 +33,7 @@ final class Result implements ResultInterface
try { try {
return $this->result->fetchNumeric(); return $this->result->fetchNumeric();
} catch (DriverException $e) { } catch (DriverException $e) {
$this->connection->handleDriverException($e); throw $this->connection->convertException($e);
} }
} }
...@@ -47,7 +47,7 @@ final class Result implements ResultInterface ...@@ -47,7 +47,7 @@ final class Result implements ResultInterface
try { try {
return $this->result->fetchAssociative(); return $this->result->fetchAssociative();
} catch (DriverException $e) { } catch (DriverException $e) {
$this->connection->handleDriverException($e); throw $this->connection->convertException($e);
} }
} }
...@@ -61,7 +61,7 @@ final class Result implements ResultInterface ...@@ -61,7 +61,7 @@ final class Result implements ResultInterface
try { try {
return $this->result->fetchOne(); return $this->result->fetchOne();
} catch (DriverException $e) { } catch (DriverException $e) {
$this->connection->handleDriverException($e); throw $this->connection->convertException($e);
} }
} }
...@@ -75,7 +75,7 @@ final class Result implements ResultInterface ...@@ -75,7 +75,7 @@ final class Result implements ResultInterface
try { try {
return $this->result->fetchAllNumeric(); return $this->result->fetchAllNumeric();
} catch (DriverException $e) { } catch (DriverException $e) {
$this->connection->handleDriverException($e); throw $this->connection->convertException($e);
} }
} }
...@@ -89,7 +89,7 @@ final class Result implements ResultInterface ...@@ -89,7 +89,7 @@ final class Result implements ResultInterface
try { try {
return $this->result->fetchAllAssociative(); return $this->result->fetchAllAssociative();
} catch (DriverException $e) { } catch (DriverException $e) {
$this->connection->handleDriverException($e); throw $this->connection->convertException($e);
} }
} }
...@@ -103,7 +103,7 @@ final class Result implements ResultInterface ...@@ -103,7 +103,7 @@ final class Result implements ResultInterface
try { try {
return $this->result->fetchFirstColumn(); return $this->result->fetchFirstColumn();
} catch (DriverException $e) { } catch (DriverException $e) {
$this->connection->handleDriverException($e); throw $this->connection->convertException($e);
} }
} }
...@@ -119,7 +119,7 @@ final class Result implements ResultInterface ...@@ -119,7 +119,7 @@ final class Result implements ResultInterface
yield $row; yield $row;
} }
} catch (DriverException $e) { } catch (DriverException $e) {
$this->connection->handleDriverException($e); throw $this->connection->convertException($e);
} }
} }
...@@ -135,7 +135,7 @@ final class Result implements ResultInterface ...@@ -135,7 +135,7 @@ final class Result implements ResultInterface
yield $row; yield $row;
} }
} catch (DriverException $e) { } catch (DriverException $e) {
$this->connection->handleDriverException($e); throw $this->connection->convertException($e);
} }
} }
...@@ -151,7 +151,7 @@ final class Result implements ResultInterface ...@@ -151,7 +151,7 @@ final class Result implements ResultInterface
yield $value; yield $value;
} }
} catch (DriverException $e) { } catch (DriverException $e) {
$this->connection->handleDriverException($e); throw $this->connection->convertException($e);
} }
} }
......
...@@ -75,7 +75,7 @@ class Statement implements DriverStatement ...@@ -75,7 +75,7 @@ class Statement implements DriverStatement
try { try {
$stmt = $driverConnection->prepare($sql); $stmt = $driverConnection->prepare($sql);
} catch (Exception $ex) { } catch (Exception $ex) {
$conn->handleExceptionDuringQuery($ex, $sql); throw $conn->convertExceptionDuringQuery($ex, $sql);
} }
$this->sql = $sql; $this->sql = $sql;
...@@ -165,7 +165,7 @@ class Statement implements DriverStatement ...@@ -165,7 +165,7 @@ class Statement implements DriverStatement
$this->conn $this->conn
); );
} catch (Exception $ex) { } catch (Exception $ex) {
$this->conn->handleExceptionDuringQuery($ex, $this->sql, $this->params, $this->types); throw $this->conn->convertExceptionDuringQuery($ex, $this->sql, $this->params, $this->types);
} finally { } finally {
if ($logger !== null) { if ($logger !== null) {
$logger->stopQuery(); $logger->stopQuery();
......
<?php
namespace Doctrine\Tests\DBAL\Connection;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\API\DefaultExceptionConverter;
use Doctrine\DBAL\Driver\Exception as DriverException;
use PHPUnit\Framework\TestCase;
use function chr;
use function fopen;
final class ExceptionHandlingTest extends TestCase
{
/** @var Connection */
private $connection;
protected function setUp(): void
{
$this->connection = new Connection([], $this->createConfiguredMock(Driver::class, [
'getExceptionConverter' => new DefaultExceptionConverter(),
]));
}
public function testDriverExceptionDuringQueryAcceptsBinaryData(): void
{
$e = $this->connection->convertExceptionDuringQuery(
$this->createMock(DriverException::class),
'',
['ABC', chr(128)]
);
self::assertStringContainsString('with params ["ABC", "\x80"]', $e->getMessage());
}
public function testDriverExceptionDuringQueryAcceptsResource(): void
{
$e = $this->connection->convertExceptionDuringQuery(
$this->createMock(DriverException::class),
'INSERT INTO file (`content`) VALUES (?)',
[
1 => fopen(__FILE__, 'r'),
]
);
self::assertStringContainsString('Resource', $e->getMessage());
}
}
...@@ -3,44 +3,13 @@ ...@@ -3,44 +3,13 @@
namespace Doctrine\DBAL\Tests; namespace Doctrine\DBAL\Tests;
use Doctrine\DBAL\DBALException; use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\Exception as InnerDriverException;
use Doctrine\DBAL\Exception\DriverException;
use Exception;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use stdClass; use stdClass;
use function chr;
use function fopen;
use function sprintf; use function sprintf;
class DBALExceptionTest extends TestCase class DBALExceptionTest extends TestCase
{ {
public function testDriverExceptionDuringQueryAcceptsBinaryData(): void
{
$driver = $this->createMock(Driver::class);
$e = DBALException::driverExceptionDuringQuery($driver, new Exception(), '', ['ABC', chr(128)]);
self::assertStringContainsString('with params ["ABC", "\x80"]', $e->getMessage());
}
public function testDriverExceptionDuringQueryAcceptsResource(): void
{
$driver = $this->createMock(Driver::class);
$e = DBALException::driverExceptionDuringQuery($driver, new Exception(), 'INSERT INTO file (`content`) VALUES (?)', [1 => fopen(__FILE__, 'r')]);
self::assertStringContainsString('Resource', $e->getMessage());
}
public function testAvoidOverWrappingOnDriverException(): void
{
$driver = $this->createMock(Driver::class);
$inner = $this->createMock(InnerDriverException::class);
$ex = new DriverException('', $inner);
$e = DBALException::driverExceptionDuringQuery($driver, $ex, '');
self::assertSame($ex, $e);
}
public function testDriverRequiredWithUrl(): void public function testDriverRequiredWithUrl(): void
{ {
$url = 'mysql://localhost'; $url = 'mysql://localhost';
......
...@@ -4,10 +4,6 @@ namespace Doctrine\DBAL\Tests\Functional; ...@@ -4,10 +4,6 @@ namespace Doctrine\DBAL\Tests\Functional;
use DateTime; use DateTime;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\IBMDB2\Driver as IBMDB2Driver;
use Doctrine\DBAL\Driver\Mysqli\Driver as MySQLiDriver;
use Doctrine\DBAL\Driver\SQLSrv\Driver as SQLSrvDriver;
use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\DBAL\Platforms\TrimMode; use Doctrine\DBAL\Platforms\TrimMode;
......
...@@ -127,10 +127,6 @@ class StatementTest extends TestCase ...@@ -127,10 +127,6 @@ class StatementTest extends TestCase
->method('getSQLLogger') ->method('getSQLLogger')
->will(self::returnValue($logger)); ->will(self::returnValue($logger));
$this->conn->expects(self::any())
->method('handleExceptionDuringQuery')
->will(self::throwException(new DBALException()));
$logger->expects(self::once()) $logger->expects(self::once())
->method('startQuery'); ->method('startQuery');
......
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