Инструмент inplace_upgrade#

Описание решения#

Для выполнения задачи обновления каталога используется инструмент inplace_upgrade.sh, который применяет заранее подготовленные разработчиками SQL-скрипты обновления системного каталога (правила написания приведены в разделе «Правила написания SQL-скриптов для обновления системных данных каталога»), а также утилиту update_catalog_version для изменения версии каталога в global/pg_control. Скрипт обновления и все что он использует, находится в директории installer/utilities/pg_inplace_upgrade.

Внимание!

Скрипт inplace_upgrade.sh, прилагаемые SQL-скрипты обновления и утилита update_catalog_version являются необходимыми для корректной работы. Изменение/удаление любого из них приведет к неправильной работе утилиты.

Для работы утилиты необходимы пакеты компонентов той версии, на которую будет совершаться обновление.

При обновлении каталога меняется содержимое системных таблиц pg_catalog (таких, как pg_class, pg_type и т.д.). Сами системные таблицы и индексы в данном типе обновления не затрагиваются. Объекты базы данных (функции, представления, типы) описаны в системном каталоге и хранятся в виде записей его таблиц. Добавлять или изменять можно только объекты по OID до 16383 включительно. Все объекты OID которых выше 16383 не должны меняться с помощью SQL-скриптов обновления, так как они относятся к пользовательским объектам, изменение которых не предусмотрено.

inplace_upgrade.sh использует функцию ядра block_user_data_modification для установки ограничений на следующий ряд SQL-операций на время ее работы:

  • DROP TYPE/FUNCTION/VIEW;

  • ALTER TYPE/FUNCTION/VIEW ... RENAME;

  • CREATE OR REPLACE FUNCTION/VIEW, кроме CREATE FUNCTION/VIEW.

Вышеперечисленные ограничения можно при необходимости снять, тем самым разрешив любые изменения системного и пользовательских каталогов, но это увеличит вероятность порчи пользовательских данных и системного каталога в случае наличия ошибок в SQL скриптах. Снятие ограничений производится флагом --drop-on.

По сравнению с pg_upgrade инструмент (inplace_upgrade.sh) работает гораздо быстрее, так как не выполняет полную выгрузку и загрузку данных системного каталога.

Скрипт inplace_upgrade.sh#

Обновление начинается с актуализации номера версии системного каталога в global/pg_control и переименования каталогов с пользовательскими табличными пространствами PG_<MAJOR_POSTGRESQL>_<CATALOG_VERSION_NO>. Далее скрипт обновляет системный каталог для всех баз данных СУБД в два этапа:

  • На первом этапе скрипт запускается последовательно во всех базах данных с функцией ROLLBACK.

    • Если тестовая загрузка проходит успешно, начинается второй этап загрузки скриптов, также последовательно во всех базах данных, но уже без функции ROLLBACK.

    • Если тестовая загрузка завершается с ошибкой, второй этап не запускается, и скрипт выполняет откат версии каталога и возвращает названия каталогов с табличными пространствами.

      • Если второй этап загрузки завершается с ошибкой, скрипт также выполняет откат версии каталога и возвращает названия каталогов с табличными пространствами.

При вызове скрипта inplace_upgrade.sh используются ключи:

  • Обязательные ключи:

    • -s | --utildir - директория со скриптом inplace_upgrade.sh, утилитой update_catalog_version и папкой sql_upgrade_6xx, содержащей SQL-скрипты для обновления pg_catalog. SQL-скрипты пишутся разработчиками в соответствии изменениями в pg_catalog, которые вносят их доработки. В случае стандартной установки данный параметр имеет значение installer/utilities/pg_inplace_upgrade;

    • -d | --pgdatadir - путь к директории с данными (обычно имеет такое же значение, как и $PGDATA);

    • -l | --logdir - директория для сохранения логов:

      • логи PostgreSQL, сгенерированные в процессе обновления, записываются в файл postgres_update.log;

      • полный лог работы скрипта обновления inplace_upgrade.sh записываются в файл inplace_upgrade.log;

      • короткий отчет обновления report.log;

    • -h | --host - хост СУБД для подключения pg_dump и psql;

    • -p | --port - порт СУБД для подключения pg_dump, pg_ctl и psql;

    • -u | --user - пользователь СУБД с правами суперпользователя для подключения pg_dump и psql;

    • -n | --old-version - текущая версия продукта (например 6.1.8 для СУБД Pangolin 6.1.8). Формат строки проверяется;

    • -N | --new-version - версия продукта на которую происходит обновление (например 6.4.0 для СУБД Pangolin 6.4.0). Формат строки проверяется;

    • -b | --dbname - имя базы данных для подключения pg_dump и psql (обычно имеет значение postgres);

    • -m | --dumpdir - директория для сохранения дампов (sql-дампы до, после и после отката обновления);

    • -B | --backupdir - директория для сохранения:

      • резервной копии файлов системного каталога;

      • файлов с перечнем таблиц и индексов системного каталога и информацией о соответствующих им файлах;

      • файл user_tbls.txt с директориями пользовательских табличных пространств;

      • файл version.txt с версией PostgreSQL до обновления;

    • -t | --pg_ctldir - директория с исполняемыми файлами утилит pg_ctl и postgres новой версии;

    • -T | --pg_utildir - директория с исполняемыми файлами утилит psql и pg_dump новой версии.

