Ошибки БД при переходе на mysql 8

При переходе с 5й версии mysql на 8ю, при тестировании могут возникать проблемы с базой данных. Часть из них успешно исправит сам битрикс или предложит варианты решения, например прописать определенные параметры в конфигурационные файлы. Но часть проблем останутся нерешенными.

Одна из таких ошибок при тестировании:

Ошибка! Кодировки таблиц имеют ошибки, общее число ошибок: 987, из них автоматически могут быть исправлены: 0.

Тут нам битрикс предлагает заглянуть поглубже, а точнее в журнал проверки системы. И там открывается ужасная картина:

Кодировка таблицы "b_b24connector_button_site" (utf8mb4) отличается от кодировки базы (utf8mb3)
Кодировка поля "SITE_ID" таблицы "b_b24connector_button_site" (utf8mb4) отличается от кодировки базы (utf8mb3)
Кодировка таблицы "b_catalog_contractor" (utf8mb4) отличается от кодировки базы (utf8mb3)
Кодировка поля "PERSON_TYPE" таблицы "b_catalog_contractor" (utf8mb4) отличается от кодировки базы (utf8mb3)
Кодировка поля "PERSON_NAME" таблицы "b_catalog_contractor" (utf8mb4) отличается от кодировки базы (utf8mb3)
Кодировка поля "PERSON_LASTNAME" таблицы "b_catalog_contractor" (utf8mb4) отличается от кодировки базы (utf8mb3)
Кодировка поля "PERSON_MIDDLENAME" таблицы "b_catalog_contractor" (utf8mb4) отличается от кодировки базы (utf8mb3)
Кодировка поля "EMAIL" таблицы "b_catalog_contractor" (utf8mb4) отличается от кодировки базы (utf8mb3)
Кодировка поля "PHONE" таблицы "b_catalog_contractor" (utf8mb4) отличается от кодировки базы (utf8mb3)
Кодировка поля "POST_INDEX" таблицы "b_catalog_contractor" (utf8mb4) отличается от кодировки базы (utf8mb3)
Кодировка поля "COUNTRY" таблицы "b_catalog_contractor" (utf8mb4) отличается от кодировки базы (utf8mb3)
Кодировка поля "CITY" таблицы "b_catalog_contractor" (utf8mb4) отличается от кодировки базы (utf8mb3)
Кодировка поля "COMPANY" таблицы "b_catalog_contractor" (utf8mb4) отличается от кодировки базы (utf8mb3)
Кодировка поля "INN" таблицы "b_catalog_contractor" (utf8mb4) отличается от кодировки базы (utf8mb3)
Кодировка поля "KPP" таблицы "b_catalog_contractor" (utf8mb4) отличается от кодировки базы (utf8mb3)
Кодировка поля "ADDRESS" таблицы "b_catalog_contractor" (utf8mb4) отличается от кодировки базы (utf8mb3)
Кодировка таблицы "b_catalog_currency" (utf8mb4) отличается от кодировки базы (utf8mb3)
Кодировка поля "CURRENCY" таблицы "b_catalog_currency" (utf8mb4) отличается от кодировки базы (utf8mb3)
Кодировка поля "NUMCODE" таблицы "b_catalog_currency" (utf8mb4) отличается от кодировки базы (utf8mb3)
Кодировка поля "BASE" таблицы "b_catalog_currency" (utf8mb4) отличается от кодировки базы (utf8mb3)

Но, если разбираться более конкретно, то на самом деле, тут нет ничего страшного. Проблема лишь в том, что в базе используется кодировка utf8mb3, а битрикс ожидает увидеть кодировку utf8mb4. Тут следует углубится в теорию и понять, чем же отличаются utf8mb3 и utf8mb4

На самом деле, данная ошибка практически ни на что не влияет, и по идее битрикс должен был пропустить мимо, но что есть, то есть. Различия в этих кодировках минимальны, самое главное, что это все то же utf8:

- utf8mb3 поддерживает только символы в Basic Multilingual Plane (BMP), то-есть выделяет 3 байта на символ, что вполне хватает для символов практически всех языков, но не может работать с более длинными символами. Это эмодзи(emoji) и сложные иероглифы.

- utf8mb4 поддерживает до 4х байтов на символ, что закрывает недостатки utf8mb3.

Соответственно, в новых проектах, следует хранить данные в utf8mb4, что битрикс и прописывает в своих sql картах. Но, что же делать нам с такой ошибкой? Самое главное - это сделать бекап базы данных. А дальше меняем кодировку БД. Для этого переходим в Настройки -> Инструменты -> SQL запрос и выполняем:

ALTER DATABASE `имя_вашей_базы` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

Теперь Ваша база работает в современной кодировке, но надо об этом сообщить же и битриксу. Переходим в /bitrix/php_interface/after_connect.php и заменяем:

$DB->Query("SET NAMES 'utf8'");
$DB->Query('SET collation_connection = "utf8_unicode_ci"');

на:

$DB->Query("SET NAMES 'utf8mb4'");
$DB->Query('SET collation_connection = "utf8mb4_general_ci"');

Похожую процедуру производим и в /bitrix/php_interface/after_connect_d7.php:

$this->queryExecute("SET NAMES 'utf8");
$this->queryExecute('SET collation_connection = "utf8_unicode_ci"');

Меняем на

$this->queryExecute("SET NAMES 'utf8mb4'");
$this->queryExecute('SET collation_connection = "utf8mb4_general_ci"'); 

Заметьте, что тут используется конструкция $this->queryExecute, но до версии 22.0.0 использовалась конструкция:

