Вход на сайт

Просмотр новости

Найдите то, что Вас интересует

ora2pg переносит около 80% Oracle‑схемы. А что происходит с оставшимися 20%?

Дата публикации: 22-08-2026 10:54:28

Работаю в конторе, которая обслуживает госзаказчиков, и последние месяцы у меня одна большая головная боль: перевод старой оракловой схемы на Postgres Pro. Контур закрытый, интернета нет, лицензии на Enterprise нет, проприетарной ora2pgpro тоже нет. То есть из автоматики только опенсорсный ora2pg, и всё.Штука рабочая, кто пользовался, тот знает. По разным оценкам тянет процентов восемьдесят перевода PL/SQL в PL/pgSQL. Для инструмента, который пилят несколько человек в свободное время против СУБД с тридцатилетней историей, это вообще‑то дофига )Схема не маленькая, под сотню пакетов и триггеров, плюс куча legacy, которое живёт ещё с нулевых. И когда я первый раз прогнал её через ora2pg и увидел зелёный SHOW_REPORT, я на секунду выдохнул. Рано выдохнул, как выяснилось;)В итоге доканали меня не эти восемьдесят процентов, а оставшиеся двадцать. И не количеством. А тем, что они не падают в момент конвертации, а спокойно ждут, пока код доедет до прода. Читать далее

Основное содержимое страницы с новостью.

Работаю в конторе, которая обслуживает госзаказчиков, и последние месяцы у меня одна большая головная боль: перевод старой оракловой схемы на Postgres Pro. Контур закрытый, интернета нет, лицензии на Enterprise нет, проприетарной ora2pgpro тоже нет. То есть из автоматики только опенсорсный ora2pg, и всё.

Штука рабочая, кто пользовался, тот знает. По разным оценкам тянет процентов восемьдесят перевода PL/SQL в PL/pgSQL. Для инструмента, который пилят несколько человек в свободное время против СУБД с тридцатилетней историей, это вообще‑то дофига )

Схема не маленькая, под сотню пакетов и триггеров, плюс куча legacy, которое живёт ещё с нулевых. И когда я первый раз прогнал её через ora2pg и увидел зелёный SHOW_REPORT, я на секунду выдохнул. Рано выдохнул, как выяснилось;)

В итоге доканали меня не эти восемьдесят процентов, а оставшиеся двадцать. И не количеством. А тем, что они не падают в момент конвертации, а спокойно ждут, пока код доедет до прода.

Молчание вместо ошибки

Нормальный конвертер, когда упирается во что‑то, чего не умеет, обычно об этом сообщает. Падает, ругается, пишет ERROR. Неприятно, зато сразу понятно, где косяк.

ora2pg в самых интересных местах так не делает. Он либо молча выкидывает конструкцию, которую не осилил, либо переносит её с багом, и баг этот сидит тихо до первого живого вызова. CREATE TABLE при этом проходит чисто, схема разворачивается, тесты в духе «схема развернулась» зелёные. А что там дальше, уже как повезёт.

Покажу три штуки, на которые напоролся. Всё прогнано через ora2pg 25.0 и PostgreSQL 16.

READ ONLY таблица. В оракле это гарантия на уровне сервера: любой INSERT/UPDATE/DELETE в такую таблицу ловит ORA-12081, кто бы ни пытался, хоть владелец схемы.

sql

CREATE TABLE audit_log (
    log_id  NUMBER,
    message VARCHAR2(200)
) READ ONLY;

ora2pg конвертит это так:

sql

CREATE TABLE audit_log (
    log_id bigint,
    message varchar(200)
) ;

Секция READ ONLY просто испарилась, без единого предупреждения. Проверяю на настоящем PostgreSQL:

sql

INSERT INTO audit_log VALUES (1, 'should have been blocked in Oracle');
-- INSERT 0 1

Записалось. В оракле этот же инсерт словил бы ORA-12081 у кого угодно. Если READ ONLY был единственной защитой какого‑нибудь архива или таблицы‑снапшота от случайной записи, то после миграции этой защиты нет вообще. И узнаешь ты об этом не в момент миграции, а когда кто‑нибудь туда что‑нибудь запишет. Может, через полгода )

