Skip to content

Commit

Permalink
Store query in driver exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
BenMorel authored and morozov committed Nov 1, 2020
1 parent d67f89d commit 26fe027
Show file tree
Hide file tree
Showing 15 changed files with 238 additions and 244 deletions.
18 changes: 18 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,24 @@ Please use other database client applications for import, e.g.:
* For PostgreSQL: `psql [dbname] < data.sql`.
* For SQLite: `sqlite3 /path/to/file.db < data.sql`.

## BC BREAK: Changed signature of `ExceptionConverter::convert()`

Before:

```php
public function convert(string $message, Doctrine\DBAL\Driver\Exception $exception): DriverException
```

After:

```php
public function convert(Doctrine\DBAL\Driver\Exception $exception, ?Doctrine\DBAL\Query $query): DriverException
```

## BC Break: The `DriverException` constructor is now internal

The constructor of `Doctrine\DBAL\Exception\DriverException` is now `@internal`.

# Upgrade to 2.12

## PDO signature changes with php 8
Expand Down
136 changes: 25 additions & 111 deletions src/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Driver\Connection as DriverConnection;
use Doctrine\DBAL\Driver\Exception as DriverException;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
use Doctrine\DBAL\Exception\ConnectionLost;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\InvalidArgumentException;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
Expand All @@ -24,18 +24,12 @@
use Traversable;

use function array_key_exists;
use function array_map;
use function assert;
use function bin2hex;
use function count;
use function implode;
use function is_int;
use function is_resource;
use function is_string;
use function json_encode;
use function key;
use function preg_replace;
use function sprintf;

/**
* A database abstraction-level connection that implements features like events, transaction isolation levels,
Expand Down Expand Up @@ -291,7 +285,7 @@ public function connect()

try {
$this->_conn = $this->_driver->connect($this->params);
} catch (DriverException $e) {
} catch (Driver\Exception $e) {
throw $this->convertException($e);
}

Expand Down Expand Up @@ -409,7 +403,7 @@ private function getServerVersion()
if ($connection instanceof ServerInfoAwareConnection) {
try {
return $connection->getServerVersion();
} catch (DriverException $e) {
} catch (Driver\Exception $e) {
throw $this->convertException($e);
}
}
Expand Down Expand Up @@ -481,7 +475,7 @@ public function fetchAssociative(string $query, array $params = [], array $types
{
try {
return $this->executeQuery($query, $params, $types)->fetchAssociative();
} catch (DriverException $e) {
} catch (Driver\Exception $e) {
throw $this->convertExceptionDuringQuery($e, $query, $params, $types);
}
}
Expand All @@ -502,7 +496,7 @@ public function fetchNumeric(string $query, array $params = [], array $types = [
{
try {
return $this->executeQuery($query, $params, $types)->fetchNumeric();
} catch (DriverException $e) {
} catch (Driver\Exception $e) {
throw $this->convertExceptionDuringQuery($e, $query, $params, $types);
}
}
Expand All @@ -523,7 +517,7 @@ public function fetchOne(string $query, array $params = [], array $types = [])
{
try {
return $this->executeQuery($query, $params, $types)->fetchOne();
} catch (DriverException $e) {
} catch (Driver\Exception $e) {
throw $this->convertExceptionDuringQuery($e, $query, $params, $types);
}
}
Expand Down Expand Up @@ -791,7 +785,7 @@ public function fetchAllNumeric(string $query, array $params = [], array $types
{
try {
return $this->executeQuery($query, $params, $types)->fetchAllNumeric();
} catch (DriverException $e) {
} catch (Driver\Exception $e) {
throw $this->convertExceptionDuringQuery($e, $query, $params, $types);
}
}
Expand All @@ -811,7 +805,7 @@ public function fetchAllAssociative(string $query, array $params = [], array $ty
{
try {
return $this->executeQuery($query, $params, $types)->fetchAllAssociative();
} catch (DriverException $e) {
} catch (Driver\Exception $e) {
throw $this->convertExceptionDuringQuery($e, $query, $params, $types);
}
}
Expand Down Expand Up @@ -866,7 +860,7 @@ public function fetchFirstColumn(string $query, array $params = [], array $types
{
try {
return $this->executeQuery($query, $params, $types)->fetchFirstColumn();
} catch (DriverException $e) {
} catch (Driver\Exception $e) {
throw $this->convertExceptionDuringQuery($e, $query, $params, $types);
}
}
Expand All @@ -890,7 +884,7 @@ public function iterateNumeric(string $query, array $params = [], array $types =
while (($row = $result->fetchNumeric()) !== false) {
yield $row;
}
} catch (DriverException $e) {
} catch (Driver\Exception $e) {
throw $this->convertExceptionDuringQuery($e, $query, $params, $types);
}
}
Expand All @@ -915,7 +909,7 @@ public function iterateAssociative(string $query, array $params = [], array $typ
while (($row = $result->fetchAssociative()) !== false) {
yield $row;
}
} catch (DriverException $e) {
} catch (Driver\Exception $e) {
throw $this->convertExceptionDuringQuery($e, $query, $params, $types);
}
}
Expand Down Expand Up @@ -974,7 +968,7 @@ public function iterateColumn(string $query, array $params = [], array $types =
while (($value = $result->fetchOne()) !== false) {
yield $value;
}
} catch (DriverException $e) {
} catch (Driver\Exception $e) {
throw $this->convertExceptionDuringQuery($e, $query, $params, $types);
}
}
Expand Down Expand Up @@ -1036,7 +1030,7 @@ public function executeQuery(
}

return new Result($result, $this);
} catch (DriverException $e) {
} catch (Driver\Exception $e) {
throw $this->convertExceptionDuringQuery($e, $sql, $params, $types);
} finally {
if ($logger !== null) {
Expand Down Expand Up @@ -1140,7 +1134,7 @@ public function executeStatement($sql, array $params = [], array $types = [])
}

return $connection->exec($sql);
} catch (DriverException $e) {
} catch (Driver\Exception $e) {
throw $this->convertExceptionDuringQuery($e, $sql, $params, $types);
} finally {
if ($logger !== null) {
Expand Down Expand Up @@ -1177,7 +1171,7 @@ public function lastInsertId($name = null)
{
try {
return $this->getWrappedConnection()->lastInsertId($name);
} catch (DriverException $e) {
} catch (Driver\Exception $e) {
throw $this->convertException($e);
}
}
Expand Down Expand Up @@ -1642,51 +1636,6 @@ private function getBindingInfo($value, $type)
return [$value, $bindingType];
}

/**
* Resolves the parameters to a format which can be displayed.
*
* @param array<int, mixed>|array<string, mixed> $params Query parameters
* @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
*
* @return array<int, int|string|Type|null>|array<string, int|string|Type|null>
*/
private function resolveParams(array $params, array $types): array
{
$resolvedParams = [];

// Check whether parameters are positional or named. Mixing is not allowed.
if (is_int(key($params))) {
// Positional parameters
$typeOffset = array_key_exists(0, $types) ? -1 : 0;
$bindIndex = 1;
foreach ($params as $value) {
$typeIndex = $bindIndex + $typeOffset;
if (isset($types[$typeIndex])) {
$type = $types[$typeIndex];
[$value] = $this->getBindingInfo($value, $type);
$resolvedParams[$bindIndex] = $value;
} else {
$resolvedParams[$bindIndex] = $value;
}

++$bindIndex;
}
} else {
// Named parameters
foreach ($params as $name => $value) {
if (isset($types[$name])) {
$type = $types[$name];
[$value] = $this->getBindingInfo($value, $type);
$resolvedParams[$name] = $value;
} else {
$resolvedParams[$name] = $value;
}
}
}

return $resolvedParams;
}

