Упаковка связанных объектов в расширение

35.15. Упаковка связанных объектов в расширение

Любое полезное расширение для PostgreSQL обычно включает несколько SQL объектов; например, новый тип данных будет требовать новые функции, новые операторы и возможно, новые классы операторов индексирования. Для более лёгкого управления СУБД, все эти объекты полезно собирать в один пакет. PostgreSQL называет такой пакет расширением. Чтобы определить расширение, вам необходимо как минимум файл скрипта, который содержит команды SQL для создания объектов расширения и управляющий файл, который задаёт несколько базовых свойств самого расширения. Если расширение включает код на языке Си, то оно обычно содержит файл динамической библиотеки, содержащий данный код на языке Си. Если у вас есть эти файлы, то команда CREATE EXTENSION загрузит объекты из расширения в вашу базу данных.

Основное преимущество использования расширения, в отличие от просто запуска SQL скрипта для загрузки пачки "отдельных" объектов в вашу базу данных, состоит в том, что PostgreSQL затем поймёт, что объекты расширения работают вместе. Вы можете удалить все объекты одной командой DROP EXTENSION (не нужно поддерживать отдельный скрипт "uninstall"). Даже pg_dump дамп знает, что не нужно делать дамп отдельных экземпляров объектов раширения — он будет вместо этого просто включать в дамп команду CREATE EXTENSION. Это значительно упрощает миграцию на новую версию расширения, которая может содержать больше объектов или другие объекты по сравнению со старой версией. Однако, обратите внимание, что при загрузке дампа в новую базу данных, у вас, должны быть доступны скрипт управления расширением и другие файлы.

PostgreSQL не позволит вам удалить отдельный объект, содержащийся в расширении, исключая удаление всего расширения. Также, когда вы изменяете определение объекта, который является членом расширения (например, для функции с помощью команды CREATE OR REPLACE FUNCTION), имейте в виду, что модифицированное определение не будет сохранятся в дамп с помощью pg_dump. Таким образом, изменение обычно будет иметь эффект только если вы внесёте такое же изменение в файл скрипта расширения. (Но существуют специальные ситуации для таблиц, содержащих конфигурационные данные, см. ниже).

У механизма расширения также есть оговорки для скриптов модификаций пакетов, которые согласуют определения SQL объектов, содержащихся в расширении. Например, если версия расширения 1.1, по сравнению с версией 1.0, добавляет одну функцию и изменяет тело другой функции, то автор расширения может предоставить скрипт обновления, который выполняет только эти два действия. Затем, чтобы применить эти изменения и отследить какая версия расширения фактически установлена в данной базе данных может быть использована команда ALTER EXTENSION UPDATE.

Виды SQL объектов, которые могут быть членами расширения перечисляются в описании ALTER EXTENSION. В частности, объекты уровня кластера баз данных, такие как базы данных, роли и табличные пространства не могут быть членами расширения, так как расширение работает только внутри одной базы данных. (Хотя скрипту расширения не запрещается создавать такие объекты, если он делает так, чтобы они не считались частью расширения). Также, обратите внимание, что в то время как таблица может быть членом расширения, её вспомогательные объекты, такие как индексы, не могут напрямую считаться членами расширения.

35.15.1. Файлы расширений

Команда CREATE EXTENSION для каждого расширения, полагается на управляющий файл, который должен иметь такое же имя как и расширение, но с суффиксом .control и должен размещаться в установочном каталоге SHAREDIR/extension. Также, должен быть как минимум один файл с SQL скриптом, имя которого следует шаблону расширение--версия.sql (например, foo--1.0.sql для версии 1.0 расширения foo). По умолчанию, файл(ы) скрипта также размещаются в каталоге SHAREDIR/extension; но управляющий файл может задать другой каталог для файла(ов) скрипта.

Формат управляющего файла расширения такой же как и у файла postgresql.conf, т.е. список строк вида имя_параметра = значение. Разрешаются пустые строки и комментарии, которые начинаются с символа #. Убедитесь, что все значения, которые не являются числами или которые состоят из нескольких слов, заключены в кавычки.

Управляющий файл может устанавливать следующие параметры:

directory (строка)

Каталог, содержащий файл(ы) SQL скриптов. Если не указан полный путь, то имя считается относительно установочного каталога SHAREDIR. Поведение по умолчанию эквивалентно установленному параметру directory = 'extension'.

default_version (строка)

Версия расширения по умолчанию (т.е. та версия, которая будет установлена, если в команде CREATE EXTENSION версия не указана). Хотя данный параметр может быть опущен, но обычно вы не захотите этого делать, потому что в этом случае, при выполнении CREATE EXTENSION с незаданной опцией VERSION произойдёт ошибка.