Я, кстати, на похожем чуть не сел. Была у нас таблица‑справочник, которую от случайной записи держал ровно READ ONLY и больше ничего. Заметил я это по чистой случайности, когда сверял DDL глазами, а не когда туда уже налили бы мусора. Повезло.

Баг с двойными скобками в IDENTITY. Тут уже не пропуск, а честный баг в самой подстановке.

sql

CREATE TABLE customers (
    customer_id NUMBER GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1),
    name        VARCHAR2(100)
);

Превращается в:

sql

CREATE TABLE customers (
    customer_id bigint GENERATED ALWAYS AS IDENTITY ((START WITH 1 INCREMENT BY 1)),
    name varchar(100)
) ;

Смотрим на двойные скобки перед START WITH. Откуда там вторая пара, я так и не понял ), но CREATE TABLE с ней падает прямо на загрузке DDL:

ERROR:  syntax error at or near "("

Эта находка самая ранняя из всего реестра, ломается раньше, чем дело вообще дойдёт до вызова функций. И противная, потому что GENERATED ALWAYS AS IDENTITY в оракле начиная с 12c это нормальный современный способ сделать автоинкремент, им реально пользуются. Причём если написать без опций в скобках (просто GENERATED ALWAYS AS IDENTITY, без START WITH и INCREMENT BY), всё конвертится нормально. Баг именно в обработке опций.

На это я убил полвечера, потому что глазами двойные скобки не видно, пока не начнёшь сверять DDL построчно. Сидишь, смотришь на ((START WITH, и не сразу доходит, что это не твои кривые руки, а конвертер лишнюю скобку влепил.

CROSS APPLY. Третья ловушка хитрее двух предыдущих. Пакет с процедурой компилируется без ошибок, потому что ora2pg просто копирует CROSS APPLY(...) как есть, слово в слово. А постгрес про APPLY не знает ничего, и падает не при деплое, а когда процедуру первый раз дёрнут:

ERROR:  syntax error at or near "APPLY"

Снаружи выглядит так, будто всё развернулось и работает. А не работает оно ровно до момента, когда кто‑то реально вызовет эту процедуру.

Вот как эти три штуки лежат вместе в отчёте самого инструмента.

b9c540e412b8d516ee2f519f2e28c939.pngПочему я уверен, что это не выдумки

Тут важно, откуда вообще уверенность, что это баги, а не мои фантазии. Потому что «звучит по‑ораклиному сложно» само по себе ничего не значит. Куча гипотез, которые казались проблемными, на проверке отвалились. Например, CREATE PACKAGE выглядел очевидным кандидатом, а ora2pg переносит его нормально, без претензий.

Правило у меня простое: детектор появляется только тогда, когда я руками воспроизвёл баг. Не когда конструкция выглядит подозрительно.

  1. Беру конкретную оракловую конструкцию.

  2. Собираю минимальный пример.

  3. Прогоняю через настоящий ora2pg.

  4. Заливаю результат в настоящий PostgreSQL и смотрю, что получилось на самом деле.

  5. Справился, гипотезу выкидываю. Нашёлся воспроизводимый баг, завожу тест‑фикстуру и пишу детектор.

032cd0537c0fb7f5e56e90856a529035.png

Плюс к этому прогнал все детекторы на живом открытом PL/SQL, тысяч на сто сорок строк (пакеты alexandria-plsql-utils и официальные демо‑схемы оракла db-sample-schemas), чтобы убедиться, что они не начинают срабатывать на нормальном коде. Заодно там нашлись и реальные попадания. В официальной схеме SH есть индекс sup_text_idx, который использует INDEXTYPE IS CTXSYS.CONTEXT (это Oracle Text), а в постгресе такого просто нет. Такие штуки я оставляю в проекте регрессионными тестами: не выдуманный пример, а код, который правда существует и лежит в паблике.

Как инструмент нашёл баг в самом себе

Раз пошёл честный разговор, вот ещё история. В какой‑то момент ревью собственных изменений выкатило системный баг в уже выпущенных детекторах. DBMS_METADATA.GET_DDL, стандартный способ выгрузить DDL из схемы, по умолчанию не ставит точку с запятой в конце оператора. А часть детекторов определяла границу своего оператора тупо: до ближайшей ;, а если её нет, до конца файла. И на таком экспорте без точек с запятой конструкция из второй таблицы могла по ошибке приписаться первой.

То есть инструмент, который я делаю ровно для того, чтобы ловить чужие косяки на выгрузке из оракла, сам пару релизов таскал косяк ровно на этой самой выгрузке. Смешно и обидно:) Починил общим хелпером: он режет оператор либо по ближайшей ;, либо по началу следующего оператора того же типа, что раньше наступит.

Отдельная песня, закрытый контур

Всё это живёт в среде без интернета вообще. pip install там не работает, скачать зависимость неоткуда. Так что каждый раз собираешь самодостаточный бандл на машине с сетью, тащишь его через jump host, и только потом ставишь на изолированной машине уже совсем без сети. Инструмент я поэтому сразу так и затачивал: анализ не должен ходить наружу ни при каких условиях, потому что ему просто некуда ходить. Выгрузка DDL из живого оракла и сам анализ у меня разнесены на две отдельные команды специально. Единственное, что вообще пересекает границу контура, это уже готовые .sql файлы.

Что в итоге

Получился небольшой сканер. Не замена ora2pg, а надстройка сверху, которая запускается до миграции. Смотрит на схему оракла и для каждой проблемной конструкции говорит: что с ней случится, почему, и на что менять руками. На сегодня в реестре 28 подтверждённых находок, у каждой воспроизводимый пример, реальный вывод ora2pg и тесты, в том числе guard‑тесты на ложные срабатывания.

Сама библиотека детекторов это чистый Python без единой внешней зависимости, можно дёргать из своих скриптов, ничего доустанавливать не надо. У CLI одна зависимость, rich, ради нормального вывода в терминал.

sh

pip install ora2pg-gap-report
ora2pg-gap-report path/to/schema_dump.pkb another_file.sql

Проект не то чтобы звёзд с неба хватает, я его пилю под свою конкретную боль на работе. Но если он сэкономит кому‑то один разбор полётов в проде, значит, не зря )