Необязательные ключи:

  • -P | --password - пароль для подключения к СУБД. Если не задан, то пароль берется из переменной окружения PASSWORD;

  • -r | --replica - ключ указывается при запуске утилиты обновления на реплике;

  • -D | --drop_on - флаг, разрешающий операции, запрещенные функцией block_user_data_modification;

  • -k | --test-skip - флаг, устанавливается для отключения запуска тестов системного каталога;

  • -a | --add-tables - флаг для выполнения резервного копирования таблиц pg_largeobject, pg_largeobject_metadata при необходимости;

  • -V | --version - версия утилиты;

  • -? | --help - справка.

Команды:

  • info - команда проверки актуальности обновления и формирования информационных файлов user_tbls.txt, version.txt для последующего запуска обновления;

  • update - команда запуска обновления;

  • reset - команда отката обновления. Откат возможен до первого запуска базы с доступом пользователей.

Требования для запуска#

Пользователь должен иметь права на запуск скрипта обновления inplace_upgrade.sh. Также этот пользователь должен иметь права суперпользователя на доступ к СУБД.

Внимание!

Проверьте, что установленная локаль на узлах соответствует: LANG: en_US.UTF-8, LC_ALL: en_US.UTF-8.

Режимы работы скрипта#

Существуют три режима работы скрипта inplace_upgrade.sh:

  • info - проверка возможности обновления;

  • update - обновление;

  • reset - откат обновления;

inplace_upgrade.sh в режиме info#

В режиме info скрипт осуществляет проверку возможности обновления (наличие утилиты update_catalog_version, папок с SQL-скриптами и всех необходимых прав доступа к ним) и необходимость обновления для новой версии СУБД. Также скрипт создает два файла user_tbls.txt и version.txt. Файл user_tbls.txt содержит список директорий пользовательских табличных пространств для последующего их обновления скриптом в режиме update. Файл version.txt используется для сохранения значения мажорной версии PostgreSQL (MAJOR_POSTGRESQL), которая является элементом пути в пользовательском табличном пространстве PG_<MAJOR_POSTGRESQL>_<CATALOG_VERSION_NO>.

Для кластерной конфигурации необходимо запускать скрипт в режиме info и на мастере и на реплике.

Запуск скрипта осуществляется при включенной СУБД.

Пример запуска скрипта:

./inplace_upgrade.sh -d <pgdata> -l <директория логов> -s <директория с утилитой> -B <директория резервной копии> -p <порт> -h <хост> -u <суперпользователь> -b <база данных для подключения по умолчанию> -n  <текущая версия продукта> -N <новая версия продукта> info --t <директория утилит postgres and pg_ctl> -T <директория утилит psql and pg_dump>
Ограничения#

Необходим доступ к папке sql_upgrade_6xx, ее внутренним папкам и доступ на чтение ко всем файлам внутри папок для администратора СУБД, запускающего утилиту. СУБД должна быть запущена со старыми бинарными файлами.

Коды возврата#

Предусмотренные коды:

  • 0 - вызов утилиты требуется;

  • 1 - провал запуска утилиты, утилита не может быть запущена корректно;

  • 2 - вызов утилиты в режиме update не требуется.

inplace_upgrade.sh в режиме update#

В режиме update скрипт осуществляет обновление таблиц схемы pg_catalog, номера версии системного каталога в pg_control и табличных пространств пользователей.

Пример запуска скрипта:

