Установка плагина завершается ошибкой:
Duplicate entry '0' for key 'PRIMARY'
Очевидное прочтение заключается в том, что плагин попытался вставить строку с id 0. Реальное положение дел обычно противоположно: плагин вставил строку без указания имени колонки id, полагаясь на то, что AUTO_INCREMENT предоставит его, и получил 0 , поскольку у колонки больше нет атрибута AUTO_INCREMENT . Вторая такая вставка затем конфликтует с первой.
Это не баг плагина, и попытки исправить его как баг лишь тратят кучу времени. Вот как это выглядит, почему распространяется и как это исправить, не наступая на те же грабли дважды.
Признак: проблема перемещается
Впервые мы столкнулись с этим при установке модуля в магазине на PrestaShop. Ошибка последовательно проходила по таблицам:
-
ps_configuration— первая настройка, которую записывает модуль - затем
ps_log— строка «начало установки модуля» - затем
ps_module— строка, регистрирующая сам модуль
Исправьте одну таблицу, запустите установку снова — ошибка возникнет на следующей. Эта игра в «молотобойца» и есть диагностический признак. Баг плагина не мигрирует в таблицы ядра. Если ваша ошибка меняет таблицу каждый раз, когда вы исправляете предыдущую, повреждение затрагивает всю базу данных.
Позже в том же магазине произошел сбой на витрине: добавление любого товара в корзину вызывало ту же ошибку в INSERT INTO ps_cart . Та же причина, другая таблица, к которой еще никто не прикасался. Если бы дело дошло до оформления заказа, следующим стал бы ps_orders .
Откуда это берется
Некорректный импорт или восстановление. mysqldump , дампы phpMyAdmin и различные инструменты миграции могут создавать схему, которая воссоздает первичный ключ без атрибута AUTO_INCREMENT — и такое восстановление часто оставляет внутренние счетчики устаревшими, из-за чего строки застревают со значением id 0.
В восстановленном нами магазине каждый AUTO_INCREMENT в базе данных исчез: товары, комбинации, модули, конфигурация, сотрудники, магазины, языки и около пятидесяти пяти таблиц модулей.
Ловушка при исправление
Естественное исправление выглядит так:
ALTER TABLE `ps_log` MODIFY `id_log` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT;
что приводит к ошибке:
#1062 - ALTER TABLE causes auto_increment resequencing,
resulting in duplicate entry 'N' for key 'PRIMARY'
Добавление AUTO_INCREMENT заставляет MySQL заново пронумеровать существующую строку id = 0 , а устаревший счетчик присваивает ей id, который уже используется.
Предотвратите перенумерацию:
SET SESSION sql_mode = 'NO_AUTO_VALUE_ON_ZERO';
ALTER TABLE `ps_log` MODIFY `id_log` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT;
ALTER TABLE `ps_configuration` MODIFY `id_configuration` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT;
ALTER TABLE `ps_module` MODIFY `id_module` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT;
NO_AUTO_VALUE_ON_ZERO указывает MySQL воспринимать литерал 0 как реальное значение, а не как запрос на получение следующего id, поэтому существующая строка сохраняет свой id, и ничего не перенумеровывается.
Запускайте SET и ALTER в рамках одного запроса (сессии отправки). SET SESSION действует в рамках только одного соединения; phpMyAdmin может выделить вам другое соединение для отдельного запроса, и тогда ALTER завершится ошибкой точно так же, как и раньше, в то время как вы уверены, что только что установили режим.
Поиск всех затронутых таблиц
Не делайте это вручную. Спросите схему:
SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'your_database_name'
AND COLUMN_KEY = 'PRI'
AND EXTRA NOT LIKE '%auto_increment%'
AND DATA_TYPE IN ('int','bigint','smallint','mediumint','tinyint')
ORDER BY TABLE_NAME;
Жестко прописывайте имя схемы. Генератор, построенный на DATABASE() , ничего не возвращает, когда phpMyAdmin находится в контексте information_schema , что часто случается, если вы перешли через обозреватель схем. Запрос выполняется, возвращает ноль строк, и вы приходите к выводу, что с базой данных все в порядке.
Этот запрос выводит список кандидатов. Он не выводит готовые исправления — и эта разница имеет значение, поскольку некоторые из этих первичных ключей законно не являются автоинкрементируемыми.
Что следует исключить
Слепое применение ALTER ко всему, что возвращает запрос, повременит вашу базу данных. Исключите:
-
Все составные первичные ключи.
AUTO_INCREMENTприменяется к единственной колонке, которая является самой левой частью ключа. Многоколоночные PK в этом списке являются связями и таблицами сопряжения, их трогать нельзя. -
Одноколоночные целочисленные PK, которые по задумке не являются автоинкрементируемыми — внешний ключ, дублирующий первичный ключ. В PrestaShop к ним относятся
ps_address_format(id_country),ps_product_sale(id_product),ps_ip2location(ip_to), таблицыps_layered_indexable_*,ps_pscheckout_address/ps_pscheckout_customer(id_customer),ps_psshipping_addressи таблицыps_eventbus_*. В вашей схеме будут свои эквиваленты: критерий заключается в том, назначается ли значение другой таблицей или генерируется здесь.
Два особых случая, о которых стоит знать, поскольку они выглядят как исключения, но ими не являются: ps_customization.id_customization и ps_cms_role.id_cms_role входят в составные первичные ключи, но правомерно являются AUTO_INCREMENT в качестве самой левой колонки.
Рабочий процесс: сгенерируйте инструкции ALTER с помощью запроса, который объединяет их через CONCAT, прочитайте вывод, удалите строки, принадлежащие списку исключений, а затем запустите то, что осталось. Два типа ошибок, с которыми мы столкнулись, делая ровно это:
- Запуск генератора с верой в то, что это что-то исправило. Он лишь выводит инструкции. Вам нужно запустить полученный результат.
- Удаление строк
id = 0вместо запуска ALTER. Это устраняет непосредственный конфликт, но оставляет схему сломанной, поэтому проблема возвращается при следующей вставке.
Еще одна операционная деталь: если вы вставляете большой пакет в phpMyAdmin и переносы строк теряются, комментарий -- «проглотит» следующую за ним инструкцию. Отправляйте SQL без комментариев для массовой вставки.
Что делать вместо этого, если есть возможность
Исправление «на живую» работает — мы проделали это примерно для 130 таблиц и после этого проверили витрину, — но подумайте, что это значит. Восстановление, при котором из каждого первичного ключа выпал AUTO_INCREMENT , не было избирательным. Возможно, оно также удалило внешние ключи, значения по умолчанию или атрибуты столбцов, которые вы еще не заметили, и никакой объем ALTER для первичных ключей вам об этом не сообщит.
Если существует чистый дамп с момента до неудачного импорта, восстановите лучше его. Исправление на месте — это то, к чему прибегают, когда такого дампа нет.
Краткая версия за пять секунд
- Установка плагина завершается ошибкой на таблице ядра с
Duplicate entry '0'→ подозревайте схему, а не плагин. - Ошибка перемещается в другую таблицу каждый раз, когда вы исправляете предыдущую → подтверждено, проблема затрагивает всю базу данных.
-
SET SESSION sql_mode = 'NO_AUTO_VALUE_ON_ZERO'в том же запросе, что и ALTER. - Запускайте через
information_schema, жестко прописывайте имя схемы и проверяйте список перед его выполнением. - Предпочитайте восстановление хорошего дампа.
Из опыта поддержки примерно шестидесяти модулей PrestaShop в MEG Venture. Мы потеряли две недели на эту проблему в двух отдельных инцидентах, прежде чем распознали ее паттерн.
Комментарии (0)
Пока нет комментариев — будьте первым.