/**
* Creates a new instance of a SQL query builder.
*
Expand All @@ -1704,66 +1653,31 @@ public function createQueryBuilder()
* @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types
*/
final public function convertExceptionDuringQuery(
DriverException $e,
Driver\Exception $e,
string $sql,
array $params = [],
array $types = []
): Exception {
$message = "An exception occurred while executing '" . $sql . "'";

if (count($params) > 0) {
$message .= ' with params ' . $this->formatParameters(
$this->resolveParams($params, $types)
);
}

$message .= ":\n\n" . $e->getMessage();

return $this->handleDriverException($e, $message);
): DriverException {
return $this->handleDriverException($e, new Query($sql, $params, $types));
}

/**
* @internal
*/
final public function convertException(DriverException $e): Exception
final public function convertException(Driver\Exception $e): DriverException
{
return $this->handleDriverException(
$e,
'An exception occurred in driver: ' . $e->getMessage()
);
return $this->handleDriverException($e, null);
}

/**
* Returns a human-readable representation of an array of parameters.
* This properly handles binary data by returning a hex representation.
*
* @param mixed[] $params
*/
private function formatParameters(array $params): string
{
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)) . ']';
}

private function handleDriverException(DriverException $driverException, string $message): Exception
{
private function handleDriverException(
Driver\Exception $driverException,
?Query $query
): DriverException {
if ($this->exceptionConverter === null) {
$this->exceptionConverter = $this->_driver->getExceptionConverter();
}

$exception = $this->exceptionConverter->convert($message, $driverException);
$exception = $this->exceptionConverter->convert($driverException, $query);

if ($exception instanceof ConnectionLost) {
$this->close();
Expand Down
7 changes: 4 additions & 3 deletions src/Driver/API/ExceptionConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Query;

interface ExceptionConverter
{
Expand All @@ -15,10 +16,10 @@ interface ExceptionConverter
* 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.
* @param Exception $exception The driver exception to convert.
* @param Query|null $query The SQL query that triggered the exception, if any.
*
* @return DriverException An instance of {@link DriverException} or one of its subclasses.
*/
public function convert(string $message, Exception $exception): DriverException;
public function convert(Exception $exception, ?Query $query): DriverException;
}
8 changes: 6 additions & 2 deletions src/Driver/API/IBMDB2/ExceptionConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@
use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Query;

/**
* @internal
*/
final class ExceptionConverter implements ExceptionConverterInterface
{
public function convert(string $message, Exception $exception): DriverException
public function convert(Exception $exception, ?Query $query): DriverException
{
return new DriverException($message, $exception);
return new DriverException($exception, $query);
}
}

0 comments on commit 26fe027

Please sign in to comment.