./inplace_upgrade.sh -d <pgdata> -l <директория логов> -p <порт> -h <хост> -u <суперпользователь> -b <база данных для подключения по умолчанию> -s <директория с утилитой> -m <директория для sql-дампов> -B <директория бэкапа> -n  <текущая версия продукта> -N <новая версия продукта> update -t <директория утилит postgres and pg_ctl> -T <директория утилит psql and pg_dump>

Примечание:

Процесс восстановления СУБД после неудачного окончания работы скрипта в режиме update описан в разделе «Восстановление после неудачного обновления исполняемых файлов».

Ограничения#

Перед запуском обновления СУБД должна быть остановлена. Доступ для обычных пользователей должен быть ограничен, чтобы не было проблем при снятии дампов системных каталогов (не менялись системные таблицы pg_class, pg_type, pg_proc и другие) и последующем их анализе, если потребуется при ручном восстановлении СУБД. Бинарные файлы СУБД должны быть новой версии и должны содержать необходимые системные объекты (если требуется). В противном случае может возникнуть проблема как в примере ниже.

Пример отсутствия функции в ядре (при ее добавлении может быть ошибка в транзакции):

"ERROR: Fail script: ERROR: there is no built-in function named \"<name_function>\"", "CONTEXT: SQL statement \"CREATE FUNCTION pg_catalog.name_function()"
Коды возврата#

Предусмотренные коды:

  • 0 - успех;

  • 1 - провал запуска утилиты, требуется ручное восстановление СУБД;

  • 4 - требуется восстановление файлов из резервной копии системного каталога, также необходим откат исполняемых файлов СУБД к исходной версии;

  • 5 - изменения не применились, необходим откат исполняемых файлов СУБД к исходной версии.

inplace_upgrade.sh в режиме reset#

В режиме reset скрипт осуществляет восстановление pg_catalog и номера каталога СУБД.

Пример запуска скрипта:

./inplace_upgrade.sh -d <pgdata> -l <директория логов> -p <порт> -h <хост> -u <суперпользователь> -b <база данных для подключения по умолчанию> -s <директория с утилитой> -m <директория для sql-дампов> -B <директория бэкапа> reset -t <директория утилит postgres and pg_ctl> -T <директория утилит psql and pg_dump>

Примечание:

Процесс восстановления СУБД после неудачного окончания работы скрипта в режиме reset описан в разделе «Ручное восстановление системного каталога СУБД».

Ограничения#

Должен быть доступ к папке, указанной в --backupdir и доступ на чтение к файлам, внутри этой папки.

Перед запуском восстановления СУБД должна быть остановлена. Доступ для обычных пользователей должен быть ограничен.

Коды возврата#
  • 0 - успех;

  • 1 - провал запуска утилиты, требуется ручное восстановление СУБД.

Утилита update_catalog_version#

Пользователь должен иметь права на исполнение утилиты update_catalog_version, запускающим обновление.

Утилита update_catalog_version производит сравнение версии текущего каталога, считанной из global/pg_control (расположение файла по умолчанию $PGDATA/global/pg_control), с версией на которую планируется обновление. Также проверяется наличие файла резервной копии pg_control.bak в директории указанной в --backup, если файл есть (возможно происходит повторный запуск), то утилита заканчивает свою работу, пишет сообщение с описанием ошибки и возвращает код ошибки 1. В случае совпадения проверяется отличие номера текущей версии от версии на которую необходимо производить обновление, если они совпали, то обновление версии не происходит и утилита заканчивает свою работу, возвращается код 2. Если новая версия больше текущей версии, то делается резервная копия pg_control.bak (файл будет располагаться в --backup) и происходит обновление версии каталога в файле pg_control. В случае успешного обновления утилита заканчивает свою работу и возвращает код 0. Если произошла ошибка при обновлении версии, то происходит восстановление pg_control и завершение работы утилиты с кодом 1. Если восстановить старый pg_control из резервной копии не удалось, то утилита возвращает код 3. Если новая версия меньше текущей версии каталога, то обновление не производится, пишется сообщение об ошибке и возвращается код 1. Если необходимо понизить версию, то нужно использовать ключ --force (-f), с этим ключом не производится проверка повышения версии каталога, также не производится проверка наличия файла резервной копии и он перезаписывается. Если не сделать обновление версии каталога, то бинарные файлы СУБД не смогут запуститься.