comment (строка)

Комментарий (любая строка) о данном расширении. В качестве альтернативы, комментарий может быть задан в файле скрипта с помощью команды COMMENT.

encoding (строка)

Кодировка набора символов, используемая файлами скриптов. Она должна быть задана, если файлы скриптов содержат не-ASCII символы. В противном случае, будет считаться, что файлы имеют ту же кодировку, что и база данных.

module_pathname (строка)

Значение данного параметра будет подставлено вместо каждого появления MODULE_PATHNAME в файлах скриптов. Если данный параметр не установлен, подстановка выполняться не будет. Обычно, значение устанавливается в $libdir/имя_динамической_библиотеки и затем MODULE_PATHNAME используется в командах CREATE FUNCTION для функций на языке Cи, так что файлам скриптов нет необходимости указывать точное имя динамической библиотеки.

requires (строка)

Список имён расширений, от которых зависит данное раширение, например requires = 'foo, bar'. Эти расширения должны быть установлены перед тем как устанавливать текущее.

superuser (логическое)

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

relocatable (логическое)

Расширение является перемещаемым, если содержащиеся в нём объекты, после начального создания расширения, возможно переместить в другую схему. По умолчанию false, т.е. расширение не перемещаемое. Подробности см. ниже.

schema (строка)

Данный параметр может быть установлен только для неперемещаемых расширений. Он заставляет расширение загружаться в схему с конкретным именем и ни в какие другие. Подробности см. ниже.

В дополенение к первичному управляющему файлу extension.control, расширение может иметь вторичные управляющие файлы, имена которых задаются в формате расширение--версия.control. Если эти файлы предоставляются, они должны быть размещены в каталоге с файлами скриптов. Вторичные управляющие файлы имеют такой же формат как и первичный управляющий файл. Любые параметры установленные во вторичных управляющих файлах при установке или обновлении данной версии расширения перекрывают установки в первичном управляющем файле. Однако, параметры directory и default_version не могут быть установлены во вторичном управляющем файле.

Файлы SQL скриптов расширений могут содержать любые SQL команды, исключая команды управления транзакциями (BEGIN, COMMIT и т.д.) и команды, которые не могут быть запущены внутри транзакционного блока (такие как VACUUM). Это из-за того, что файлы скриптов неявно выполняются внутри транзакционного блока.

В то время как файлы скриптов могут содержать любые символы, разрешённые указанной кодировкой, управляющие файлы должны содержать только ASCII символы, потому что для PostgreSQL не существует способа узнать в какой кодировке управляющий файл. На практике, использование не-ASCII символов может понадобится только для комментариев. В этом случае рекомендуется не использовать параметр comment в управляющем файле, но вместо него для задания комментария использовать COMMENT ON EXTENSION внутри файла скрипта.

35.15.2. Перемещение расширений

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

  • Полностью перемещаемое расширение, может быть перемещено в другую схему в любое время, даже после того как оно было загружено в базу данных. Это делается при помощи команды ALTER EXTENSION SET SCHEMA, которая автоматически переименовывает все объекты члены расширения в новую схему. Обычно, такое возможно только если расширение не содержит внутренних указаний на конкретную схему, внутри которой должны быть размещены его объекты. Также, чтобы сделать это, объекты расширения все должны находиться в одной схеме (игнорируя объекты, которые не привязаны к схемам, такие как процедурные языки). Помечайте полностью перемещаемое расширение в его управляющем файле, установкой relocatable = true.

  • Расширение может быть перемещено во время установки, но не после неё. Обычно такая ситуация возникает, если в файле скрипта расширения явно указана схема, в которую загружается расширение, например установкой search_path в функциях SQL. Для таких расширений в их управляющем файле, устанавливайте relocatable = false и используйте @extschema@, чтобы указать в файле скрипта схему, в которую загружается расширение. Все вхождения этой подстроки будут замещаться фактическим именем схемы, перед тем как скрипт будет выполнен. Пользователь может задать эту схему, используя опцию SCHEMA команды CREATE EXTENSION.

  • Если расширение совсем не поддерживает перемещение, установите в его управляющем файле relocatable = false, а также установите schema в имя необходимой для него схемы. Это предотвратит использование опции SCHEMA в команде CREATE EXTENSION, если только указанное в опции имя не то же самое, что и в управляющем файле. Такое обычно необходимо, если расширение содержит внутренние указания на имена схем, которые не могут быть изменены при использовании @extschema@. Механизм подстановки @extschema@ в этом случае также доступен, хотя его использование ограничивается, если имя схемы задано в управляющем файле.

