Postgres - статьи

Общие замечания по программированию на С под PostgreSQL


Для понимания примеров мы приводим особенности написания пользовательских функций на языке C для PostgreSQL, полное описание можно найти в разделе C-Language Functions

документации.

Функции должны использовать интерфейс версии 1, версия 0 deprecated, хотя пока и поддерживается.

  • На уровне С PostgreSQL оперирует с базовыми типами данных

    или SQL-типами в представлении С-типа Datum. Datum имеет размер, равный размеру указателя на данной архитектуре (PostgreSQL не поддерживает архитектуры с указателем, меньшим 32 бит). SQL-типы в PostgreSQL делятся на передаваемые по значению и по указателю. Передаваемые по значению типы не должны превышать размер 32 бита. Передаваемые по указателю типы подразделяются на типы с фиксированной длиной и переменной. Для типов с переменной длиной первым полем всегда должна быть длина значения int4 (в байтах, с учетом размера поля длины). Для преобразования Datum в тип и обратно существует набор макросов, см.,например, файлы postgres.h, fmgr.h:

    • int32 i = DatumGetInt32(datum);
    • Datum datum = BoolGetDatum( true );

    • text *sometext = DatumGetTextP( datum );

    • Для абстрактного типа, передаваемого по указателю, можно использовать преобразование в указатель:

    • SOMETYPE *ptr = (SOMETYPE*)DatumGetPointer(datum);

    • Datum datum = PointerGetDatum( ptr );

  • Длинные значения типов с переменной длиной могут "тоститься", то есть нарезаться маленькими кусочками (TOAST - The Oversized Attribute Storage Technique, подробнее). Макросы для встроенных типов учитывают эту возможность, для user-defined это должно указываться непосредственно:
    SOMETYPE *ptr = (SOMETYPE*)PG_DETOAST_DATUM( DatumGetPointer(datum) );

  • Для работы с типами переменной длины есть дополнительные макросы:
    • VARSIZE( ptr ) - размер в байтах
    • VARDATA( ptr ) - возвращает указатель на данные после поля длины.
    • Т.е., если для типа определена структура: typedef struct { int32 length; char data[1]; } FOO; FOO *foo = f();

      то f->length == VARSIZE(f) и f->data == (char*)VARDATA(f) всегда. Заметим, что длина поля не может превышать 1Gb. Два бита в поле длина используются PostgreSQL в своих целях.

    • Функция должна возвращать тип Datum и объявляться как:


      PG_FUNCTION_INFO_V1(function);

      Datum function(PG_FUNCTION_ARGS);

    • Передача параметров вызываемой функции и возвращаемое значение прозводится посредством макросов. Порядковый номер аргумента задается параметром макроса. Datum function(PG_FUNCTION_ARGS) { /* целое число, передается по значению */ int32 i = PG_GETARG_INT32(0); /* указатель на прямоугольник */ BOX *b = PG_GETARG_BOX_P(1); /* указатель на текст */ text *t = PG_GETARG_TEXT_P(2); /* пользовательский тип с переменной длиной */ FOO *f = (FOO*)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(3));

      ....

      /* * после того, как работа произведена, не плохо бы очистить память * "потостенных" значений, Аргументы: * первый - имя переменной-параметра функции, * второй - порядковый номер */ PG_FREE_IF_COPY(t,2); PG_FREE_IF_COPY(f,3);

      PG_RETURN_INT32( i ); }

    • Функциям запрещено менять значения своих аргументов. Это правило не относится к некоторым специальным функциям, например к функциям penalty, equal, union и picksplit интерфейса к GiST.


    • Все управление динамической памятью должно осуществляться PostgreSQL-аналогами palloc/repalloc/pfree. Их использование, как правило, предохраняет от утечек памяти, быстрее, и облегчает отладку (если PostgreSQL скомпилен с флагами --enable-debug и --enable-cassert)


    • Память для типов, передаваемых по указателю и соответствующих какому-либо SQL-типу, должна быть зарезервирована вызовом palloc.



    • Содержание раздела