Ключи утилиты update_catalog_version:

  • -D, --pgdatadir - директория с СУБД (PGDATA);

  • -b, --backupdir - директория для сохранения резервной копии файла pg_control;

  • -C, --catalog-version-new - новая версия системного каталога (на которую происходит обновление) (CATALOG_VERSION_NO);

  • -c, --catalog-version-old - старая версия системного каталога (с которой происходит обновление) (CATALOG_VERSION_NO);

  • -f, --force - флаг принудительной смены версии;

  • -y, --dry-run - флаг проверки соответствия версии текущего каталога обновлению, без его изменения;

  • -V, --version - флаг печати версии утилиты;

  • -?, --help - флаг печати справки об утилите.

Режимы работы утилиты update_catalog_version#
Работа утилиты без –force#

Утилита запускается с ключами: update_catalog_version catalog_version_no_old -C catalog_version_no_new -b <dir_backup> -D <PGDATA>.

Ход работы:

  1. Проверяется что заданы все основные параметры (-D, -c, -C, -b), если хотя бы один не задан, то пишется сообщение в stdout и возвращается код ошибки 1.

  2. Проверяется что PostgreSQL не запущен. Если запущен, то выдается сообщение об ошибке и возвращается код возврата 1.

  3. Проверяется что версия каталога не понижается, если версия понижается, то выдается сообщение об ошибке и возвращается код возврата 1.

  4. Проверяется наличие файла резервной копии pg_control.bak, если он есть, то выдается сообщение об ошибке и возвращается код возврата 1. Это нужно для исключения повторного ошибочного обновления.

  5. Проверяется соответствие версии текущего каталога, версии переданной с ключом -c, если версии разные, то выдается сообщение об ошибке и возвращается код возврата 1.

  6. Проверяется соответствие версии текущего каталога, версии переданной с ключом , если версии одинаковые, то выдается сообщение соответствия версий и возвращается код 2.

  7. Формируется файл резервной копии pg_control.bak. Если файл не сформировался, то выдается сообщение об ошибке и возвращается код возврата 1.

  8. Обновляется версия системного каталога в файле pg_control. Если произошла ошибка, то происходит восстановление файла pg_control из резервной копии, выдается сообщение об ошибке и возвращается код 1. Если восстановление файла pg_control не получилось, то выдается сообщение об ошибке и выдается код 3.

Работа утилиты с –force#

Утилита запускается с ключами: update_catalog_version catalog_version_no_old -C catalog_version_no_new -b <dir_backup> -D <PGDATA> -f.

Ход работы:

  1. Проверяется что заданы все основные параметры (-D, -c, -C, -b), если хотя бы один не задан, то пишется сообщение в stdout и возвращается код ошибки 1.

  2. Проверяется что PostgreSQL не запущен. Если запущен, то выдается сообщение об ошибке и возвращается код возврата 1.

  3. Проверяется соответствие версии текущего каталога, версии переданной с ключом -c, если версии разные, то выдается сообщение об ошибке и возвращается код возврата 1.

  4. Проверяется соответствие версии текущего каталога, версии переданной с ключом , если версии одинаковые, то выдается сообщение соответствия версий и возвращается код 2.

  5. Формируется файл бэкапа pg_control.bak. Если файл не сформировался, то выдается сообщение об ошибке и возвращается код возврата 1.

  6. Обновляется версия системного каталога в файле pg_control. Если произошла ошибка, то происходит восстановление файла pg_control из резервной копии, выдается сообщение об ошибке и возвращается код 1. Если восстановление файла pg_control не получилось, то выдается сообщение об ошибке и выдается код 3.

Работа утилиты с –dry-run#

Утилита запускается с ключами: update_catalog_version catalog_version_no_old -C catalog_version_no_new -b <dir_backup> -D <PGDATA> -y.

Ход работы:

  1. Проверяется что заданы все основные параметры (-D, -c, -C, -b), если хотя бы один не задан, то пишется сообщение в stdout и возвращается код ошибки 1.

  2. Проверяется что версия каталога не понижается. Если версия понижается, то выдается сообщение об ошибке и возвращается код возврата 1.

  3. Проверяется наличие файла резервной копии pg_control.bak. Если он есть, то выдается сообщение об ошибке и возвращается код возврата 1. Это нужно для исключения повторного ошибочного обновления.

  4. Проверяется соответствие версии текущего каталога, версии переданной с ключом -c. Если версии разные, то выдается сообщение об ошибке и возвращается код возврата 1.

  5. Проверяется соответствие версии текущего каталога версии переданной с ключом . Если версии одинаковые, то выдается сообщение соответствия версий и возвращается код 2, если версии разные, то выдается сообщение о возможности обновления и возвращается код 0.