Во всех случаях, файл скрипта будет выполняться вначале с использованием search_path для поиска схемы, куда нужно провести установку; таким обазом команда CREATE EXTENSION делает эквивалентно следующему:

SET LOCAL search_path TO @extschema@;

Это разрешает объектам, создаваемым файлом скрипта размещаться в нужной схеме. Файл скрипта может изменить search_path, если он того хочет, но обычно это нежелательно. После завершения команды CREATE EXTENSION, search_path восстанавливается к предыдущему значению.

Нужная схема, определяется параметром schema в управляющем файле, если он задан, в противном случае опцией SCHEMA команды CREATE EXTENSION, если она задана, в противном случае это текущая схема для создания объектов по умолчанию (первая в пути search_path). Когда управляющий файл использует параметр schema, нужная схема, если она уже не существует, будет создана, но в двух других случаях, она уже должна существовать.

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

Хотя неперемещаемое расширение может содержать объекты, находящиеся в нескольких схемах, желательно размешать всех объекты расширения, предназначенные для внешнего использования, в одной схеме, которая считается схемой для загрузки расширения. Если search_path установлен по умолчанию, то с такой договорёностью удобно работать при создании зависимых расширений.

35.15.3. Конфигурационные таблицы расширения

Некоторые расширения включают конфигурационные таблицы, содержащие данные, которые могут быть добавлены или изменены пользователем после установки расширения. Обычно, если таблица является частью расширения, то ни её определение, ни её содержимое не попадёт в дамп при выполнении pg_dump. Однако, для конфигурационной таблицы такое нежелательно; любые изменения данных, которые выполнил пользователь нужно включать в дамп, иначе расширение после загрузки из дампа будет вести себя по-другому.

Чтобы решить эту проблему, файл скрипта расширения может пометить табдицу как конфигурационную, что заставит pg_dump включать её содержимое (но не определение её структуры) в дамп. Чтобы сделать это, после создания таблицы вызовите функцию pg_extension_config_dump(regclass, text), например:

CREATE TABLE my_config (key text, value text);

SELECT pg_catalog.pg_extension_config_dump('my_config', '');

Таким способом можно пометить любое количество таблиц.

Когда второй аргумент pg_extension_config_dump является пустой строкой, в дамп при выполнении pg_dump попадает всё содержимое таблицы. Такое обычно правильно только в случае, если изначально, при создании скриптом расширения, таблица пуста. Если в таблице смесь из записей, изначально созданных скриптом и созданных пользователем, второй аргумент pg_extension_config_dump предоставляет условие WHERE, выбирающее данные, которые должны попасть в дамп. Например, вы можете выполнить:

CREATE TABLE my_config (key text, value text, standard_entry boolean);

SELECT pg_catalog.pg_extension_config_dump('my_config', 'WHERE NOT standard_entry');

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

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

35.15.4. Обновления расширений

Одно из преимуществ механизма расширений состоит в том, что он предоставляет удобные способы управления обновлениями SQL команд, которые создают и определяют объекты расширения. Управление осуществляется привязкой имени или номера версии к версии установочного скрипта расширения. В дополнение, если вы хотите, чтобы пользователи могли обновлять свои базы данных динамически с одной версии на следующую, вы должны предоставить скрипты обновлений, которые сделают необходимые изменения при переходе от одной версии к следующей. Скрипты обновлений имеют имена, которые задаются в формате расширение--старая_версия--новая_версия.sql (например, foo--1.0--1.1.sql содержит команды, которые изменяют расширение foo с версии 1.0 на версию 1.1).

При условии, что есть в наличии подходящий скрипт обновления, команда ALTER EXTENSION UPDATE обновит установленное расширение на указанную новую версию. Скрипт обновления запускается в том же окружении, которое предоставляет команда CREATE EXTENSION для скриптов установки: в частности, search_path устанавливается тем же способом и любые новые объекты, создаваемые скриптом автоматически добавляются в раширение.

Если расширение имеет вторичные управляющие файлы, управляющие параметры, которые используются скриптом обновления, ассоциируются с версией (новой) целевых скриптов.

