Skip to content

Commit

Permalink
improved exception messages when driver error occurs
Browse files Browse the repository at this point in the history
  • Loading branch information
schmittjoh authored and beberlei committed Jul 29, 2012
1 parent 39e0f2f commit f94e777
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 23 deletions.
9 changes: 9 additions & 0 deletions UPGRADE
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Upgrade to 2.3

## Doctrine\DBAL\Connection and Doctrine\DBAL\Statement

The query related methods including but not limited to executeQuery, exec, query, and executeUpdate
now wrap the driver exceptions such as PDOException with DBALException to add more debugging
information such as the executed SQL statement, and any bound parameters.

If you want to retrieve the driver specific exception, you can retrieve it by calling the
``getPrevious()`` method on DBALException.

## Doctrine\DBAL\Connection#setCharsetSQL() removed

This method only worked on MySQL and it is considered unsafe on MySQL to use SET NAMES UTF-8 instead
Expand Down
113 changes: 91 additions & 22 deletions lib/Doctrine/DBAL/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,12 @@ public function prepare($statement)
{
$this->connect();

$stmt = new Statement($statement, $this);
try {
$stmt = new Statement($statement, $this);
} catch (\Exception $ex) {
throw DBALException::driverExceptionDuringQuery($ex, $statement);
}

$stmt->setFetchMode($this->_defaultFetchMode);

return $stmt;
Expand Down Expand Up @@ -626,18 +631,22 @@ public function executeQuery($query, array $params = array(), $types = array(),
$logger->startQuery($query, $params, $types);
}

if ($params) {
list($query, $params, $types) = SQLParserUtils::expandListParameters($query, $params, $types);
try {
if ($params) {
list($query, $params, $types) = SQLParserUtils::expandListParameters($query, $params, $types);

$stmt = $this->_conn->prepare($query);
if ($types) {
$this->_bindTypedValues($stmt, $params, $types);
$stmt->execute();
$stmt = $this->_conn->prepare($query);
if ($types) {
$this->_bindTypedValues($stmt, $params, $types);
$stmt->execute();
} else {
$stmt->execute($params);
}
} else {
$stmt->execute($params);
$stmt = $this->_conn->query($query);
}
} else {
$stmt = $this->_conn->query($query);
} catch (\Exception $ex) {
throw DBALException::driverExceptionDuringQuery($ex, $query, $this->resolveParams($params, $types));
}

$stmt->setFetchMode($this->_defaultFetchMode);
Expand Down Expand Up @@ -729,7 +738,12 @@ public function query()
$logger->startQuery($args[0]);
}

$statement = call_user_func_array(array($this->_conn, 'query'), $args);
try {
$statement = call_user_func_array(array($this->_conn, 'query'), $args);
} catch (\Exception $ex) {
throw DBALException::driverExceptionDuringQuery($ex, func_get_arg(0));
}

$statement->setFetchMode($this->_defaultFetchMode);

if ($logger) {
Expand Down Expand Up @@ -760,19 +774,23 @@ public function executeUpdate($query, array $params = array(), array $types = ar
$logger->startQuery($query, $params, $types);
}

if ($params) {
list($query, $params, $types) = SQLParserUtils::expandListParameters($query, $params, $types);
try {
if ($params) {
list($query, $params, $types) = SQLParserUtils::expandListParameters($query, $params, $types);

$stmt = $this->_conn->prepare($query);
if ($types) {
$this->_bindTypedValues($stmt, $params, $types);
$stmt->execute();
$stmt = $this->_conn->prepare($query);
if ($types) {
$this->_bindTypedValues($stmt, $params, $types);
$stmt->execute();
} else {
$stmt->execute($params);
}
$result = $stmt->rowCount();
} else {
$stmt->execute($params);
$result = $this->_conn->exec($query);
}
$result = $stmt->rowCount();
} else {
$result = $this->_conn->exec($query);
} catch (\Exception $ex) {
throw DBALException::driverExceptionDuringQuery($ex, $query, $this->resolveParams($params, $types));
}

if ($logger) {
Expand All @@ -797,7 +815,11 @@ public function exec($statement)
$logger->startQuery($statement);
}

$result = $this->_conn->exec($statement);
try {
$result = $this->_conn->exec($statement);
} catch (\Exception $ex) {
throw DBALException::driverExceptionDuringQuery($ex, $statement);
}

if ($logger) {
$logger->stopQuery();
Expand Down Expand Up @@ -1227,6 +1249,53 @@ private function getBindingInfo($value, $type)
return array($value, $bindingType);
}

/**
* Resolves the parameters to a format which can be displayed.
*
* @internal This is a purely internal method. If you rely on this method, you are advised to
* copy/paste the code as this method may change, or be removed without prior notice.
*
* @param array $params
* @param array $types
*
* @return array
*/
public function resolveParams(array $params, array $types)
{
$resolvedParams = array();

// Check whether parameters are positional or named. Mixing is not allowed, just like in PDO.
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];
list($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];
list($value,) = $this->getBindingInfo($value, $type);
$resolvedParams[$name] = $value;
} else {
$resolvedParams[$name] = $value;
}
}
}

return $resolvedParams;
}

/**
* Create a new instance of a SQL query builder.
*
Expand Down
11 changes: 11 additions & 0 deletions lib/Doctrine/DBAL/DBALException.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ public static function unknownDriver($unknownDriverName, array $knownDrivers)
"Doctrine currently supports only the following drivers: ".implode(", ", $knownDrivers));
}

public static function driverExceptionDuringQuery(\Exception $driverEx, $sql, array $params = array())
{
$msg = "An exception occurred while executing '".$sql."'";
if ($params) {
$msg .= " with params ".json_encode($params);
}
$msg .= ":\n\n".$driverEx->getMessage();

return new self($msg, 0, $driverEx);
}

public static function invalidWrapperClass($wrapperClass)
{
return new self("The given 'wrapperClass' ".$wrapperClass." has to be a ".
Expand Down
6 changes: 5 additions & 1 deletion lib/Doctrine/DBAL/Statement.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,11 @@ public function execute($params = null)
$logger->startQuery($this->sql, $this->params, $this->types);
}

$stmt = $this->stmt->execute($params);
try {
$stmt = $this->stmt->execute($params);
} catch (\Exception $ex) {
throw DBALException::driverExceptionDuringQuery($ex, $this->sql, $this->conn->resolveParams($this->params, $this->types));
}

if ($logger) {
$logger->stopQuery();
Expand Down
29 changes: 29 additions & 0 deletions tests/Doctrine/Tests/DBAL/ConnectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,35 @@ public function testEventManagerPassedToPlatform()
$this->assertSame($this->_conn->getEventManager(), $this->_conn->getDatabasePlatform()->getEventManager());
}

/**
* @expectedException Doctrine\DBAL\DBALException
* @dataProvider getQueryMethods
*/
public function testDriverExceptionIsWrapped($method)
{
$this->setExpectedException('Doctrine\DBAL\DBALException', "An exception occurred while executing 'MUUHAAAAHAAAA':
SQLSTATE[HY000]: General error: 1 near \"MUUHAAAAHAAAA\"");

$con = \Doctrine\DBAL\DriverManager::getConnection(array(
'driver' => 'pdo_sqlite',
'memory' => true,
));

$con->$method('MUUHAAAAHAAAA');
}

public function getQueryMethods()
{
return array(
array('exec'),
array('query'),
array('executeQuery'),
array('executeUpdate'),
array('prepare'),
);
}

/**
* Pretty dumb test, however we want to check that the EchoSQLLogger correctly implements the interface.
*
Expand Down

0 comments on commit f94e777

Please sign in to comment.