Ограничения#

Перед запуском update_catalog_version, для изменения версии каталога, СУБД должна быть остановлена. Доступ для обычных пользователей должен быть ограничен.

Коды возврата утилиты update_catalog_version#

Предусмотренные коды:

  • 0 - успешное завершения обновления версии каталога (в режиме проверки --dry-run данный код означает, что обновление возможно);

  • 1 - ошибка обновления версии каталога, файл pg_control не поменялся;

  • 2 - обновление каталога не требуется (в режиме проверки --dry-run данный код означает, что обновление не требуется);

  • 3 - ошибка обновления версии каталога, файл pg_control поменялся, требуется ручное восстановление из файла резервной копии pg_control.bak.

Правила написания SQL-скриптов для обновления системных данных каталога#

Разработчикам необходимо придерживаться правил написания SQL-скриптов обновления:

  1. SQL-скрипты должны иметь права на чтение пользователем, запускающим обновление.

  2. На данный момент возможно добавление в системный каталог объектов: functions, view, type. Добавление других объектов возможно, но на текущий момент это не проверялось.

  3. Системные объекты (например, функции), с OID<10000 добавляются по заданному системному OID. Если задаваемый OID уже занят другим системным объектом, то возникнет конфликт, и транзакция откатится с указанием конфликта OID.

  4. Для некоторых системных объектов добавление по заданному OID невозможно, так как они инициализируются в СУБД при вызове initdb, а при обновлении данной утилиты должны добавляться по свободным системных OID в диапазоне 12000<=OID<16384, чтобы не вызвать конфликтов с существующими системными объектами СУБД.

Написание скриптов должно проводиться строго в соответствии с описанными далее шаблонам, отклонение от шаблонов может привести к порче объектов системного каталога.

При создании новых объектов системного каталога необходимо точно знать какие таблицы системного каталога задействуются при создании этого объекта и учесть это в скрипте обновления.

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

Примечание:

При создании новой функции обновляется таблица pg_proc системного каталога.

Шаблоны#

SQL-скрипты для обновления пишутся разработчиками по шаблонам, описанным в данном разделе.

Для функций по системному OID<10000#

Условия проверки функции перед ее созданием пишутся разработчиками в соответствии с их задачами. Удаление функции не рекомендуется, так как OID системных функций находится в диапазоне от 1 до 9999, а в данном диапазоне удаление функции производится без учета зависимостей, относящихся к этой функции.

Примечание:

Для функций добавляемых в исходные файлы:

  • postgresql/src/include/catalog/pg_proc.dat;

  • postgresql/src/include/catalog/pg_proc.pangolin.dat;

  • postgresql/src/include/catalog/pg_proc.xid64.dat.

OID задается в диапазоне от 0 до 9999 включительно.

Шаблон:

-- Создать функцию, если ее нет по системному OID system_oid < 10000
IF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_proc WHERE
proname = 'my_function') THEN
    -- Создание новой системной функции 
    --  Вызов функции binary_upgrade_set_next_pg_proc_oid для задания конкретного OID системного каталога
    PERFORM pg_catalog.binary_upgrade_set_next_pg_proc_oid('system_oid'::pg_catalog.oid);
    CREATE FUNCTION pg_catalog.my_function(text, bool, int4) -- Здесь присутствует список аргументов функции, который создал разработчик
        RETURNS void
        LANGUAGE internal
        STABLE PARALLEL SAFE STRICT
    AS $function$my_function$function$;
 
    -- Обновление параметров в таблице pg_catalog.pg_proc (если требуется)
    UPDATE pg_catalog.pg_proc AS pp SET
    -- Числовые аргументы типов для поля proallargtypes можно узнать с помощью запроса "select oid from pg_type where typname = '<имя типа>';".
    proallargtypes = '{25,16,23}', --{text, bool, int4}
    -- Указать тип аргументов как входных и выходных
    proargmodes = '{i,i,o}',
    -- Установить права на выполнения функции (если требуется)
    proacl = '{postgres=X/postgres}'
    WHERE pp.oid = (select oid from pg_catalog.pg_proc where proname = 'my_function');
 
    -- распечатка состояния "после изменений" из затронутых таблиц системного каталога
    -- (эта информация может понадобиться позже для диагностики или восстановления)
    SELECT rtrim(ltrim(replace(pg_catalog.pg_proc::text, ',', '|'), '('), ')') INTO msg FROM pg_catalog.pg_proc WHERE proname = 'my_function';
    RAISE NOTICE 'CREATE FUNCTION pg_catalog.my_function: %', quote_ident(msg);
