Перейти к основному содержимому

Инструмент 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;