Commit 7e8d99b5 authored by catch's avatar catch
Browse files

Issue #1376778 by c960657 | andypost: Added Consistent 'duplicate key' detection in core.

parent ffa989f8
......@@ -495,13 +495,14 @@ protected function filterComment($comment = '') {
* @return Drupal\Core\Database\StatementInterface
* 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 IT of the last query, depending on the value of
* insert ID of the last query, depending on the value of
* $options['return']. Typically that value will be set by default or a
* query builder and should not be set by a user. If there is an error,
* this method will return NULL and may throw an exception if
* $options['throw_exception'] is TRUE.
*
* @throws PDOException
* @throws Drupal\Core\Database\IntegrityConstraintViolationException
*/
public function query($query, array $args = array(), $options = array()) {
......@@ -545,7 +546,13 @@ public function query($query, array $args = array(), $options = array()) {
// debug information.
$query_string = ($query instanceof DatabaseStatementInterface) ? $stmt->getQueryString() : $query;
$message = $e->getMessage() . ": " . $query_string . "; " . print_r($args, TRUE);
$exception = new DatabaseExceptionWrapper($message, 0, $e);
// 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);
}
throw $exception;
}
......
......@@ -11,6 +11,7 @@
use Drupal\Core\Database\Connection as DatabaseConnection;
use Drupal\Core\Database\DatabaseNotFoundException;
use Drupal\Core\Database\StatementInterface;
use Drupal\Core\Database\IntegrityConstraintViolationException;
use Locale;
use PDO;
......@@ -132,6 +133,10 @@ public function query($query, array $args = array(), $options = array()) {
}
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);
}
// Add additional debug information.
if ($query instanceof StatementInterface) {
$e->query_string = $stmt->getQueryString();
......
<?php
/**
* @file
* Definition of Drupal\Core\Database\IntegrityConstraintViolationException
*/
namespace Drupal\Core\Database;
use RuntimeException;
/**
* Exception thrown if a query would violate an integrity constraint.
*
* This exception is thrown e.g. when trying to insert a row that would violate
* a unique key constraint.
*/
class IntegrityConstraintViolationException extends RuntimeException implements DatabaseException { }
......@@ -9,6 +9,7 @@
use Drupal\Core\Database\Database;
use Drupal\Core\Database\Connection;
use Drupal\Core\Database\IntegrityConstraintViolationException;
use Exception;
......@@ -423,7 +424,7 @@ public function execute() {
$insert->execute();
return self::STATUS_INSERT;
}
catch (Exception $e) {
catch (IntegrityConstraintViolationException $e) {
// The insert query failed, maybe it's because a racing insert query
// beat us in inserting the same row. Retry the select query, if it
// returns a row, ignore the error and continue with the update
......
......@@ -7,7 +7,7 @@
namespace Drupal\Core\Lock;
use Drupal\Core\Database\DatabaseExceptionWrapper;
use Drupal\Core\Database\IntegrityConstraintViolationException;
/**
* Defines the database lock backend. This is the default backend in Drupal.
......@@ -53,12 +53,12 @@ public function acquire($name, $timeout = 30.0) {
// We never need to try again.
$retry = FALSE;
}
catch (DatabaseExceptionWrapper $e) {
catch (IntegrityConstraintViolationException $e) {
// Suppress the error. If this is our first pass through the loop,
// then $retry is FALSE. In this case, the insert must have failed
// meaning some other request acquired the lock but did not release it.
// We decide whether to retry by checking lock_may_be_available()
// Since this will break the lock in case it is expired.
// then $retry is FALSE. In this case, the insert failed because some
// other request acquired the lock but did not release it. We decide
// whether to retry by checking lockMayBeAvailable(). This will clear
// the offending row from the database table in case it has expired.
$retry = $retry ? FALSE : $this->lockMayBeAvailable($name);
}
// We only retry in case the first attempt failed, but we then broke
......
......@@ -7,6 +7,7 @@
namespace Drupal\system\Tests\Database;
use Drupal\Core\Database\IntegrityConstraintViolationException;
use Exception;
/**
......@@ -46,7 +47,7 @@ function testInsertDuplicateData() {
->execute();
$this->fail('Insert succeedded when it should not have.');
}
catch (Exception $e) {
catch (IntegrityConstraintViolationException $e) {
// Check if the first record was inserted.
$name = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 63))->fetchField();
......
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