ELSE
    -- Если функция уже присутствует в системном каталоге, то выдавать ошибку, так как неправильно установлены параметры обновления для текущей и новой версий СУБД
    RAISE EXCEPTION 'The function "my_function" already exists, there may be a product version error.';
END IF;
 
-- Проверка наличия функции после обновления
IF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_proc WHERE proname = 'my_function') THEN
    RAISE EXCEPTION 'The function "my_function" does not exist.';
END IF;

Для функций по системному 12000<=OID<116384#

Примечание:

Для функций добавляемых в исходный файл postgresql/src/backend/catalog/system_functions.sql OID задается в диапазоне от 12000 до 16383 включительно.

При создании нового представления обновляются таблицы pg_class, pg_type, pg_rewrite системного каталога, которые необходимо указать в соответствии с описанным далее шаблоном. Также меняется ряд таблиц системного каталога, которые не нужно явно указывать в SQL-скрипте обновления, но необходимо учесть в тесте системного каталога базы данных после обновления.

Шаблон:

-- Создать функцию, если ее нет по системному OID 12000 <= system_oid < 16384
IF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_proc WHERE
proname = 'my_function') THEN
    -- Вызов функции binary_upgrade_set_next_free_pg_proc_oid для задания динамического OID системного каталога в диапазоне от 12000 до 16383
    PERFORM pg_catalog.binary_upgrade_set_next_free_pg_proc_oid();
 
    -- Создание новой системной функции 
    CREATE FUNCTION pg_catalog.my_function(text, bool, int4) -- Здесь присутствует список аргументов функции, который создал разработчик
        RETURNS void
        LANGUAGE internal
        STABLE PARALLEL SAFE STRICT
    AS $function$my_function$function$;
 
    -- Обновление параметров в таблице pg_catalog.pg_proc (если требуется)
    UPDATE pg_catalog.pg_proc AS pp SET
    -- Числовые аргументы типов для поля proallargtypes можно узнать с помощью запроса "select oid from pg_type where typname = '<имя типа>';".
    proallargtypes = '{25,16,23}', --{text, bool, int4}
    -- Указать тип аргументов как входных и выходных
    proargmodes = '{i,i,o}',
    -- Установить права на выполнения функции (если требуется)
    proacl = '{postgres=X/postgres}'
    WHERE pp.oid = (select oid from pg_catalog.pg_proc where proname = 'my_function');
 
    -- распечатка состояния "после изменений" из затронутых таблиц системного каталога
    -- (эта информация может понадобиться позже для диагностики или восстановления)
    SELECT rtrim(ltrim(replace(pg_catalog.pg_proc::text, ',', '|'), '('), ')') INTO msg FROM pg_catalog.pg_proc WHERE proname = 'my_function';
    RAISE NOTICE 'CREATE FUNCTION pg_catalog.my_function: %', quote_ident(msg);
ELSE
    -- Если функция уже присутствует в системном каталоге, то выдавать ошибку, так как неправильно установлены параметры обновления для текущей и новой версий СУБД
    RAISE EXCEPTION 'The function "my_function" already exists, there may be a product version error.';
END IF;
 
-- Проверка наличия функции после обновления
IF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_proc WHERE proname = 'my_function') THEN
    RAISE EXCEPTION 'The function "my_function" does not exist.';
END IF;

Для представления по системному 12000<=OID<16384#

Примечание:

Для функций добавляемых в исходный файл postgresql/src/backend/catalog/system_views.sql OID задается в диапазоне от 12000 до 16383 включительно.

Условия проверки представления перед его созданием пишутся разработчиками в соответствии с их задачами. Удаление представления или изменение не рекомендуется, так как будут утеряны предоставленные на них права у пользователей (если они присутствовали), а также многие другие зависимости.

Шаблон:

