From bacf782e30ea898b84873b4047cd63caaaad8763 Mon Sep 17 00:00:00 2001 From: catch Date: Fri, 7 Aug 2020 14:46:22 +0100 Subject: [PATCH] Issue #2999569 by mondrake, Hardik_Patel_12, ravi.shankar, vsujeetkumar, mrinalini9, daffie: MySql driver throws a generic DatabaseExceptionWrapper for integrity violation at insertion when column has no default --- .../Core/Database/Driver/mysql/Connection.php | 19 +++++++++++++++ .../KernelTests/Core/Database/InsertTest.php | 24 +++++++++++++++++++ .../Entity/EntityDefinitionUpdateTest.php | 21 ++++++---------- 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php b/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php index 4bcda15a07d0..2894517fd4a0 100644 --- a/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php +++ b/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php @@ -3,6 +3,7 @@ namespace Drupal\Core\Database\Driver\mysql; use Drupal\Core\Database\DatabaseAccessDeniedException; +use Drupal\Core\Database\IntegrityConstraintViolationException; use Drupal\Core\Database\DatabaseExceptionWrapper; use Drupal\Core\Database\Database; @@ -87,6 +88,24 @@ class Connection extends DatabaseConnection { } } + /** + * {@inheritdoc} + */ + protected function handleQueryException(\PDOException $e, $query, array $args = [], $options = []) { + // In case of attempted INSERT of a record with an undefined column and no + // default value indicated in schema, MySql returns a 1364 error code. + // Throw an IntegrityConstraintViolationException here like the other + // drivers do, to avoid the parent class to throw a generic + // DatabaseExceptionWrapper instead. + if (!empty($e->errorInfo[1]) && $e->errorInfo[1] === 1364) { + $query_string = ($query instanceof StatementInterface) ? $query->getQueryString() : $query; + $message = $e->getMessage() . ": " . $query_string . "; " . print_r($args, TRUE); + throw new IntegrityConstraintViolationException($message, is_int($e->getCode()) ? $e->getCode() : 0, $e); + } + + parent::handleQueryException($e, $query, $args, $options); + } + /** * {@inheritdoc} */ diff --git a/core/tests/Drupal/KernelTests/Core/Database/InsertTest.php b/core/tests/Drupal/KernelTests/Core/Database/InsertTest.php index ea66ed47a7fe..1c18d5fadf22 100644 --- a/core/tests/Drupal/KernelTests/Core/Database/InsertTest.php +++ b/core/tests/Drupal/KernelTests/Core/Database/InsertTest.php @@ -2,6 +2,8 @@ namespace Drupal\KernelTests\Core\Database; +use Drupal\Core\Database\IntegrityConstraintViolationException; + /** * Tests the insert builder. * @@ -208,4 +210,26 @@ class InsertTest extends DatabaseTestBase { $this->assertEquals('Update value 2', $saved_value); } + /** + * Tests insertion integrity violation with no default value for a column. + */ + public function testInsertIntegrityViolation() { + // Remove the default from the 'age' column, so that inserting a record + // without its value specified will lead to integrity failure. + $this->connection->schema()->changeField('test', 'age', 'age', [ + 'description' => "The person's age", + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ]); + + // Try inserting a record that misses the value for the 'age' column, + // should raise an IntegrityConstraintViolationException. + $this->expectException(IntegrityConstraintViolationException::class); + $this->connection->insert('test') + ->fields(['name']) + ->values(['name' => 'Elvis']) + ->execute(); + } + } diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php index 400037adf0fb..234c256e4859 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php @@ -4,7 +4,6 @@ namespace Drupal\KernelTests\Core\Entity; use Drupal\Component\Plugin\Exception\PluginNotFoundException; use Drupal\Core\Database\Database; -use Drupal\Core\Database\DatabaseExceptionWrapper; use Drupal\Core\Database\IntegrityConstraintViolationException; use Drupal\Core\Entity\ContentEntityType; use Drupal\Core\Entity\EntityStorageException; @@ -482,19 +481,13 @@ class EntityDefinitionUpdateTest extends EntityKernelTestBase { ->execute(); $this->fail($message); } - catch (\RuntimeException $e) { - if ($e instanceof DatabaseExceptionWrapper || $e instanceof IntegrityConstraintViolationException) { - // Now provide a value for the 'not null' column. This is expected to - // succeed. - $values['new_bundle_field_shape'] = $this->randomString(); - $this->database->insert('entity_test_update__new_bundle_field') - ->fields($values) - ->execute(); - } - else { - // Keep throwing it. - throw $e; - } + catch (IntegrityConstraintViolationException $e) { + // Now provide a value for the 'not null' column. This is expected to + // succeed. + $values['new_bundle_field_shape'] = $this->randomString(); + $this->database->insert('entity_test_update__new_bundle_field') + ->fields($values) + ->execute(); } }