diff --git a/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php b/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php index e6c16bb4e83..4bc3c29e087 100644 --- a/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php +++ b/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php @@ -14,6 +14,7 @@ use Drupal\Core\Database\DatabaseNotFoundException; use Drupal\Core\Database\TransactionCommitFailedException; use Drupal\Core\Database\DatabaseException; use Drupal\Core\Database\Connection as DatabaseConnection; +use Drupal\Component\Utility\Unicode; /** * @addtogroup database @@ -34,6 +35,16 @@ class Connection extends DatabaseConnection { */ protected $needsCleanup = FALSE; + /** + * The minimal possible value for the max_allowed_packet setting of MySQL. + * + * @link https://mariadb.com/kb/en/mariadb/server-system-variables/#max_allowed_packet + * @link https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_max_allowed_packet + * + * @var int + */ + const MIN_MAX_ALLOWED_PACKET = 1024; + /** * Constructs a Connection object. */ @@ -49,6 +60,24 @@ class Connection extends DatabaseConnection { $this->connectionOptions = $connection_options; } + /** + * {@inheritdoc} + */ + public function query($query, array $args = array(), $options = array()) { + try { + return parent::query($query, $args, $options); + } catch (DatabaseException $e) { + if ($e->getPrevious()->errorInfo[1] == 1153) { + // If a max_allowed_packet error occurs the message length is truncated. + // This should prevent the error from recurring if the exception is + // logged to the database using dblog or the like. + $message = Unicode::truncateBytes($e->getMessage(), self::MIN_MAX_ALLOWED_PACKET); + $e = new DatabaseExceptionWrapper($message, $e->getCode(), $e->getPrevious()); + } + throw $e; + } + } + /** * {@inheritdoc} */ @@ -278,6 +307,7 @@ class Connection extends DatabaseConnection { } } } + } diff --git a/core/modules/system/src/Tests/Database/LargeQueryTest.php b/core/modules/system/src/Tests/Database/LargeQueryTest.php new file mode 100644 index 00000000000..eed80048c08 --- /dev/null +++ b/core/modules/system/src/Tests/Database/LargeQueryTest.php @@ -0,0 +1,58 @@ +fetchField(); + if (Environment::checkMemoryLimit($max_allowed_packet + (16 * 1024 * 1024))) { + $long_name = str_repeat('a', $max_allowed_packet + 1); + try { + db_query('SELECT name FROM {test} WHERE name = :name', array(':name' => $long_name)); + $this->fail("An exception should be thrown for queries larger than 'max_allowed_packet'"); + } catch (DatabaseException $e) { + // Close and re-open the connection. Otherwise we will run into error + // 2006 "MySQL server had gone away" afterwards. + Database::closeConnection(); + Database::getConnection(); + $this->assertEqual($e->getPrevious()->errorInfo[1], 1153, "Got a packet bigger than 'max_allowed_packet' bytes exception thrown."); + // Use strlen() to count the bytes exactly, not the unicode chars. + $this->assertTrue(strlen($e->getMessage()) <= $max_allowed_packet, "'max_allowed_packet' exception message truncated."); + } + } + else { + $this->verbose('The configured max_allowed_packet exceeds the php memory limit. Therefore the test is skipped.'); + } + } + else { + $this->verbose('The test requires MySQL. Therefore the test is skipped.'); + } + } + +}