-- Проверка наличия представления
IF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_class WHERE
relname = 'my_view') THEN
    -- Вызываются функции binary_upgrade для записи объектов представления по системным OID с 12000 до 16833
    PERFORM pg_catalog.binary_upgrade_set_next_free_array_pg_type_oid(); -- Устанавливается свободный OID для array_pg_type, необходимого для my_view, в таблице pg_catalog.pg_type
    PERFORM pg_catalog.binary_upgrade_set_next_free_pg_type_oid();       -- Устанавливается свободный OID для pg_type, необходимого для my_view, в таблице pg_catalog.pg_type
    PERFORM pg_catalog.binary_upgrade_set_next_free_heap_pg_class_oid(); -- Устанавливается свободный OID для pg_class, необходимого для my_view, в таблице pg_catalog.pg_class
    PERFORM pg_catalog.binary_upgrade_set_next_free_pg_rewrite_oid();    -- Устанавливается свободный OID для pg_rewrite, необходимого для my_view, в таблице pg_catalog.pg_rewrite
 
    -- Создание самого представления
    CREATE VIEW my_view AS
        SELECT 1;
     
    -- распечатка состояния "после изменений" из затронутых таблиц системного каталога
    -- (эта информация может понадобиться позже для диагностики или восстановления)
    -- Замечание. Описание представления содержится в нескольких таблицах каталога - pg_class, pg_type, pg_rewrite, pg_depend и (возможно) других.
    -- Сложно описать связи между ними заранее, поэтому их анализ оставляем на усмотрение разработчиков.
    -- В данном предполагаем, связи будут установлены корректно автоматически.
    -- В дальнейшей работе можно провести такой анализ.
    -- В идеале следует показывать все затронутые таблицы системного каталога после создания представления.
 
    SELECT rtrim(ltrim(replace(pg_catalog.pg_class::text, ',', '|'), '('), ')') INTO msg FROM pg_catalog.pg_class WHERE relname = 'my_view';
    RAISE NOTICE 'CREATE VIEW pg_catalog.my_view: %', quote_ident(msg);
ELSE
    -- Если представление уже присутствует в системном каталоге, то выдавать ошибку, так как неправильно установлены параметры обновления для текущей и новой версий СУБД
    RAISE EXCEPTION 'The view "my_view" already exists, there may be a product version error.';
END IF;
 
-- Проверка что представление добавилось
IF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_class WHERE relname = 'my_view') THEN
    RAISE EXCEPTION 'The view "my_view" does not exist.';
END IF;

Для типов по системному 12000<=OID<116384#

Примечание:

При создании нового типа по OID от 12000 до 16393 обновляются таблицы pg_class, pg_type и pg_rewrite системного каталога, которые необходимо указать в соответствии с описанным далее шаблоном. Также меняется ряд таблиц системного каталога, которые не нужно явно указывать в SQL-скрипте обновления, но необходимо учесть в тесте системного каталога базы данных после обновления.

Важная информация:

Добавление типов не проверялось на объектах системного каталога, существующего в Pangolin версии 6.4.0. Добавление системных типов по OID от 0 до 9999 включительно производится в файлы:

  • postgresql/src/include/catalog/pg_type.dat

  • postgresql/src/include/catalog/pg_type.xid64.dat

Механизм добавления схож с добавлением новых функций по 0 до 9999 включительно в таблицу pg_type.

Далее рассмотрен пример добавления системного типа по OID от 12000 до 16383 включительно.

Условия проверки типа перед его созданием пишутся разработчиками в соответствии с их задачами. Удаление типа или его изменение не рекомендуется, так как будут утеряны зависимости, связанные с этим типом.

Шаблон:

-- Проверка наличия типа
IF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_type WHERE
typname = 'my_type') THEN
    -- Вызываются функции binary_upgrade для записи объектов представления по системным OID с 12000 до 16833
    PERFORM pg_catalog.binary_upgrade_set_next_free_array_pg_type_oid(); -- Устанавливается свободный OID для array_pg_type, необходимого для my_view, в таблице pg_catalog.pg_type
    PERFORM pg_catalog.binary_upgrade_set_next_free_pg_type_oid();       -- Устанавливается свободный OID для pg_type, необходимого для my_view, в таблице pg_catalog.pg_type
    PERFORM pg_catalog.binary_upgrade_set_next_free_heap_pg_class_oid(); -- Устанавливается свободный OID для pg_class, необходимого для my_view, в таблице pg_catalog.pg_class
    PERFORM pg_catalog.binary_upgrade_set_next_free_pg_rewrite_oid();    -- Устанавливается свободный OID для pg_rewrite, необходимого для my_view, в таблице pg_catalog.pg_rewrite
 
    -- Создание самого типа
    CREATE TYPE my_type AS (f1 int, f2 text);
 
    -- распечатка состояния "после изменений" из затронутых таблиц системного каталога
    -- (эта информация может понадобиться позже для диагностики или восстановления)
    -- Сложно описать связи между ними заранее, поэтому их анализ оставляем на усмотрение разработчиков.
    -- В данном предполагаем, связи будут установлены корректно автоматически.
    -- В дальнейшей работе можно провести такой анализ.
    -- В идеале следует показывать все затронутые таблицы системного каталога после создания представления.   
 
