Commit cfdf10cf authored by webchick's avatar webchick
Browse files

Issue #2454625 by amateescu: SQLite: Fix SQLITE_SCHEMA errors in web tests

parent a7a52364
......@@ -520,7 +520,7 @@ protected function filterComment($comment = '') {
* An associative array of options to control how the query is run. See
* the documentation for DatabaseConnection::defaultOptions() for details.
*
* @return \Drupal\Core\Database\StatementInterface
* @return \Drupal\Core\Database\StatementInterface|int|null
* This method will return one of: the executed statement, the number of
* rows affected by the query (not the number matched), or the generated
* insert ID of the last query, depending on the value of
......@@ -529,12 +529,11 @@ protected function filterComment($comment = '') {
* this method will return NULL and may throw an exception if
* $options['throw_exception'] is TRUE.
*
* @throws \PDOException
* @throws \Drupal\Core\Database\DatabaseExceptionWrapper
* @throws \Drupal\Core\Database\IntegrityConstraintViolationException
* @throws \InvalidArgumentException
*/
public function query($query, array $args = array(), $options = array()) {
// Use default values if not already set.
$options += $this->defaultOptions();
......@@ -562,32 +561,61 @@ public function query($query, array $args = array(), $options = array()) {
$stmt->allowRowCount = TRUE;
return $stmt->rowCount();
case Database::RETURN_INSERT_ID:
return $this->connection->lastInsertId();
$sequence_name = isset($options['sequence_name']) ? $options['sequence_name'] : NULL;
return $this->connection->lastInsertId($sequence_name);
case Database::RETURN_NULL:
return;
return NULL;
default:
throw new \PDOException('Invalid return directive: ' . $options['return']);
}
}
catch (\PDOException $e) {
if ($options['throw_exception']) {
// Wrap the exception in another exception, because PHP does not allow
// overriding Exception::getMessage(). Its message is the extra database
// debug information.
$query_string = ($query instanceof StatementInterface) ? $stmt->getQueryString() : $query;
$message = $e->getMessage() . ": " . $query_string . "; " . print_r($args, TRUE);
// Match all SQLSTATE 23xxx errors.
if (substr($e->getCode(), -6, -3) == '23') {
$exception = new IntegrityConstraintViolationException($message, $e->getCode(), $e);
}
else {
$exception = new DatabaseExceptionWrapper($message, 0, $e);
}
// Most database drivers will return NULL here, but some of them
// (e.g. the SQLite driver) may need to re-run the query, so the return
// value will be the same as for static::query().
return $this->handleQueryException($e, $query, $args, $options);
}
}
throw $exception;
/**
* Wraps and re-throws any PDO exception thrown by static::query().
*
* @param \PDOException $e
* The exception thrown by static::query().
* @param $query
* The query executed by static::query().
* @param array $args
* An array of arguments for the prepared statement.
* @param array $options
* An associative array of options to control how the query is run.
*
* @return \Drupal\Core\Database\StatementInterface|int|null
* Most database drivers will return NULL when a PDO exception is thrown for
* a query, but some of them may need to re-run the query, so they can also
* return a \Drupal\Core\Database\StatementInterface object or an integer.
*
* @throws \Drupal\Core\Database\DatabaseExceptionWrapper
* @throws \Drupal\Core\Database\IntegrityConstraintViolationException
*/
protected function handleQueryException(\PDOException $e, $query, array $args = array(), $options = array()) {
if ($options['throw_exception']) {
// Wrap the exception in another exception, because PHP does not allow
// overriding Exception::getMessage(). Its message is the extra database
// debug information.
$query_string = ($query instanceof StatementInterface) ? $query->getQueryString() : $query;
$message = $e->getMessage() . ": " . $query_string . "; " . print_r($args, TRUE);
// Match all SQLSTATE 23xxx errors.
if (substr($e->getCode(), -6, -3) == '23') {
$exception = new IntegrityConstraintViolationException($message, $e->getCode(), $e);
}
return NULL;
else {
$exception = new DatabaseExceptionWrapper($message, 0, $e);
}
throw $exception;
}
return NULL;
}
/**
......
......@@ -102,67 +102,22 @@ public static function open(array &$connection_options = array()) {
return $pdo;
}
/**
* {@inheritdoc}
*/
public function query($query, array $args = array(), $options = array()) {
$options += $this->defaultOptions();
// The PDO PostgreSQL driver has a bug which
// doesn't type cast booleans correctly when
// parameters are bound using associative
// arrays.
// See http://bugs.php.net/bug.php?id=48383
// The PDO PostgreSQL driver has a bug which doesn't type cast booleans
// correctly when parameters are bound using associative arrays.
// @see http://bugs.php.net/bug.php?id=48383
foreach ($args as &$value) {
if (is_bool($value)) {
$value = (int) $value;
}
}
try {
if ($query instanceof StatementInterface) {
$stmt = $query;
$stmt->execute(NULL, $options);
}
else {
$this->expandArguments($query, $args);
$stmt = $this->prepareQuery($query);
$stmt->execute($args, $options);
}
switch ($options['return']) {
case Database::RETURN_STATEMENT:
return $stmt;
case Database::RETURN_AFFECTED:
$stmt->allowRowCount = TRUE;
return $stmt->rowCount();
case Database::RETURN_INSERT_ID:
return $this->connection->lastInsertId($options['sequence_name']);
case Database::RETURN_NULL:
return;
default:
throw new \PDOException('Invalid return directive: ' . $options['return']);
}
}
catch (\PDOException $e) {
if ($options['throw_exception']) {
// Match all SQLSTATE 23xxx errors.
if (substr($e->getCode(), -6, -3) == '23') {
$e = new IntegrityConstraintViolationException($e->getMessage(), $e->getCode(), $e);
}
else {
$e = new DatabaseExceptionWrapper($e->getMessage(), 0, $e);
}
// Add additional debug information.
if ($query instanceof StatementInterface) {
$e->query_string = $stmt->getQueryString();
}
else {
$e->query_string = $query;
}
$e->args = $args;
throw $e;
}
return NULL;
}
return parent::query($query, $args, $options);
}
public function prepareQuery($query) {
......
......@@ -12,7 +12,6 @@
use Drupal\Core\Database\TransactionNoActiveException;
use Drupal\Core\Database\TransactionNameNonUniqueException;
use Drupal\Core\Database\TransactionCommitFailedException;
use Drupal\Core\Database\Driver\sqlite\Statement;
use Drupal\Core\Database\Connection as DatabaseConnection;
/**
......@@ -334,6 +333,23 @@ protected function expandArguments(&$query, &$args) {
return $modified;
}
/**
* {@inheritdoc}
*/
protected function handleQueryException(\PDOException $e, $query, array $args = array(), $options = array()) {
// The database schema might be changed by another process in between the
// time that the statement was prepared and the time the statement was run
// (e.g. usually happens when running tests). In this case, we need to
// re-run the query.
// @see http://www.sqlite.org/faq.html#q15
// @see http://www.sqlite.org/rescode.html#schema
if (!empty($e->errorInfo[1]) && $e->errorInfo[1] === 17) {
return $this->query($query, $args, $options);
}
parent::handleQueryException($e, $query, $args, $options);
}
public function queryRange($query, $from, $count, array $args = array(), array $options = array()) {
return $this->query($query . ' LIMIT ' . (int) $from . ', ' . (int) $count, $args, $options);
}
......
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