Внутренности PostgreSQL: как добавить новую функцию
4 декабря 2023
При разработке пача для PostgreSQL иногда требуется добавить новую функцию, чтобы ее можно было вызывать из SQL. Недавно вопрос о том, как это делается, задали мне два разных человека в течение одной недели. И хотя это простая задача, информация, по всей видимости, является востребованной. Давайте же рассмотрим решение.
Рассматривать будем на примере конкретного пача 260a1f18, добавляющего в ядро функции to_bin()
и to_oct()
:
to_bin
---------
1111011
=# SELECT to_bin(255);
to_bin
----------
11111111
=# SELECT pg_typeof(to_bin(255));
pg_typeof
-----------
text
Добавление функции осуществляется путем редактирования файла pg_proc.dat.
Например:
proname => 'to_bin', prorettype => 'text', proargtypes => 'int4',
prosrc => 'to_bin32' },
Если функция имеет несколько аргументов (proargtypes
), их типы указываются через пробел. Можно определить несколько функций с одинаковыми именами (proname
), но разными аргументами или их количеством. Само собой разумеется, реализации функции на языке C при этом будут иметь разные имена (prosrc
). Перегрузки функций по возвращаемому значению (prorettype
) в PostgreSQL не предусмотренно. Описание функции (descr
) — это то, что будет показываться в выводе \df+ to_bin
.
Для поиска свободного Oid есть специальный скрипт:
...
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:
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. Также файл бывает полезен, когда вы примерно понимаете, какую функцию ищите, но не знаете ее название.
Метки: C/C++, PostgreSQL, СУБД.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.