postgresql-add-function/

При разработке пача для PostgreSQL иногда требуется добавить новую функцию, чтобы ее можно было вызывать из SQL. Недавно вопрос о том, как это делается, задали мне два разных человека в течение одной недели. И хотя это простая задача, информация, по всей видимости, является востребованной. Давайте же рассмотрим решение.

Рассматривать будем на примере конкретного пача 260a1f18 , добавляющего в ядро функции to_bin() и to_oct() :

=# SELECT to_bin(123);
to_bin
———
1111011

=# SELECT to_bin(255);
to_bin
———-
11111111

=# SELECT pg_typeof(to_bin(255));
pg_typeof
————
text

Добавление функции осуществляется путем редактирования файла pg_proc.dat.

Например:

{ oid => ‘9030’, descr => ‘convert int4 number to binary’,
proname => ‘to_bin’, prorettype => ‘text’, proargtypes => ‘int4’,
prosrc => ‘to_bin32’ },

Если функция имеет несколько аргументов ( proargtypes ), их типы указываются через пробел. Можно определить несколько функций с одинаковыми именами ( proname ), но разными аргументами или их количеством. Само собой разумеется, реализации функции на языке C при этом будут иметь разные имена ( prosrc ). Перегрузки функций по возвращаемому значению ( prorettype ) в PostgreSQL не предусмотренно. Описание функции ( descr ) — это то, что будет показываться в выводе df+ to_bin .

Для поиска свободного Oid есть специальный скрипт:

$ ./src/include/catalog/unused_oids

6312 — 8402
8404 — 9029
9034 — 9999
Patches should use a more-or-less consecutive range of OIDs.
Best practice is to start with a random choice in the range 8000-9999.
Suggested random unused OID: 9223 (777 consecutive OID(s) available ⏎
starting here)

Еще одно важное свойство функции — это volatility . Оно указывается при помощи параметра provolatile . Значение s говорит, что функция является STABLE . Для одних и тех же аргументов функция возвращает один и тот же результат в рамках одного SQL-выражения . Это поведение функций, к примеру, зависящих от параметров конфигурации (GUCs) . Значение v соответствует VOLATILE функциям. Это «грязные» функции в том смысле, что они могут иметь внутреннее состояние, ходить в файлы или сеть, и так далее. Если provolatile не указан, то функция является IMMUTABLE . Это как чистые функции в функциональном программировании . Их возвращаемое значение зависит только от аргументов.

Но есть нюансы. Например, функция, работающая с часовыми поясами при прочих равных считается IMMUTABLE . Несмотря на то, что обновление базы часовых поясов повлияет на возвращаемые ею значения, а также будет требовать перестройки индексов, если они использовали эту функцию. Также поведение IMMUTABLE функции может измениться при обновлении самого PostgreSQL. Например, если реализация содержала ошибку.

Не считая редактирования pg_proc.dat, написание новой функции ничем не отличается от написания новой функции для расширения — см один , два , три и далее по ссылкам. Конкретная реализация to_bin32() может быть найдена в файле varlena.c:

static inline text *
convert_to_base ( uint64 value , int base )
{
const char * digits = «0123456789abcdef» ;
char buf [ sizeof ( uint64 ) * BITS_PER_BYTE ] ;
char * const end = buf + sizeof ( buf ) ;
char * ptr = end ;

Assert ( base > 1 ) ;
Assert ( base <= 16 ) ;

do
{
*— ptr = digits [ value % base ] ;
value /= base ;
} while ( ptr > buf && value ) ;

return cstring_to_text_with_len ( ptr , end ptr ) ;
}

Datum
to_bin32 ( PG_FUNCTION_ARGS )
{
uint64 value = ( uint32 ) PG_GETARG_INT32 ( 0 ) ;

PG_RETURN_TEXT_P ( convert_to_base ( value , 2 ) ) ;
}

Помимо кода функции еще нужно добавить документацию и тесты. Это уже дело техники, а конкретные изменения могут быть найдены во все том же 260a1f18 . Поэтому не будем задерживаться на этом вопросе.

Напоследок хочется отметить два момента. Во-первых, некоторые функции имеет смысл добавлять не в ядро системы, а поместить в отдельное расширение — либо стороннее, либо идущее вместе с PostgreSQL и живущее в каталоге /contrib/ . Во-вторых, pg_proc.dat является удобной точкой входа для изучения внутренностей PostgreSQL. Также файл бывает полезен, когда вы примерно понимаете, какую функцию ищите, но не знаете ее название.

EnglishRussianUkrainian