Commit 8b1a0496 authored by catch's avatar catch

Issue #1831184 by sun, chx: Fixed DatabaseExceptionWrapper completely hides *unexpected* errors.

parent c96966f0
...@@ -478,9 +478,9 @@ protected function filterComment($comment = '') { ...@@ -478,9 +478,9 @@ protected function filterComment($comment = '') {
* @param $query * @param $query
* The query to execute. In most cases this will be a string containing * The query to execute. In most cases this will be a string containing
* an SQL query with placeholders. An already-prepared instance of * an SQL query with placeholders. An already-prepared instance of
* DatabaseStatementInterface may also be passed in order to allow calling * StatementInterface may also be passed in order to allow calling
* code to manually bind variables to a query. If a * code to manually bind variables to a query. If a
* DatabaseStatementInterface is passed, the $args array will be ignored. * StatementInterface is passed, the $args array will be ignored.
* It is extremely rare that module code will need to pass a statement * It is extremely rare that module code will need to pass a statement
* object to this method. It is used primarily for database drivers for * object to this method. It is used primarily for database drivers for
* databases that require special LOB field handling. * databases that require special LOB field handling.
...@@ -492,7 +492,7 @@ protected function filterComment($comment = '') { ...@@ -492,7 +492,7 @@ protected function filterComment($comment = '') {
* An associative array of options to control how the query is run. See * An associative array of options to control how the query is run. See
* the documentation for DatabaseConnection::defaultOptions() for details. * the documentation for DatabaseConnection::defaultOptions() for details.
* *
* @return Drupal\Core\Database\StatementInterface * @return \Drupal\Core\Database\StatementInterface
* This method will return one of: the executed statement, the number of * This method will return one of: the executed statement, the number of
* rows affected by the query (not the number matched), or the generated * rows affected by the query (not the number matched), or the generated
* insert ID of the last query, depending on the value of * insert ID of the last query, depending on the value of
...@@ -502,7 +502,7 @@ protected function filterComment($comment = '') { ...@@ -502,7 +502,7 @@ protected function filterComment($comment = '') {
* $options['throw_exception'] is TRUE. * $options['throw_exception'] is TRUE.
* *
* @throws PDOException * @throws PDOException
* @throws Drupal\Core\Database\IntegrityConstraintViolationException * @throws \Drupal\Core\Database\IntegrityConstraintViolationException
*/ */
public function query($query, array $args = array(), $options = array()) { public function query($query, array $args = array(), $options = array()) {
...@@ -513,7 +513,7 @@ public function query($query, array $args = array(), $options = array()) { ...@@ -513,7 +513,7 @@ public function query($query, array $args = array(), $options = array()) {
// We allow either a pre-bound statement object or a literal string. // We allow either a pre-bound statement object or a literal string.
// In either case, we want to end up with an executed statement object, // In either case, we want to end up with an executed statement object,
// which we pass to PDOStatement::execute. // which we pass to PDOStatement::execute.
if ($query instanceof DatabaseStatementInterface) { if ($query instanceof StatementInterface) {
$stmt = $query; $stmt = $query;
$stmt->execute(NULL, $options); $stmt->execute(NULL, $options);
} }
...@@ -544,7 +544,7 @@ public function query($query, array $args = array(), $options = array()) { ...@@ -544,7 +544,7 @@ public function query($query, array $args = array(), $options = array()) {
// Wrap the exception in another exception, because PHP does not allow // Wrap the exception in another exception, because PHP does not allow
// overriding Exception::getMessage(). Its message is the extra database // overriding Exception::getMessage(). Its message is the extra database
// debug information. // debug information.
$query_string = ($query instanceof DatabaseStatementInterface) ? $stmt->getQueryString() : $query; $query_string = ($query instanceof StatementInterface) ? $stmt->getQueryString() : $query;
$message = $e->getMessage() . ": " . $query_string . "; " . print_r($args, TRUE); $message = $e->getMessage() . ": " . $query_string . "; " . print_r($args, TRUE);
// Match all SQLSTATE 23xxx errors. // Match all SQLSTATE 23xxx errors.
if (substr($e->getCode(), -6, -3) == '23') { if (substr($e->getCode(), -6, -3) == '23') {
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
use PDOStatement; use PDOStatement;
/** /**
* Default implementation of DatabaseStatementInterface. * Default implementation of StatementInterface.
* *
* PDO allows us to extend the PDOStatement class to provide additional * PDO allows us to extend the PDOStatement class to provide additional
* functionality beyond that offered by default. We do need extra * functionality beyond that offered by default. We do need extra
...@@ -32,7 +32,7 @@ class Statement extends PDOStatement implements StatementInterface { ...@@ -32,7 +32,7 @@ class Statement extends PDOStatement implements StatementInterface {
*/ */
public $dbh; public $dbh;
protected function __construct($dbh) { protected function __construct(Connection $dbh) {
$this->dbh = $dbh; $this->dbh = $dbh;
$this->setFetchMode(PDO::FETCH_OBJ); $this->setFetchMode(PDO::FETCH_OBJ);
} }
......
...@@ -30,6 +30,25 @@ ...@@ -30,6 +30,25 @@
*/ */
interface StatementInterface extends Traversable { interface StatementInterface extends Traversable {
/**
* Constructs a new PDOStatement object.
*
* The PDO manual does not document this constructor, but when overriding the
* PDOStatement class with a custom without this constructor, PDO will throw
* the internal exception/warning:
*
* "PDO::query(): SQLSTATE[HY000]: General error: user-supplied statement does
* not accept constructor arguments"
*
* PDO enforces that the access type of this constructor must be protected,
* and lastly, it also enforces that a custom PDOStatement interface (like
* this) omits the constructor (declaring it results in fatal errors
* complaining about "the access type must not be public" if it is public, and
* "the access type must be omitted" if it is protected; i.e., conflicting
* statements). The access type has to be protected.
*/
//protected function __construct(Connection $dbh);
/** /**
* Executes a prepared statement * Executes a prepared statement
* *
...@@ -113,7 +132,7 @@ public function fetchField($index = 0); ...@@ -113,7 +132,7 @@ public function fetchField($index = 0);
/** /**
* Fetches the next row and returns it as an object. * Fetches the next row and returns it as an object.
* *
* The object will be of the class specified by DatabaseStatementInterface::setFetchMode() * The object will be of the class specified by StatementInterface::setFetchMode()
* or stdClass if not specified. * or stdClass if not specified.
*/ */
// public function fetchObject(); // public function fetchObject();
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
use PDOException; use PDOException;
/** /**
* An implementation of DatabaseStatementInterface that prefetches all data. * An implementation of StatementInterface that prefetches all data.
* *
* This class behaves very similar to a PDOStatement but as it always fetches * This class behaves very similar to a PDOStatement but as it always fetches
* every row it is possible to manipulate those results. * every row it is possible to manipulate those results.
...@@ -342,7 +342,7 @@ public function valid() { ...@@ -342,7 +342,7 @@ public function valid() {
return isset($this->currentRow); return isset($this->currentRow);
} }
/* Implementations of DatabaseStatementInterface. */ /* Implementations of StatementInterface. */
public function rowCount() { public function rowCount() {
return $this->rowCount; return $this->rowCount;
......
<?php
/**
* @file
* Contains Drupal\system\Tests\Database\DatabaseExceptionWrapperTest.
*/
namespace Drupal\system\Tests\Database;
use Drupal\Core\Database\DatabaseExceptionWrapper;
use Drupal\Core\Database\Database;
use Drupal\simpletest\UnitTestBase;
/**
* Tests DatabaseExceptionWrapper thrown.
*/
class DatabaseExceptionWrapperTest extends UnitTestBase {
public static function getInfo() {
return array(
'name' => 'Database exceptiontests',
'description' => 'Tests exceptions thrown by queries.',
'group' => 'Database',
);
}
function testDatabaseExceptionWrapper() {
$connection = Database::getConnection();
$query = $connection->prepare('bananas');
try {
$connection->query($query);
$this->fail('The expected exception is not thrown.');
}
catch (DatabaseExceptionWrapper $e) {
$this->pass('The expected exception has been thrown.');
}
}
}
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