Механизм обновления может быть использован для решения одного важного специального случая: преобразования "несвязанной" коллекции объектов в расширение. Перед тем как механизм расширения был добавлен в PostgreSQL (в версии 9.1), многие люди писали модули расширений, которые просто создавали неклассифицированные, необъединённые в пакет объекты. При условии, что существующая база данных содержит такие объекты, каким образом возможно преобразовать эти объекты в должным образом работающий пакет расширения? Одним из способов является их удаление и затем выполнение CREATE EXTENSION, но это нежелательно если данные объекты имеют зависимости (например, если в таблицах есть колонки тех типов данных, которые создаёт данное расширение). Способом, который решает данную ситуацию, является создание пустого расширения, а затем использование ALTER EXTENSION ADD, чтобы прикрепить каждый уже существующий объект к этому расширению, а затем создать любые новые объекты, которые есть в текущей версии расширения, но которых не существовало в версии без сформированного пакета. CREATE EXTENSION прддерживает такой случай с помощью опции FROM старая_версия, которая вынуждает не запускать обычный скрипт установки для нужной версии, а вместо этого запускает скрипт обновления с именем расширение--старая_версия--нужная_версия.sql. Выбор какого-либо фиктивного имени версии, используемой как старая_версия остаётся за автором расширения, хотя обычно есть договорённость использовать имя unpackaged. Если у вас есть несколько предыдущих версий, для которых вам необходимо иметь возможность обновлять их как это делается для расширений, используйте несколько фиктивных имён версий, для идентификации каждой их них.

ALTER EXTENSION позволяет выполнить последовательность файлов скриптов обновлений для получения нужного обновления. Например, если доступны только foo--1.0--1.1.sql и foo--1.1--2.0.sql, ALTER EXTENSION выполнит их последовательно, если текущей установленной версией является 1.0, а затребовано обновление до версии 2.0.

PostgreSQL не имеет никаких предположений о свойствах номеров версий: например, СУБД не знает следует ли версия 1.1 за версией 1.0. СУБД только проверяет доступные имена версий и применяет подходящие скрипты обновлений. (Имя версии фактически может быть строкой, которая не содержит в конце или начале -- или -.)

Иногда также полезно предоставить скрипты "регрессии" (замены новых версий на старые), например foo--1.1--1.0.sql, чтобы позволить обратить назад изменения, сделанные в версии 1.1. Если вы делаете это, будьте осторожны, чтобы скрипты регрессии не оказались неожиданно запущенными, потому что они дают более короткий путь. Риск в данном случае есть там, где существует скрипт обновления по "быстрому пути", который прыгает через несколько версий, также как скипт регресии для быстрого пути к исходной точке. Это может привести к выпадению нескольких шагов при выполнении регрессии по быстрому пути при перемещении на несколько версий за один раз. Если скрипт регрессии удаляет неперемещаемые объекты, то это приведёт к нежелательным результатам.

Чтобы выполнить проверку на наличие неожиданных путей обновления, используйте команду:

SELECT * FROM pg_extension_update_paths('имя_расширения');

Эта команда показывает каждую отдельную пару известных имён версий для указанного расширения, вместе с последовательностью пути обновления, который будет выбран для получения затребованной версии из исходной версии или NULL, если такой путь обновления недоступен. Путь показывается в текстовом формате с использованием -- как разделителей. Вы можете использовать regexp_split_to_array(path,'--'), если вы предпочитаете формат массива. format.

35.15.5. Пример расширения

Здесь дан полный пример расширения, собранного только на SQL, двухэлементный составной тип, который может хранить любой тип значений, в своих слотах, которые называются "k" и "v". Не-текстовые значения для хранения автоматически приводятся к тексту.

Файл скрипта pair--1.0.sql выглядит так:

CREATE TYPE pair AS ( k text, v text );

CREATE OR REPLACE FUNCTION pair(anyelement, text)
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';

CREATE OR REPLACE FUNCTION pair(text, anyelement)
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';

CREATE OR REPLACE FUNCTION pair(anyelement, anyelement)
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';

CREATE OR REPLACE FUNCTION pair(text, text)
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair;';

CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = anyelement, PROCEDURE = pair);
CREATE OPERATOR ~> (LEFTARG = anyelement, RIGHTARG = text, PROCEDURE = pair);
CREATE OPERATOR ~> (LEFTARG = anyelement, RIGHTARG = anyelement, PROCEDURE = pair);
CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = text, PROCEDURE = pair);

Управляющий файл pair.control выглядит так:

# pair extension
comment = 'A key/value pair data type'
default_version = '1.0'
relocatable = true

Поскольку вам фактически нужен makefile для установки этих двух файлов в правильный каталог, вы можете использовать вот такой Makefile:

EXTENSION = pair
DATA = pair--1.0.sql

PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)

Данный makefile полагается на PGXS, который описан в Section 35.16. Команда make install установит управляющий файл и файл скрипта в правильный каталог, который показывает pg_config.

После того как файлы установлены, используйте команду CREATE EXTENSION для загрузки объектов в определённую базу данных.

Back to top

(С) Виктор Вислобоков, 2008-2023