$connection = \Bitrix\Main\Application::getConnection();
$connection->queryExecute('SET NAMES "utf8"');
$connection->queryExecute('SET collation_connection = "utf8_unicode_ci"');

Снова тестируем систему. Могут возникнуть ошибки, которые битрикс исправит автоматически. Исправляем и снова тестируем. И получаем ошибку:

Ошибка! Кодировки таблиц имеют ошибки, общее число ошибок: 1640, из них автоматически могут быть исправлены: 0..

Открываем журнал и видим примерно тоже самое:

Кодировка таблицы "b_admin_notify" (utf8mb3) отличается от кодировки базы (utf8mb4)
Кодировка поля "MODULE_ID" таблицы "b_admin_notify" (utf8mb3) отличается от кодировки базы (utf8mb4)
Кодировка поля "TAG" таблицы "b_admin_notify" (utf8mb3) отличается от кодировки базы (utf8mb4)
Кодировка поля "MESSAGE" таблицы "b_admin_notify" (utf8mb3) отличается от кодировки базы (utf8mb4)
Кодировка поля "ENABLE_CLOSE" таблицы "b_admin_notify" (utf8mb3) отличается от кодировки базы (utf8mb4)
Кодировка поля "PUBLIC_SECTION" таблицы "b_admin_notify" (utf8mb3) отличается от кодировки базы (utf8mb4)
Кодировка поля "NOTIFY_TYPE" таблицы "b_admin_notify" (utf8mb3) отличается от кодировки базы (utf8mb4)
Кодировка таблицы "b_admin_notify_lang" (utf8mb3) отличается от кодировки базы (utf8mb4)
Кодировка поля "LID" таблицы "b_admin_notify_lang" (utf8mb3) отличается от кодировки базы (utf8mb4)
Кодировка поля "MESSAGE" таблицы "b_admin_notify_lang" (utf8mb3) отличается от кодировки базы (utf8mb4)
Кодировка таблицы "b_agent" (utf8mb3) отличается от кодировки базы (utf8mb4)
Кодировка поля "MODULE_ID" таблицы "b_agent" (utf8mb3) отличается от кодировки базы (utf8mb4)
Кодировка поля "NAME" таблицы "b_agent" (utf8mb3) отличается от кодировки базы (utf8mb4)
Кодировка поля "ACTIVE" таблицы "b_agent" (utf8mb3) отличается от кодировки базы (utf8mb4)
Кодировка поля "IS_PERIOD" таблицы "b_agent" (utf8mb3) отличается от кодировки базы (utf8mb4)
Кодировка поля "RUNNING" таблицы "b_agent" (utf8mb3) отличается от кодировки базы (utf8mb4)

Теперь нам надо делать все самим. Снова нужно сформировать запрос, который изменит кодировку конкретной таблицы:

ALTER TABLE `b_admin_notify` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

То-есть находим все записи о неверной кодировки таблицы и формируем запросы. После чего выполняем их и запускаем проверку системы снова. После чего могут появится новые проблемы с полями в журнале:

ALTER TABLE `b_messageservice_channel` CHANGE `ADDITIONAL_PARAMS` `ADDITIONAL_PARAMS` text NULL DEFAULT NULL;
ALTER TABLE `b_ticket` MODIFY `TITLE` varchar(255) NOT NULL ;

Ошибки такого вида - это запросы, которые мы просто выполняем в Настройки -> Инструменты -> SQL запрос

Вариативность ошибок может быть разная, например:

В таблице b_iblock поле DESCRIPTION "`DESCRIPTION` mediumtext NULL DEFAULT NULL" не соответствует описанию на диске "`DESCRIPTION` text NULL DEFAULT NULL"

Подобные ошибки лечатся преобразованием в запрос вида:

ALTER TABLE `b_iblock` CHANGE `DESCRIPTION` `DESCRIPTION` text NULL DEFAULT NULL;

После всех преобразований, тестируем систему, ошибок быть не должно. Далее необходимо оптимизировать БД. Переходим в Настройки -> Инструменты -> Диагностика -> Оптимизация БД и выполняем оптимизацию. На этом - все.

Ну и на последок, для тех кому лень руками прописывать все запросы, можно скопировать их из журнала в какой нибудь файл, например в convert_db.txt, рядом положить файл convert_db.php и написать простейший скриптик, который перобразует текст ошибок в SQL запросы, например:

$lines = file('convert_db.txt', FILE_IGNORE_NEW_LINES);

foreach($lines as $line){
	if (str_contains($line, 'Кодировка таблицы')) {
		echo preg_replace("!Кодировка таблицы \"(.*?)\" \((.*?)\) отличается от кодировки базы \((.*?)\)!si","ALTER TABLE \\1 CONVERT TO CHARACTER SET \\3 COLLATE \\3_general_ci;",$line);
		echo "<\br>";
	}elseif(str_contains($line, 'В таблице')){
		echo preg_replace("!В таблице (.*?) поле (.*?) \"(.*?)\" не соответствует описанию на диске \"(.*?)\"!si","ALTER TABLE `\\1` CHANGE `\\2` \\4;",$line);
		echo "<\br>";
	}
}

В итоге, вместо:

В таблице b_iblock поле DESCRIPTION "`DESCRIPTION` mediumtext NULL DEFAULT NULL" не соответствует описанию на диске "`DESCRIPTION` text NULL DEFAULT NULL"
Кодировка таблицы "b_admin_notify_lang" (utf8mb3) отличается от кодировки базы (utf8mb4)

Вы получите:

ALTER TABLE `b_iblock` CHANGE `DESCRIPTION` `DESCRIPTION` text NULL DEFAULT NULL;
ALTER TABLE b_admin_notify_lang CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;