Типовые ошибки при миграции MySQL → PostgreSQL с pgloader и как их снять
Введение
При переносе на PostgreSQL сообщения об ошибках часто выглядят страшнее, чем есть на самом деле. Ниже — несколько повторяющихся классов проблем, с которыми сталкиваются команды, использующие pgloader; для проекта на 1С‑Битрикс прямой параллели с ядром нет, но те же паттерны встречаются в смежных PHP-сервисах и интеграционных базах рядом с порталом.
Клиент pgloader не подключается к MySQL
Типичный текст содержит MYSQL-UNSUPPORTED-AUTHENTICATION: драйверы стека Common Lisp не понимают дефолтный для MySQL 8 механизм caching_sha2_password. Практичный обход для служебного учёта миграции — временно перейти на mysql_native_password и ограничить хост, с которого разрешено подключаться.
ALTER USER 'replica_loader'@'%'
IDENTIFIED WITH mysql_native_password BY 'REPLACE_WITH_SECURE_VALUE';
FLUSH PRIVILEGES;После переноса верните политику паролей и сетевые ACL в соответствие с регламентом.
Виртуальный столбец в источнике и «его нет» в целевой схеме
В MySQL могли добавить GENERATED … VIRTUAL столбец вручную, а Laravel/Doctrine-миграции на стороне Postgres его не создают. Тогда поток pgloader упирается в попытку писать в колонку, которой физически нет в Postgres. Если виртуальное поле воспроизводимо формулой, лучше оформить его как VIEW или вычисляемое выражение уже в Postgres; если нет — удалите столбец на источнике перед финальным забегом или смоделируйте его там, где это допустимо по данным.
ALTER TABLE web_sessions DROP COLUMN utm_utm_src;Имена таблиц/полей заменены; подставьте свои из сообщения об ошибке.
Символ U+0000 в JSON-тексте
PostgreSQL не принимает нулевой байт внутри текстовых/JSON-путей, поэтому 22P05: unsupported Unicode escape sequence при COPY часто означает, что в колонке лежит «грязный» JSON или сериализация объектов PHP с управляющими символами. Путь исправления двойной: убрать источник (корректная сериализация без приватных полей-magic) и санировать уже записанные строки до повторной загрузки.
Пустая строка вместо UUID
Сообщение вида invalid input syntax for type uuid: "" говорит, что приложение сохранило пустую строку в столбце, который в Postgres строже. До загрузки нормализуйте значения в NULL или удалите заведомо битые строки — шаблон тот же, меняются только таблицы.
UPDATE web_sessions SET source_id = NULL WHERE source_id = '';
DELETE FROM search_requests WHERE profile_uuid = '';
UPDATE filters_ui SET connector_id = NULL WHERE connector_id = '';Админки Horizon/Telescope и PEM
Если в логах фигурируют PEM routines: no start line, приложение может пытаться читать повреждённый ключ JWT из таблицы вроде jwt_public_keys. На тестовом контуре иногда быстрее очистить таблицу и заново выпустить ключи уже в среде Postgres, чем восстанавливать битые записи вслепую.
artisan migrate после оборванной загрузки
Если в BEFORE LOAD DO временно переименовали public, а прогрузка оборвалась, Laravel может жаловаться, что некуда создавать таблицу migrations. Проверьте реальное имя схемы в \dn и верните public.
ALTER SCHEMA "staging_core_tmp" RENAME TO "public";Итог
Большинство ошибок сводится к несогласованности типов между СУБД, «устаревшим» клиентом MySQL и хвостами ручных правок DDL. Перед боевым окном делайте сухой прогон, фиксируйте DDL-дифф и автоматизируйте санитизацию данных, чтобы второй запуск pgloader был предсказуемым.
Не хотите копаться сами?
Починю за 1-3 дня. Без предоплаты — оплата по результату.
15+ лет опыта с 1С-Битрикс · Без предоплаты · 7 дней гарантии