Код и вся доказательная база на гитхабе: Lunch418/ora2pg‑gap‑report, лицензия MIT. Если у вас есть своя оракловая схема с какой‑нибудь дичью, которой нет в реестре, заводите issue. Мне правда интересно её найти и разобрать так же честно, как всё остальное тут )

Схожие новости

#Наименование новостиТональностьИнформативностьДата публикации
1[Перевод] Статистика PostgreSQL: почему запросы выполняются медленно07.7419-08-2026
2Как уронить базу данных06.7217-08-2026
3MetaORM — когда устал от ORM настолько, что написал свою07.3822-08-2026
4REPACK в PostgreSQL 19: перепаковка в ядре и, как всегда, дьявол в деталях08.9820-08-2026
5Ловушка неявного приведения числовых типов0530-06-2026
6Как мы тестируем Tantor Postgres для 1С — от нагрузочных тестов до оптимизаций планировщика5724-06-2026
7[Перевод] Проектирование системы хранения POSTGRES0515-07-2026
8Устанавливаем Digital Q.DataBase 18.2 на РЕД ОС 8: PostgreSQL, MS SQL и Oracle в одной СУБД010.412-08-2026
9Четыре антипаттерна CTE в PostgreSQL: разбираем на EXPLAIN ANALYZE09.1620-08-2026
103000 точек на карте грузились полсекунды. Ускорял не там, где думал010.7521-08-2026

Классификация: Мнения. Схожих патентов: 0. Схожих новостей: 10. Тональность: 1. Информативность: 11.01. Источник: habr.com.