Issue #1376778 by c960657 | andypost: Added Consistent 'duplicate key' detection in core.
parent
ffa989f8a3
commit
7e8d99b5b6
|
@ -495,13 +495,14 @@ abstract class Connection extends PDO {
|
|||
* @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 @@ abstract class Connection extends PDO {
|
|||
// 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\Database;
|
|||
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 @@ class Connection extends DatabaseConnection {
|
|||
}
|
||||
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();
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<?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 @@ namespace Drupal\Core\Database\Query;
|
|||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\IntegrityConstraintViolationException;
|
||||
|
||||
use Exception;
|
||||
|
||||
|
@ -423,7 +424,7 @@ class Merge extends Query implements ConditionInterface {
|
|||
$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 @@ class DatabaseLockBackend extends LockBackendAbstract {
|
|||
// 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 @@ class InvalidDataTest extends DatabaseTestBase {
|
|||
->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();
|
||||
|
||||
|
|
Loading…
Reference in New Issue