Инструмент 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>.
Ход работы:
Проверяется что заданы все основные параметры (
-D,-c,-C,-b), если хотя бы один не задан, то пишется сообщение вstdoutи возвращается код ошибки1.Проверяется что PostgreSQL не запущен. Если запущен, то выдается сообщение об ошибке и возвращается код возврата
1.Проверяется что версия каталога не понижается, если версия понижается, то выдается сообщение об ошибке и возвращается код возврата
1.Проверяется наличие файла резервной копии
pg_control.bak, если он есть, то выдается сообщение об ошибке и возвращается код возврата1. Это нужно для исключения повторного ошибочного обновления.Проверяется соответствие версии текущего каталога, версии переданной с ключом
-c, если версии разные, то выдается сообщение об ошибке и возвращается код возврата1.Проверяется соответствие версии текущего каталога, версии переданной с ключом
-С, если версии одинаковые, то выдается сообщение соответствия версий и возвращается код2.Формируется файл резервной копии
pg_control.bak. Если файл не сформировался, то выдается сообщение об ошибке и возвращается код возврата1.Обновляется версия системного каталога в файле
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.
Ход работы:
Проверяется что заданы все основные параметры (
-D,-c,-C,-b), если хотя бы один не задан, то пишется сообщение вstdoutи возвращается код ошибки1.Проверяется что PostgreSQL не запущен. Если запущен, то выдается сообщение об ошибке и возвращается код возврата
1.Проверяется соответствие версии текущего каталога, версии переданной с ключом
-c, если версии разные, то выдается сообщение об ошибке и возвращается код возврата1.Проверяется соответствие версии текущего каталога, версии переданной с ключом
-С, если версии одинаковые, то выдается сообщение соответствия версий и возвращается код2.Формируется файл бэкапа
pg_control.bak. Если файл не сформировался, то выдается сообщение об ошибке и возвращается код возврата1.Обновляется версия системного каталога в файле
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.
Ход работы:
Проверяется что заданы все основные параметры (
-D,-c,-C,-b), если хотя бы один не задан, то пишется сообщение вstdoutи возвращается код ошибки1.Проверяется что версия каталога не понижается. Если версия понижается, то выдается сообщение об ошибке и возвращается код возврата
1.Проверяется наличие файла резервной копии
pg_control.bak. Если он есть, то выдается сообщение об ошибке и возвращается код возврата1. Это нужно для исключения повторного ошибочного обновления.Проверяется соответствие версии текущего каталога, версии переданной с ключом
-c. Если версии разные, то выдается сообщение об ошибке и возвращается код возврата1.Проверяется соответствие версии текущего каталога версии переданной с ключом
-С. Если версии одинаковые, то выдается сообщение соответствия версий и возвращается код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-скриптов обновления:
SQL-скрипты должны иметь права на чтение пользователем, запускающим обновление.
На данный момент возможно добавление в системный каталог объектов: functions, view, type. Добавление других объектов возможно, но на текущий момент это не проверялось.
Системные объекты (например, функции), с
OID<10000добавляются по заданному системному OID. Если задаваемый OID уже занят другим системным объектом, то возникнет конфликт, и транзакция откатится с указанием конфликта OID.Для некоторых системных объектов добавление по заданному 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.sqlOID задается в диапазоне от 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.sqlOID задается в диапазоне от 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.datpostgresql/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;