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

Утилита Pangolin Inplace Upgrade. Утилита для обновления системных каталогов Pangolin без изменения структуры данных

Утилита Pangolin Inplace Upgrade отдельный компонент, упакованный в стандартный rpm/deb-пакет. Такой подход обеспечивает удобную эксплуатацию утилиты, а также ее сопровождение.

Расположение файлов утилиты​

Файлы утилиты размещаются в директории /opt/pangolin-utilities/bin. Права на каталоги и файлы при установке rpm/deb-пакета устанавливаются следующим образом:

  • каталоги /opt/pangolin-utilities/*: владелец и группа — postgres, права — 700.
  • файлы /opt/pangolin-utilities/*: владелец и группа — postgres, права — 400.
  • /opt/pangolin-utilities/bin/pg_inplace_upgrade/inplace_upgrade.sh: владелец и группа — postgres, права — 500.
  • /opt/pangolin-utilities/bin/pg_inplace_upgrade/update_catalog_version: владелец и группа — postgres, права — 500.
  • /opt/pangolin-utilities/bin/pg_inplace_upgrade/verify_pg_catalog.py: владелец и группа — postgres, права — 500.
Сведения

Если во время установки пакета pangolin-inplace-upgrade учетная запись пользователя postgres отсутствует, она будет создана автоматически.

Сведения

В процессе сборки rpm/deb-пакета включается автоматический сбор всех runtime-зависимостей на этапе компиляции. Это позволяет гарантировать работоспособность утилиты на целевых системах без необходимости дополнительной установки пакетов. Списки необходимых runtime-зависимостей приведены в разделе «Список пакетных зависимостей, необходимых для сопровождения Pangolin».

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

Для выполнения задачи обновления каталога используется утилита pangolin-inplace-upgrade, в основе скрипт inplace_upgrade.sh, который применяет:

Скрипт обновления, и все что он использует, устанавливается в директорию /opt/pangolin-utilities/bin/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):

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

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

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

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

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

    • -s | --utildir – директория со скриптом inplace_upgrade.sh, утилитой update_catalog_version и папкой sql_upgrade_6xx, содержащей SQL-скрипты для обновления pg_catalog. SQL-скрипты пишутся разработчиками в соответствии изменениями в pg_catalog, которые вносят их доработки. В случае стандартной установки данный параметр имеет значение /opt/pangolin-utilities/bin/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 – версия утилиты;

  • -C | --compress – степень сжатия данных резервной копии и дампов системного каталога алгоритмом gzip. Может иметь значение от 1 до 9, которое обозначает степень сжатия. Если ключ -C отсутствует, то сжатие не выполняется. Ключ необходимо указывать в режиме info.

  • -M | --dump-null – флаг, указывающий формировать дампы, после обновления системного каталога, без сохранения их на диске. Ключ необходимо указывать в режиме info.

  • -F | --extra-free-size – флаг для задания дополнительного запаса свободного места на диске (в МБ). Значение по-умолчанию 1024 МБ.

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

    • бэкапа;
    • суммарного размера дампов системного каталога всех баз данных до обновления системного каталога;
    • суммарного размера дампов системного каталога всех баз данных после его обновления.
  • -? | --help – справка.

примечание

Скрипт inplace_upgrade.sh создает информационные и служебные файлы, которые не учитываются при оценке необходимого свободного пространства для бэкапа и дампов в режиме info. Накладные расходы зависят от количества баз данных и размера их системных каталогов (в среднем не превышает 100 КБ на одну базу данных, но в редких случаях может быть больше, например если системный каталог сильно превышает 1ГБ). Значения по умолчанию в 1024 МБ должно быть достаточно для того что бы хватило дискового пространства для служебных файлов в большинстве случаев, но флаг -F | --extra-free-size позволяет в случае необходимости скорректировать оценку дополнительного свободного пространства.

Команды:

  • 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 – содержит список директорий пользовательских табличных пространств для последующего их обновления скриптом в режиме update;
  • version.txt – используется для сохранения значения мажорной версии PostgreSQL (MAJOR_POSTGRESQL), которая является элементом пути в пользовательском табличном пространстве PG_<MAJOR_POSTGRESQL>_<CATALOG_VERSION_NO>;
  • size_backups.txt – содержит информацию о занимаемом месте бэкапом;
  • size_dumps.txt – содержит информацию о занимаемом месте дампами.

Для кластерной конфигурации необходимо запускать скрипт в режиме 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>
Ограничения​

Упаковка файлов утилиты в rpm/deb-пакет производится без предварительного этапа компиляции.

Необходим доступ к папке 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 производит сравнение версии текущего каталога в файле pg_control (его расположение по-умолчанию: $PGDATA/global/pg_control) и версии, на которую планируется обновление. Также утилита проверяет наличие файла резервной копии pg_control.bak в директории, указанной через опцию --backup. Если такой файл уже существует (что может свидетельствовать о повторном запуске), утилита завершает работу, выводит сообщение об ошибке и возвращает код ошибки 1.

В случае совпадения текущей версии каталога с целевой версией обновление не производится, и утилита завершает работу с кодом 2. Если новая версия больше текущей, выполняется резервное копирование pg_control в файл pg_control.bak (расположенный в директории, указанной в --backup), после чего обновляется версия каталога в файле pg_control. При успешном обновлении утилита завершает работу и возвращает код 0.

Если во время обновления версии возникает ошибка, восстанавливается предыдущая версия каталога из резервной копии, и утилита завершает работу с кодом 1. В случае невозможности восстановления из резервной копии возвращается код 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 -c <catalog_version_number_old> -C <catalog_version_number_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. Проверяется соответствие версии текущего каталога версии, переданной с ключом -C. Если версии совпадают, выводится сообщение соответствия версий и возвращается код 2.
  7. Формируется файл резервной копии pg_control.bak. Если файл не был сформирован, выводится сообщение об ошибке и возвращается код 1.
  8. Обновляется версия системного каталога в файле pg_control. Если произойдет ошибка, выполняется восстановление файла pg_control из резервной копии, выводится сообщение об ошибке и возвращается код 1. Если восстановление файла pg_control не удается, выводится сообщение об ошибке и возвращается код 3.
примечание

В этом режиме обеспечивается максимальная защита: понижение версии запрещено, повторные запуски блокируются, все ошибки восстанавливают исходное состояние.

Работа утилиты с --force​
update_catalog_version -c <catalog_version_number_old> -C <catalog_version_number_new> -b <dir_backup> -D <PGDATA> -f

Алгоритм:

  1. Проверяется, заданы ли все основные параметры (-D, -c, -C, -b). Если хотя бы один из них не задан, выводится сообщение в stdout и возвращается код 1.
  2. Проверяется, не запущен ли PostgreSQL. Если он запущен, выводится сообщение об ошибке и возвращается код 1.
  3. Проверяется соответствие версии текущего каталога версии, переданной с ключом -c. Если версии не совпадают, выводится сообщение об ошибке и возвращается код 1.
  4. Проверяется соответствие версии текущего каталога версии, переданной с ключом -C. Если версии совпадают, выводится сообщение соответствия версий и возвращается код 2.
  5. Формируется файл резервной копии pg_control.bak. Если файл не был сформирован, выводится сообщение об ошибке и возвращается код возврата 1.
  6. Обновляется версия системного каталога в файле pg_control. Если произойдет ошибка, выполняется восстановление файла pg_control из резервной копии, выводится сообщение об ошибке и возвращается код 1. Если восстановление файла pg_control не удается, выводится сообщение об ошибке и возвращается код 3.
Работа утилиты с --dry-run​
update_catalog_version -c <catalog_version_number_old> -C <catalog_version_number_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. Проверяется соответствие версии текущего каталога версии, переданной с ключом -C. Если версии совпадают, выводится сообщение соответствия версий и возвращается код 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-скрипты для обновления пишутся разработчиками по шаблонам, описанным в данном разделе.

Внимание!

При обновлении системного каталога утилита возвращает стандартные привилегии на следующие функции:

select oid, pronamespace::regnamespace, proname, proacl from pg_proc where oid in (5556, 8668, 9816, 9817, 9875,8669);
oid | pronamespace | proname | proacl
------+--------------+-------------------------------------+--------
5556 | pg_catalog | stirhandler |
8668 | pg_catalog | get_last_wal_key_rotation_time |
8669 | pg_catalog | get_last_master_key_rotation_time |
9816 | pg_catalog | pg_available_extensions_ext |
9817 | pg_catalog | pg_available_extension_versions_ext |
9875 | pg_catalog | pangolin_check_enterprise_license |

Это связано с тем, что они могли получить отличные от стандартных привилегии в случае наличия в БД дефолтных привилегий (DEFAULT PRIVILEGES).

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

Для функций​

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

    • 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 системных функций находится в диапазоне от 1 до 9999, а в данном диапазоне удаление функции производится без учета зависимостей, относящихся к этой функции.

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

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

Шаблон:

-- Создать функцию с OID < 10000,
-- либо 12000 <= OID <= 16383

-- ---------------------------------------------------------
-- select oid, proname from pg_proc where oid = 9308;
-- oid | proname | proacl
-- ------+-----------------------+-----------------------
-- 9308 | pg_integrity_add_file | {postgres=X/postgres}
-- ---------------------------------------------------------

-- 1. Проверка наличия функции

IF NOT (SELECT (COUNT(oid)>0)::boolean FROM pg_catalog.pg_proc WHERE
(pronamespace::regnamespace::name,oid::regprocedure::name)
=
('pg_catalog'::name,'pg_integrity_add_file(text)'::name) -- 'схема.имя_функции(входные_аргументы)'
-- входные аргументы как в запросе CREATE FUNCTIONS, а также в столбце proargtypes
) THEN

-- Проверка, что oid не занят другой функцией
IF (SELECT (COUNT(oid)>0)::boolean FROM pg_catalog.pg_proc WHERE oid=9308) THEN -- oid функции
RAISE EXCEPTION 'The pg_proc.oid=% is occupied by different function: % !','9308','9308'::regprocedure::name; -- oid функции, oid функции
END IF;

-- 2. Создание новой системной функции.

-- Выбор способа создания функции в зависимости от значения oid

-- Вызов функции binary_upgrade_set_next_pg_proc_oid для установки определенного oid в диапазоне от 0 до 10000.
PERFORM pg_catalog.binary_upgrade_set_next_pg_proc_oid('9308'::pg_catalog.oid); -- oid функции

-- Вызов функции 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.pg_integrity_add_file(file_check text) -- CREATE FUNCTION
RETURNS boolean
LANGUAGE internal
STABLE PARALLEL SAFE STRICT
AS $function$IntegrityAddFiles$function$;

-- Проверка наличия созданной функции в каталоге после ее создания
IF NOT (SELECT (COUNT(oid)>0)::boolean FROM pg_catalog.pg_proc WHERE
(oid, pronamespace::regnamespace::name, oid::regprocedure::name)
=
(9308::oid, 'pg_catalog'::name, 'pg_integrity_add_file(text)'::name) -- oid,схема,имя_функции(входные_аргументы)
) THEN
RAISE EXCEPTION 'The function "pg_catalog.pg_integrity_add_file (oid=9308)" is missing after creation.'; -- имя_функции
END IF;

-- 3. Установка привилегий на функцию (proacl в таблице pg_catalog.pg_proc) и других параметров.

-- Требуется (!) явная установка стандартного значения proacl, так как настройки default privileges бд могут привести к отличному значению при создании функции.
-- Формат привилигии (acl): 'роль=привилегии/кем_предоставлены'; для роли public: '=привилегии/кем_предоставлены'
-- Также выполним преобразование acl к типу aclitem для дополнительной проверки ('{pangolin=X/public}'::aclitem[];)
-- Значение 'proacl=NULL' означает привилегии по-умолчанию - все привилегии для владельца (proowner) а также привилегии для PUBLIC (в случае функции).
-- Привилегии по-умолчанию: select acldefault('f',proowner::regrole) from pg_proc where oid = 9308; --> {=X/postgres,postgres=X/postgres}
-- Преобразование 'pg_catalog.pg_integrity_add_file(text)'::regprocedure необходимо, чтобы обновить именно ту функцию, которая была создана выше в схеме pg_catalog и не затронуть другие.
-- В скобках после имени функции указываются входные аргументы, перечисленные в столбце proargtypes таблицы pg_proc. Они соответсвуют параметрам запроса CREATE FUNCTION).

UPDATE pg_catalog.pg_proc AS pp SET
proacl = '{postgres=X/postgres}'::aclitem[] -- стандартные привилегии
-- proallargtypes = '{25}', -- 'select 25::regtype;'-->'text'
-- proargmodes = '{i}',
-- proargnames = '{file_check}',
WHERE pp.oid='pg_catalog.pg_integrity_add_file(text)'::regprocedure::oid; -- схема,имя_функции(входные_аргументы)

-- 4. Проверка состояния "после изменений" из затронутых таблиц системного каталога

-- (эта информация может понадобиться позже для диагностики или восстановления)
SELECT rtrim(ltrim(replace(pg_proc::text, ',', '|'), '('), ')') INTO msg FROM pg_catalog.pg_proc
WHERE oid='pg_catalog.pg_integrity_add_file(text)'::regprocedure::oid; -- схема,имя_функции(входные_аргументы)
RAISE NOTICE 'CREATE FUNCTION pg_catalog.pg_integrity_add_file: %', quote_ident(msg);

-- (также можно получить ddl функции)
-- SELECT regexp_replace(pg_get_functiondef(oid),'[\n\r]+',' ','g') INTO msg FROM pg_catalog.pg_proc
-- WHERE oid='pg_catalog.pg_integrity_add_file(text)'::regprocedure; -- схема,имя_функции(входные_аргументы)
-- RAISE NOTICE 'CREATE FUNCTION pg_catalog.pg_integrity_add_file: %', quote_ident(msg);

ELSE
RAISE EXCEPTION 'The function "pg_catalog.pg_integrity_add_file" already exists in schema pg_catalog with oid: %',
'pg_catalog.pg_integrity_add_file(text)'::regprocedure::oid; -- схема,имя_функции(входные_аргументы)
END IF;

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

примечание

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

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

Шаблон:

-- Создать представление со значениями oid из диапазона: 12000 <= OID < 16383

-- ----------------------------------------------------------------------------------------
-- SELECT oid, relname, relacl FROM pg_class WHERE relname = 'pg_available_extensions_ext';
-- oid | relname | relacl
-- -------+-----------------------------+--------------------------------------------------
-- 12398 | pg_available_extensions_ext | {postgres=arwdDxt/postgres,=r/postgres}
-- ----------------------------------------------------------------------------------------

-- 1. Проверка наличия представления

IF NOT (SELECT (COUNT(oid)>0)::boolean FROM pg_catalog.pg_class WHERE
(relnamespace::regnamespace::name,oid::regclass::name)
=
('pg_catalog'::name,'pg_available_extensions_ext'::name) -- схема, имя_представления

) THEN

-- 2. Создание нового представления

-- Вызываются функции binary_upgrade для записи объектов представления по OID в диапазоне с 12000 до 16833

PERFORM pg_catalog.binary_upgrade_set_next_free_array_pg_type_oid(); -- свободный OID для typarray в таблице pg_type
PERFORM pg_catalog.binary_upgrade_set_next_free_pg_type_oid(); -- свободный OID для pg_type
PERFORM pg_catalog.binary_upgrade_set_next_free_heap_pg_class_oid(); -- свободный OID для pg_class
PERFORM pg_catalog.binary_upgrade_set_next_free_pg_rewrite_oid(); -- свободный OID для pg_rewrite

-- Создание представления

CREATE VIEW pg_catalog.pg_available_extensions_ext AS -- CREATE VIEW
SELECT E.name, E.default_version, X.extversion AS installed_version, E.comment, E.path, E.blocked
FROM pg_available_extensions_ext() AS E
LEFT JOIN pg_extension AS X ON E.name = X.extname;

-- Проверка наличия созданного представления в каталоге после его создания
IF NOT (SELECT (COUNT(oid)>0)::boolean FROM pg_catalog.pg_class WHERE
(relnamespace::regnamespace::name,oid::regclass::name)
=
('pg_catalog'::name,'pg_available_extensions_ext'::name) -- oid,схема,имя_функции(входные_аргументы)
) THEN
RAISE EXCEPTION 'The view "pg_catalog.pg_available_extensions_ext" is missing after creation.'; -- имя_представления
END IF;

-- 3. Установка привилегий на представление

-- Требуется явная установка стандартного значения proacl, так как настройки default privileges БД могут привести к отличному значению.
-- Преобразование 'pg_catalog.pg_available_extensions_ext'::regclass::oid необходимо, чтобы обновить именно то представление, которое было создана выше в схеме pg_catalog и не затронуть другие.

UPDATE pg_catalog.pg_class AS pc SET
relacl = '{postgres=arwdDxt/postgres,=r/postgres}'::aclitem[] -- стандартные привилегии
WHERE pc.oid='pg_catalog.pg_available_extensions_ext'::regclass::oid; -- схема,имя_представления

INSERT INTO pg_catalog.pg_init_privs (objoid, classoid, objsubid, privtype, initprivs)
VALUES (
'pg_catalog.pg_available_extensions_ext'::regclass::oid, -- схема,имя_представления
1259,
0,
'i',
'{postgres=arwdDxt/postgres,=r/postgres}'::aclitem[] -- стандартные первоначальные привилегии
);

-- 4. Проверка состояния "после изменений" из затронутых таблиц системного каталога

-- (эта информация может понадобиться позже для диагностики или восстановления)
-- Замечание. Описание представления содержится в нескольких таблицах каталога - pg_class, pg_type, pg_rewrite, pg_depend и (возможно) других.
-- Сложно описать связи между ними заранее, поэтому их анализ оставляем на усмотрение разработчиков.
-- Здесь предполагаем, связи будут установлены корректно автоматически.
-- В идеале следует представить все затронутые таблицы системного каталога после создания представления.

SELECT rtrim(ltrim(replace(pg_class::text, ',', '|'), '('), ')') INTO msg FROM pg_catalog.pg_class WHERE
oid='pg_catalog.pg_available_extensions_ext'::regclass::oid; -- схема, имя_представления
RAISE NOTICE 'CREATE VIEW pg_catalog.pg_available_extensions_ext: %', quote_ident(msg);

ELSE
RAISE EXCEPTION 'The view "pg_available_extensions_ext" already exists in schema pg_catalog with oid: %',
'pg_catalog.pg_available_extensions_ext'::regclass::oid; -- схема, имя_представления

END IF;

Для типов по системному 12000 &lt;= 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;