Расширяемость
SP-GiST предлагает интерфейс с высоким уровнем абстракции, требуя от разработчика методов доступа реализовывать только методы, специфичные для данного типа данных. Ядро SP-GiST отвечает за эффективное отображение на диск и поиск в древовидной структуре. Оно также заботится о параллелизме и протоколировании.
Листовые кортежи дерева SP-GiST обычно содержат значения того же типа данных, что и индексируемый столбец, хотя возможно, что они также содержат представления индексируемого столбца с потерями. Кортежи, хранящиеся на корневом уровне, будут непосредственно представлять исходное значение индексированных данных, но кортежи на более низких уровнях могут содержать только частичное значение, например суффикс. В этом случае функции поддержки операторного класса должны уметь восстанавливать исходное значение, используя информацию, накопленную из внутренних кортежей, которые передаются для достижения уровня листа.
Когда индекс SP-GiST создается с колонками INCLUDE
, значения этих колонок также хранятся в кортежах листа. Столбцы INCLUDE
не имеют отношения к классу оператора SP-GiST, поэтому далее они не рассматриваются.
Внутренние кортежи более сложны, поскольку являются точками ветвления в дереве поиска. Каждый внутренний кортеж содержит набор из одного или н ескольких узлов, которые представляют собой группы одинаковых значений листьев. Узел содержит ссылку, которая ведет либо к другому внутреннему кортежу более низкого уровня, либо к короткому списку листовых кортежей, которые находятся на одной индексной странице. Каждый узел обычно имеет метку, которая описывает его; например, в радиксном дереве меткой узла может быть следующий символ строкового значения. (В качестве альтернативы класс оператора может опустить метки узлов, если он работает с фиксированным набором узлов для всех внутренних кортежей; см. раздел «SP-GiST без меток узлов».) По желанию, внутренний кортеж может иметь префиксное значение, которое описывает все его члены. В радиксном дереве это может быть общим префиксом представленных строк. Значение префикса не обязательно является префиксом, но может быть любой информацией, необходимой классу оператора; например, в квадродереве оно может хранить центральную точку, относительно которой измеряются четыре квадранта. Тогда внутренний кортеж квадродерева будет также содержать четыре узла, соответствующие квадрантам вокруг этой центральной точки.
Некоторые алгоритмы работы с деревьями требуют знания уровня (или глубины) текущего кортежа, поэтому ядро SP-GiST предоставляет классам операторов возможность управлять подсчетом уровней во время спуска по дереву. Также поддерживается инкрементная реконструкция представленного значения, когда это необходимо, и передача дополнительных данных (называемых значениями обхода) во время спуска по дереву.
Примечание
Ядро SP-GiST заботится о записях NULL. Хотя индексы SP-GiST сохраняют записи для NULL в индексируемых столбцах, это скрыто от кода класса операторов индекса: никакие нулевые записи индекса или условия поиска никогда не будут переданы методам класса операторов. (Предполагается, что операторы SP-GiST являются строгими и поэтому не могут быть успешными для нулевых значений
values
.), поэтому значения NULL здесь не обсуждаются.
Существует пять пользовательских методов, которые должен предоставлять класс оператора индекса для SP-GiST, и два необязательных. Все пять обязательных методов принимают два внутренних аргумента, первый из которых - указатель на структуру C, содержащий входные значения для метода поддержки, а второй аргумент - указатель на структуру C, куда должны быть помещены выходные значения. Четыре обязательных метода просто возвращают void
, поскольку все их результаты появляются в выходной структуре; но leaf_consistent
возвращает булевский результат. Методы не должны изменять никаких полей своих входных структур. Во всех случаях выходная структура инициализируется нулями перед вызовом пользовательского метода. Необязательный шестой метод compress принимает в качестве единственного аргумента индексируемый массив данных и возвращает значение, пригодное для физического хранения в кортеже листа. Необязательный седьмой метод options принимает внутренний указатель на C-структуру, в которой должны быть размещены специфические для opclass
параметры, и возвращает void
.
Пять обязательных пользовательских методов:
config
Возвращает статическую информацию о реализации индекса, включая OID типов данных префикса и метки узла.
Объявление функции в формате SQL должно выглядеть следующим образом:
CREATE FUNCTION my_config(internal, internal) RETURNS void ...
Первый аргумент - указатель на структуру spgConfigIn
языка C, содержащую входные данные для функции. Второй аргумент - указатель на структуру spgConfigOut
языка C, которую функция должна заполнить результатами.
typedef struct spgConfigIn
{
Oid attType; /* Data type to be indexed */
} spgConfigIn;
typedef struct spgConfigOut
{
Oid prefixType; /* Data type of inner-tuple prefixes */
Oid labelType; /* Data type of inner-tuple node labels */
Oid leafType; /* Data type of leaf-tuple values */
bool canReturnData; /* Opclass can reconstruct original data */
bool longValuesOK; /* Opclass can cope with values > 1 page */
} spgConfigOut;
attType
передается для поддержки классов полиморфных индексных операторов; для обычных классов операторов с фиксированным типом данных он всегда будет иметь одно и то же значение, поэтому его можно игнорировать.
Для классов операторов, не использующих префиксы, prefixType
может быть установлен в VOIDOID
. Аналогично, для классов операторов, не использующих метки узлов, labelType
может быть установлен в VOIDOID
. canReturnData
должен быть установлен в true
, если класс оператора способен восстановить первоначально подставленное значение индекса. longValuesOK
должен быть установлен в true
, только если attType
имеет переменную длину и класс оператора способен сегментировать длинные значения путем повторной суффиксации (см. раздел «SP-GiST без меток узлов»).
leafType
должен соответствовать типу хранения индекса, определенному в записи каталога opckeytype
класса оператора. (Обратите внимание, что opckeytype
может быть нулем, подразумевая, что тип хранения совпадает с типом ввода класса оператора, что является наиболее распространенной ситуацией). По соображениям обратной совместимости метод config
может установить leafType
в какое-либо другое значение, и оно будет использоваться; но это устарело, так как содержимое индекса в этом случае будет неверно идентифицировано в каталогах. Кроме того, допустимо оставить leafType
неинициализированным (нулевым); это интерпретируется как значение типа хранения индекса, полученного из opckeytype
.
Если attType
и leafType
различны, необходимо предоставить необязательный метод compress
. Метод compress
отвечает за преобразование индексируемых точек отсчета из attType
в leafType
.
choose
Выбирает метод для вставки нового значения во внутренний кортеж.
SQL-объявление функции должно выглядеть следующим образом:
CREATE FUNCTION my_choose(internal, internal) RETURNS void ...
Первый аргумент - указатель на структуру spgChooseIn
языка C, содержащую входные данные для функции. Второй аргумент - указатель на структуру spgChooseOut
языка C, которую функция должна заполнить данными о результате.
typedef struct spgChooseIn
{
Datum datum; /* original datum to be indexed */
Datum leafDatum; /* current datum to be stored at leaf */
int level; /* current level (counting from zero) */
/* Data from current inner tuple */
bool allTheSame; /* tuple is marked all-the-same? */
bool hasPrefix; /* tuple has a prefix? */
Datum prefixDatum; /* if so, the prefix value */
int nNodes; /* number of nodes in the inner tuple */
Datum *nodeLabels; /* node label values (NULL if none) */
} spgChooseIn;
typedef enum spgChooseResultType
{
spgMatchNode = 1, /* descend into existing node */
spgAddNode, /* add a node to the inner tuple */
spgSplitTuple /* split inner tuple (change its prefix) */
} spgChooseResultType;
typedef struct spgChooseOut
{
spgChooseResultType resultType; /* action code, see above */
union
{
struct /* results for spgMatchNode */
{
int nodeN; /* descend to this node (index from 0) */
int levelAdd; /* increment level by this much */
Datum restDatum; /* new leaf datum */
} matchNode;
struct /* results for spgAddNode */
{
Datum nodeLabel; /* new node's label */
int nodeN; /* where to insert it (index from 0) */
} addNode;
struct /* results for spgSplitTuple */
{
/* Info to form new upper-level inner tuple with one child tuple */
bool prefixHasPrefix; /* tuple should have a prefix? */
Datum prefixPrefixDatum; /* if so, its value */
int prefixNNodes; /* number of nodes */
Datum *prefixNodeLabels; /* their labels (or NULL for
* no labels) */
int childNodeN; /* which node gets child tuple */
/* Info to form new lower-level inner tuple with all old nodes */
bool postfixHasPrefix; /* tuple should have a prefix? */
Datum postfixPrefixDatum; /* if so, its value */
} splitTuple;
} result;
} spgChooseOut;
datum
- исходное значение типа spgConfigIn.attType
, которое должно быть вставлено в индекс. leafDatum
- значение типа spgConfigOut.leafType
, которое изначально является результатом значение метода compress
, примененное к datum
, если предоставлен метод compress
, или то же значение, что и datum
, в противном случае. leafDatum
может изменяться на более низких уровнях дерева, если его изменяют методы choose
или picksplit
. Когда поиск вставки дост игает листовой страницы, текущее значение leafDatum
- это то, что будет храниться во вновь созданном листовом кортеже. level
- уровень текущего внутреннего кортежа, начиная с нуля для корневого уровня. allTheSame
- true
, если текущий внутренний кортеж помечен как содержащий несколько эквивалентных узлов (см. раздел «Внутренние кортежи "все-равны"»). hasPrefix
- истина, если текущий внутренний кортеж содержит префикс; если да, то prefixDatum
- его значение. nNodes
- количество дочерних узлов, содержащихся во внутреннем кортеже, а nodeLabels
- массив значений их меток, или NULL, если меток нет.
Функция choose
может определить, что новое значение совпадает с одним из существующих дочерних узлов, или что необходимо добавить новый дочерний узел, или что новое значение не соответствует префиксу кортежа, и поэтому внутренний кортеж должен быть разделен, чтобы создать менее строгий префикс.
Если новое значение совпадает с одним из существующих дочерних узлов, установите resultType
в spgMatchNode
. Установите nodeN
в индекс (от нуля) этого узла в массиве узлов. Установите levelAdd
в приращение уровня, вызванное спуском через этот узел, или оставьте его равным нулю, если класс оператора не использует уровни. Установите restDatum
равным leafDatum
, если класс оператора не модифицирует точки отсчета от одного уровня к другому, или иначе установите его в модифицированное значение, которое будет использоваться как leafDatum
на следующем уровне.
Если необходимо добавить новый дочерний узел, установите resultType
в spgAddNode
. Установите nodeLabel
в метку, которая будет использоваться для нового узла, и установите nodeN
в индекс (от нуля), по которому нужно вставить узел в массив узлов. После добавления узла функция choose
будет вызвана снова с измененным внутренним кортежем; этот вызов должен привести к результату spgMatchNode
.
Если новое значение несовместимо с префиксом кортежа, установите resultType
в spgSplitTuple
. Это действие перемещает все существующие узлы в новый внутренний кортеж нижнего уровня и заменяет существующий внутренний кортеж кортежем с единственной нисходящей ссылкой, указывающей на новый внутренний кортеж нижнего уровня. Установите prefixHasPrefix
, чтобы указать, должен ли новый верхний кортеж иметь префикс, и если да, то установите prefixPrefixDatum
в значение префикса. Это новое значение префикса должно быть достаточно менее ограничительным, чем исходное, чтобы новое значение могло быть проиндексировано. Установите prefixNNodes
в число узлов, необходимых в новом кортеже, и установите prefixNodeLabels
в массив palloc, содержащий их метки, или в NULL, если метки узлов не требуются. Обратите внимание, что общий размер нового верхнего кортежа должен быть не больше общего размера кортежа, который он заменяет; это ограничивает длину нового префикса и новых меток. Установите childNodeN
в индекс (от нуля) узла, который будет ссылаться на новый внутренний кортеж нижнего уровня. Установите postfixHasPrefix
, чтобы определить, должен ли новый внутренний кортеж нижнего уровня иметь префикс, и если да, то установите postfixPrefixDatum
в значение префикса. Комбинация этих двух префиксов и метки нисходящего узла (если таковая имеется) должна иметь то же значение, что и исходный префикс, поскольк у нет возможности изменить метки узлов, которые перемещаются в новый кортеж нижнего уровня, а также изменить какие-либо записи дочерних индексов. После того как узел будет разделен, функция choose
будет вызвана снова с заменяющим внутренним кортежем. Этот вызов может вернуть результат spgAddNode
, если действием spgSplitTuple
не был создан подходящий узел. В конечном итоге choose
должна вернуть spgMatchNode
, чтобы вставка могла спуститься на следующий уровень.
picksplit
Решает, как создать новый внутренний кортеж над набором листовых кортежей.
SQL-объявление функции должно выглядеть следующим образом:
CREATE FUNCTION my_picksplit(internal, internal) RETURNS void ...
Первый аргумент - указатель на структуру spgPickSplitIn
языка C, содержащую входные данные для функции. Второй аргумент - указатель на структуру spgPickSplitOut
языка C, которую функция должна заполнить данными о результате.
typedef struct spgPickSplitIn
{
int nTuples; /* number of leaf tuples */
Datum *datums; /* their datums (array of length nTuples) */
int level; /* current level (counting from zero) */
} spgPickSplitIn;
typedef struct spgPickSplitOut
{
bool hasPrefix; /* new inner tuple should have a prefix? */
Datum prefixDatum; /* if so, its value */
int nNodes; /* number of nodes for new inner tuple */
Datum *nodeLabels; /* their labels (or NULL for no labels) */
int *mapTuplesToNodes; /* node index for each leaf tuple */
Datum *leafTupleDatums; /* datum to store in each new leaf tuple */
} spgPickSplitOut;
nTuples
– количество предоставленных кортежей листьев. datums
– массив их значений datum
типа spgConfigOut.leafType
. level
- текущий уровень, который разделяют все кортежи листьев и который станет уровнем нового внутреннего кортежа.
Установите hasPrefix
, чтобы указать, должен ли новый внутренний кортеж иметь префикс, и если да, установите prefixDatum
в значение префикса. Установите nNodes
, чтобы указать количество узлов, которые будет содержать новый внутренний кортеж, и установите nodeLabels
в массив значений их меток, или в NULL, если метки узлов не требуются. Установите mapTuplesToNodes
в массив, задающий индекс (от нуля) узла, которому должен быть присвоен каждый кортеж листа. Установите leafTupleDatums
в массив значений, которые будут храниться в новых кортежах листьев (они будут такими же, как и входные значения, если класс оператора не изменяет значения от одного уровня к другому). Обратите внимание, что функция picksplit
отвечает за распределение массивов nodeLabels
, mapTuplesToNodes
и leafTupleDatums
.
Если предоставлено более одного кортежа листьев, ожидается, что функция picksplit
отнесет их к нескольким узлам; иначе невозможно разделить кортежи листьев по нескольким страницам, что является конечной целью этой операции. Поэтому, если функция picksplit
в итоге поместит все кортежи листьев в один узел, основной код SP-GiST отменит это решение и сгенерирует внутренний кортеж, в котором кортежи листьев будут случайным образом распределены по нескольким одинаково помеченным узлам. Такой кортеж помечается как allTheSame
, чтобы показать, что это произошло. Функции choose
и inner_consistent
должны проявлять соответствующую осторожность при работе с такими внутренними кортежами. Более подробную информацию см. в разделе «Внутренние кортежи "все-равны"»
Операция picksplit
может быть применена к кортежу с одним листом только в том случае, если функция config
установила значение longValuesOK
в true
и было предоставлено входное значение, превышающее размер страницы. В этом случае смысл операции заключается в удалении префикса и создании нового, более короткого значения данных листа. Вызов будет повторяться до тех пор, пока не будет получено достаточно короткое значение leaf datum
, чтобы поместиться на странице. Дополнительные сведения см. в разделе «Лимиты SP-GiST»
inner_consistent
Возвращает набор узлов (ветвей), по которым следует двигаться при поиске дерева.
SQL-объявление функции должно выглядеть следующим образом:
CREATE FUNCTION my_inner_consistent(internal, internal) RETURNS void ...
Первый аргумент – указатель на структуру spgInnerConsistentIn
языка C, содержащую входные данные для функции. Второй аргумент - указатель на структуру spgInnerConsistentOut
языка C, которую функция должна заполнить данными о результате.
typedef struct spgInnerConsistentIn
{
ScanKey scankeys; /* array of operators and comparison values */
ScanKey orderbys; /* array of ordering operators and comparison
* values */
int nkeys; /* length of scankeys array */
int norderbys; /* length of orderbys array */
Datum reconstructedValue; /* value reconstructed at parent */
void *traversalValue; /* opclass-specific traverse value */
MemoryContext traversalMemoryContext; /* put new traverse values here */
int level; /* current level (counting from zero) */
bool returnData; /* original data must be returned? */
/* Data from current inner tuple */
bool allTheSame; /* tuple is marked all-the-same? */
bool hasPrefix; /* tuple has a prefix? */
Datum prefixDatum; /* if so, the prefix value */
int nNodes; /* number of nodes in the inner tuple */
Datum *nodeLabels; /* node label values (NULL if none) */
} spgInnerConsistentIn;
typedef struct spgInnerConsistentOut
{
int nNodes; /* number of child nodes to be visited */
int *nodeNumbers; /* their indexes in the node array */
int *levelAdds; /* increment level by this much for each */
Datum *reconstructedValues; /* associated reconstructed values */
void **traversalValues; /* opclass-specific traverse values */
double **distances; /* associated distances */
} spgInnerConsistentOut;
Массив scankeys
длины nkeys
описывает условие(я) поиска по индексу. Эти условия объединяются с помощью AND
- интересны только те записи индекса, которые удовлетворяют всем этим условиям. (Примечание что nkeys = 0
означает, что все за писи индекса удовлетворяют запросу). Обычно последовательная функция интересуется только полями sk_strategy
и sk_argument
каждого элемента массива, которые повторно указывают индексируемый оператор и значение сравнения. В частности, нет необходимости проверять sk_flags
на предмет того, не является ли значение сравнения NULL, поскольку код ядра SP-GiST отфильтрует такие условия. Массив orderbys
длины norderbys
описывает операторы упорядочивания (если таковые имеются) аналогичным образом. reconstructedValue
- значение, реконструированное для родительского кортежа; оно равно (Datum
) 0
на корневом уровне или если функция inner_consistent
не предоставила значения на родительском уровне. traversalValue
- указатель на любые данные траверса, переданные из предыдущего вызова inner_consistent
для родительского кортежа индексов, или NULL на корневом уровне. traversalMemoryContext
- контекст памяти, в котором будут храниться выходные значения траверса (см. ниже). level
- текущий уровень внутреннего кортежа, начиная с нуля для корневого уровня. returnData
- истина, если для этого запроса требуются реконструированные данные; это будет так, только если функция config
утверждает canReturnData
. allTheSame
- истина, если текущий внутренний кортеж помечен как «all-the-same» (все равны); в этом случае все узлы имеют одну и ту же метку (если она есть) и поэтому либо все, либо ни один из них не соответствует запросу (см. раздел «Внутренние кортежи "все-равны"»). hasPrefix
- истина, если текущий внутренний кортеж содержит префикс; если да, то prefixDatum
- его значение. nNodes
- количество дочерних узлов, содержащихся во внутреннем кортеже, а nodeLabels
- массив значений их меток, или NULL, если узлы не имеют меток.
nNodes
должно быть задано количество дочерних узлов, которые должны быть посещены при поиске, а nodeNumbers
- массив их индексов. Если класс оператора отслеживает уровни, установите levelAdds
в массив приращений уровней, необходимых при спуске к каждому узлу, который необходимо посетить. (Часто эти приращения будут одинаковыми для всех узлов, но это не обязательно, поэтому используется массив). Если требуется реконструкция значений, установите reconstructedValues
в массив значений, реконструированных для каждого посещаемого дочернего узла; в противном случае оставьте reconstructedValues
как NULL. Предполагается, что реконструированные значения имеют тип spgConfigOut.leafType
. (Однако, поскольку система ядра не будет ничего с ними делать, кроме возможного копирования, достаточно, чтобы они имели те же свойства typlen
и typbyval
, что и leafType
). Если выполняется упорядоченный поиск, установите distances
в массив значений расстояний в соответствии с массивом orderbys
(узлы с наименьшим расстоянием будут обработаны первыми). В противном случае оставьте значение NULL. Если необходимо передать дополнительную внеполосную информацию («значения обхода») на более низкие уровни поиска по дереву, установите traversalValues
в массив соответствующих значений обхода, по одному для каждого дочернего узла, который будет посещен; в противном случае оставьте traversalValues
как NULL. Обратите внимание, что функция inner_consistent
отвечает за размещение массивов nodeNumbers
, levelAdds
, distances
, reconstructedValues
и traversalValues
в текущем контексте памяти. Однако все выходные значения траверса, на которые указывает массив traversalValues
, должны быть выделены в traversalMemoryContext
. Каждое значение траверса должно быть одним выделенным чанком.
leaf_consistent
Возвращает true
, если кортеж листьев удовлетворяет запросу.
Объявление функции в формате SQL должно выглядеть следующим образом:
CREATE FUNCTION my_leaf_consistent(internal, internal) RETURNS bool ...
Первый аргумент - указатель на структуру spgLeafConsistentIn
языка C, содержащую входные данные для функции. Второй аргумент - указатель на структуру spgLeafConsistentOut
языка C, которую функция должна заполнить данными о результате.
typedef struct spgLeafConsistentIn
{
ScanKey scankeys; /* array of operators and comparison values */
ScanKey orderbys; /* array of ordering operators and comparison
* values */
int nkeys; /* length of scankeys array */
int norderbys; /* length of orderbys array */
Datum reconstructedValue; /* value reconstructed at parent */
void *traversalValue; /* opclass-specific traverse value */
int level; /* current level (counting from zero) */
bool returnData; /* original data must be returned? */
Datum leafDatum; /* datum in leaf tuple */
} spgLeafConsistentIn;
typedef struct spgLeafConsistentOut
{
Datum leafValue; /* reconstructed original data, if any */
bool recheck; /* set true if operator must be rechecked */
bool recheckDistances; /* set true if distances must be rechecked */
double *distances; /* associated distances */
} spgLeafConsistentOut;
Массив scankeys
длины nkeys
описывает условие(я) поиска по индексу. Эти условия объединяются оператором AND
– только те записи индекса, которые удовлетворяют всем из них, удовлетворяют запросу. (Заметим, что nkeys = 0
подразумевает, что все записи индекса удовлетворяют запросу). Обычно последовательную функцию интересуют только поля sk_strategy
и sk_argument
каждой записи массива, которые повторно указывают индексируемый оператор и значение сравнения. В частности, нет необходимости проверять sk_flags
на предмет того, не является ли значение сравнения NULL, поскольку код ядра SP-GiST отфильтрует такие условия. Массив orderbys
длины norderbys
описывает операторы упорядочивания аналогичным образом. reconstructedValue
- значение, реконструированное для родительского кортежа; оно равно (Datum
) 0
на корневом уровне или если функция inner_consistent
не предоставила значения на родительском уровне. traversalValue
- указатель на любые данные траверса, переданные из предыдущего вызова inner_consistent
на родительском индексном кортеже, или NULL на корневом уровне. level
- уровень текущего кортежа листьев, начиная с нуля для корневого уровня. returnData
- true
, если для этого запроса требуются реконструированные данные; это будет так, только если функция config
утверждает canReturnData
. leafDatum
- ключевое значение spgConfigOut.leafType
, хранящееся в текущем кортеже листьев.
Функция должна возвращать true
, если кортеж листа соответствует запросу, или false
, если нет. В истинном случае, если returnData
равен true
, то leafValue
должно быть установлено в значение (типа spgConfigIn.attType
), первоначально предоставленное для индексирования этого кортежа листьев. Кроме того, перепроверка может быть установлена в true
, если совпадение неопределено, и поэтому оператор(ы) должен(ы) быть повторно применен(ы) к фактическому кортежу кучи, чтобы проверить совпадение. Если выполняется упорядоченный поиск, установите distances
в массив значений расстояний в соответствии с массиво м orderbys
. В противном случае оставьте значение NULL. Если хотя бы одно из возвращаемых значений расстояний не является точным, установите для параметра recheckDistances
значение true
. В этом случае исполнитель вычислит точные расстояния после извлечения кортежа из кучи и при необходимости переупорядочит кортежи.
Дополнительными пользовательскими методами являются:
Datum compress (Datum in)
Преобразует элемент данных в формат, подходящий для физического хранения в листовом кортеже индекса. Принимает значение типа spgConfigIn.attType
и возвращает значение типа spgConfigOut.leafType
. Возвращаемое значение не должно содержать указатель на внешние TOAST-данные.
Обратите внимание, метод сжатия применяется только к значениям, подлежащим хранению. Консистентные методы получают scankeys запросов в неизменном виде, без преобразования с помощью сжатия.
options
Определяет набор видимых пользователю параметров, которые управляют поведением класса оператора.
SQL-объявление функции должно выглядеть следующим образом:
CREATE OR REPLACE FUNCTION my_options(internal)
RETURNS void
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
Функции передается указатель на структуру local_relopts
, которая должна быть заполнена набором опций, специфичных для класса оператора. Доступ к опциям можно получить из других функций поддержки с помощью макросов PG_HAS_OPCLASS_OPTIONS()
и PG_GET_OPCLASS_OPTIONS()
.
Поскольку представление ключа в SP-GiST является гибким, оно может зависеть от заданных пользователем параметров.
Все методы поддержки SP-GiST обычно вызываются в кратковременных контекстах памяти; то есть CurrentMemoryContext
будет сброшен после обработки каждого кортежа. Таким образом, можно не заботиться об освобождении любых блоков памяти, выделенных функцией palloc. (Метод config
является исключением: он должен стараться избегать утечек памяти. Но обычно метод config
не должен делать ничего, кроме как присваивать константы в переданную структуру параметров).
Если индексируемый столбец имеет сортируемый тип данных, правило сортировки индекса будет передаваться всем опорным методам, используя стандартный механизм PG_GET_COLLATION()
.