SELECT rtrim(ltrim(replace(pg_catalog.pg_type::text, ',', '|'), '('), ')') INTO msg FROM pg_catalog.pg_type WHERE typname = 'my_type';
    RAISE NOTICE 'CREATE VIEW pg_catalog.my_type: %', quote_ident(msg);
ELSE
    -- Если тип уже присутствует в системном каталоге, то выдавать ошибку, так как неправильно установлены параметры обновления для текущей и новой версий СУБД
    RAISE EXCEPTION 'The type "my_type" already exists, there may be a product version error.';
END IF;
 
-- Проверка что тип добавился
IF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_type WHERE typname = 'my_type') THEN
    RAISE EXCEPTION 'The type "my_type" does not exist.';
END IF;

Внимание!

Изменение или удаление уже существующих объектов является очень опасной операцией с системным каталогом, так как может привезти к трудновосстановимой или безвозвратной потере данных, таких как назначение прав, потеря зависимостей и т.д. Любые подобные операции должны дополнительно тестироваться и проходить дополнительное ревью специалистами L3.

Для изменения текущих объектов в системном каталоге используйте функцию UPDATE:

-- Изменить функцию
IF EXISTS (SELECT 1 FROM pg_catalog.pg_proc WHERE
proname = 'my_function') THEN
    -- распечатка состояния "до изменений" из затронутых таблиц системного каталога
    -- (эта информация может понадобиться позже для диагностики или восстановления)
    SELECT rtrim(ltrim(replace(pg_catalog.pg_proc::text, ',', '|'), '('), ')') INTO msg FROM pg_catalog.pg_proc WHERE proname = 'my_function';
    RAISE NOTICE 'CREATE VIEW pg_catalog.my_function: %', quote_ident(msg); 
 
    -- Обновление параметров в таблице pg_catalog.pg_proc
    -- Необходимо проанализировать изменяемые значения на предмет порчи текущих характеристик объекта, например ACL(привилегии)
    UPDATE pg_catalog.pg_proc AS pp SET
    proallargtypes = '{25,16,23}', --{text, bool, int4}
    proargmodes = '{i,i,o}',
    proacl = '{postgres=X/postgres}'
    WHERE pp.oid = (select oid from pg_catalog.pg_proc where proname = 'my_function');
 
    -- распечатка состояния "после изменений" из затронутых таблиц системного каталога
    -- (эта информация может понадобиться позже для диагностики или восстановления)
    SELECT rtrim(ltrim(replace(pg_catalog.pg_proc::text, ',', '|'), '('), ')') INTO msg FROM pg_catalog.pg_proc WHERE proname = 'my_function';
    RAISE NOTICE 'CREATE VIEW pg_catalog.my_function: %', quote_ident(msg);
ELSE
    -- Если функция отсутствует в системном каталоге, то выдавать ошибку, так как неправильно установлены параметры обновления для текущей и новой версий СУБД
    RAISE EXCEPTION 'The function "my_function" does not exists, there may be a product version error.';
END IF;

Для удаления объектов системного каталога или их пересоздания запустите утилиту с флагом --drop-on, тогда функции DROP, ALTER, REPLAСE и DELETE не вызовут ошибку в скриптах обновления и изменения применятся:


-- Удалить функцию
IF EXISTS (SELECT 1 FROM pg_catalog.pg_proc WHERE
proname = 'my_function') THEN
    -- распечатка состояния "до изменений" из затронутых таблиц системного каталога
    -- (эта информация может понадобиться позже для диагностики или восстановления)
    SELECT rtrim(ltrim(replace(pg_catalog.pg_proc::text, ',', '|'), '('), ')') INTO msg FROM pg_catalog.pg_proc WHERE proname = 'my_function';
    RAISE NOTICE 'CREATE VIEW pg_catalog.my_function: %', quote_ident(msg); 
     
    --  Удаление функции
   DROP FUNCTION my_function;
END IF;
 
-- Проверка отсутствия функции
IF EXISTS (SELECT 1 FROM pg_catalog.pg_proc WHERE
proname = 'my_function') THEN
    RAISE EXCEPTION 'The function "my_function" is exists.';
END IF;