67.3. Расширяемость#

67.3. Расширяемость

67.3. Расширяемость

SP-GiST предлагает интерфейс с высоким уровнем абстракции, требуя от разработчика метода доступа реализовать только методы, специфичные для определенного типа данных. Основная часть SP-GiST отвечает за эффективное отображение на диске и поиск в структуре дерева. Он также заботится о параллельности и рассмотрении журнала.

Все листовые кортежи дерева SP-GiST обычно содержат значения того же типа данных, что и индексируемый столбец, хотя также возможно, что они содержат упрощенные представления индексируемого столбца. Листовые кортежи, хранящиеся на корневом уровне, будут напрямую представлять исходное значение индексируемых данных, но листовые кортежи на более низких уровнях могут содержать только частичное значение, например, суффикс. В этом случае функции поддержки класса операторов должны быть способны восстановить исходное значение, используя информацию, накопленную из внутренних кортежей, которые передаются для достижения листового уровня.

Когда индекс SP-GiST создается с использованием колонок INCLUDE, значения этих колонок также хранятся в листовых кортежах. Колонки INCLUDE не имеют значения для класса операторов SP-GiST, поэтому они здесь не обсуждаются далее.

Внутренние кортежи более сложны, поскольку они являются точками разветвления в дереве поиска. Каждый внутренний кортеж содержит набор одного или более узлов, которые представляют группы похожих листовых значений. Узел содержит ссылку, которая ведет либо к другому, более низкоуровневому внутреннему кортежу, либо к короткому списку листовых кортежей, которые все находятся на одной странице индекса. Каждый узел обычно имеет метку, которая его описывает; например, в радиксном дереве меткой узла может быть следующий символ строки значения. (В качестве альтернативы, класс оператора может опустить метки узлов, если он работает с фиксированным набором узлов для всех внутренних кортежей; см. Раздел 67.4.2). По желанию, внутренний кортеж может иметь префиксное значение, которое описывает всех его членов. В радиксном дереве это может быть общий префикс представленных строк. Значение префикса не обязательно является действительным префиксом, но может быть любыми данными, необходимыми классу оператора; например, в квадродереве оно может хранить центральную точку, относительно которой измеряются четыре квадранта. Внутренний кортеж квадродерева также содержит четыре узла, соответствующих квадрантам вокруг этой центральной точки.

Некоторые алгоритмы деревьев требуют знания уровня (или глубины) текущей кортежа, поэтому ядро SP-GiST предоставляет возможность операторным классам управлять подсчетом уровня при спуске по дереву. Также поддерживается инкрементальное восстановление представленного значения, когда это необходимо, и передача дополнительных данных (называемых значениями обхода) во время спуска по дереву.

Примечание

Основной код SP-GiST заботится о пустых записях. Хотя индексы SP-GiST хранят записи для пустых значений в индексируемых столбцах, это скрыто от кода класса оператора индекса: никакие записи индекса или условия поиска для пустых значений никогда не передаются методам класса оператора. (Предполагается, что операторы SP-GiST являются строгими и поэтому не могут быть успешными для пустых значений). Пустые значения здесь далее не обсуждаются.

Всего существует пять пользовательских методов, которые должен предоставить класс операторов индекса для SP-GiST, и два из них являются необязательными. Все пять обязательных методов следуют соглашению о принятии двух аргументов типа internal, первый из которых является указателем на структуру C, содержащую входные значения для метода поддержки, а второй аргумент является указателем на структуру C, в которую должны быть помещены выходные значения. Четыре из обязательных методов просто возвращают void, так как все их результаты появляются в выходной структуре; но метод leaf_consistent возвращает логическое значение boolean. Методы не должны изменять никакие поля своих входных структур. Во всех случаях выходная структура инициализируется нулями перед вызовом пользовательского метода. Необязательный шестой метод compress принимает значение типа datum, которое должно быть проиндексировано, в качестве единственного аргумента и возвращает значение, подходящее для физического хранения в кортеже листа. Необязательный седьмой метод options принимает указатель типа internal на структуру C, в которую должны быть помещены параметры, специфичные для opclass, и возвращает void.

Все пять обязательных пользовательских методов:

config

Возвращает статическую информацию о реализации индекса, включая типы данных OIDs префикса и типов данных меток узлов.

Декларация функции SQL должна выглядеть так:

CREATE FUNCTION my_config(internal, internal) RETURNS void ...

Первый аргумент - указатель на структуру C spgConfigIn, содержащую входные данные для функции. Второй аргумент - указатель на структуру C spgConfigOut, которую функция должна заполнить данными результата.

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 имеет переменную длину, и класс операторов способен разделить длинные значения путем повторного добавления суффиксов (см. Раздел 67.4.1).

Соответствие типа хранения индекса, определенного типом ключа операторного класса opckeytype, должно совпадать с leafType в каталоге записи. (Обратите внимание, что opckeytype может быть равен нулю, что означает, что тип хранения совпадает с типом ввода операторного класса, что является наиболее распространенной ситуацией). По соображениям обратной совместимости метод config может устанавливать leafType в другое значение, и это значение будет использоваться; но это устарело, так как содержимое индекса неправильно идентифицируется в каталогах. Также допускается оставить leafType неинициализированным (нулевым); это интерпретируется как означающее тип хранения индекса, производный от opckeytype.

Когда attType и leafType отличаются, необходимо предоставить необязательный метод compress. Метод compress отвечает за преобразование данных, которые будут индексироваться, из attType в leafType.

choose

Выбирает метод для вставки нового значения во внутренний кортеж.

Декларация функции SQL должна выглядеть так:

CREATE FUNCTION my_choose(internal, internal) RETURNS void ...

Первый аргумент - указатель на структуру C spgChooseIn, содержащую входные данные для функции. Второй аргумент - указатель на структуру C spgChooseOut, которую функция должна заполнить данными результата.

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, если текущий внутренний кортеж помечен как содержащий несколько эквивалентных узлов (см. Раздел 67.4.3). hasPrefix - это значение true, если текущий внутренний кортеж содержит префикс; если это так, то prefixDatum - это его значение. nNodes - это количество дочерних узлов, содержащихся во внутреннем кортеже, а nodeLabels - это массив их меток значений или NULL, если меток нет.

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

Если новое значение совпадает с одним из существующих дочерних узлов, установите resultType в spgMatchNode. Установите nodeN в индекс (начиная с нуля) этого узла в массиве узлов. Установите levelAdd в приращение level, вызванное спуском через этот узел, или оставьте его равным нулю, если класс операторов не использует уровни. Установите 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 ...

Первый аргумент - указатель на структуру C spgPickSplitIn, содержащую входные данные для функции. Второй аргумент - указатель на структуру C spgPickSplitOut, которую функция должна заполнить результатами.

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 - это массив значений их данных для типа spgConfigOut.leafType. level - это текущий уровень, который все листовые кортежи разделяют, и который станет уровнем нового внутреннего кортежа.

Установите hasPrefix для указания, должен ли новый внутренний кортеж иметь префикс, и если да, установите prefixDatum в значение префикса. Установите nNodes для указания количества узлов, которые будут содержаться в новом внутреннем кортеже, и установите nodeLabels в массив их меток значений или в NULL, если метки узлов не требуются. Установите mapTuplesToNodes в массив, который указывает индекс (от нуля) узла, к которому должен быть присвоен каждый листовой кортеж. Установите leafTupleDatums в массив значений, которые должны быть сохранены в новых листовых кортежах (они будут такими же, как datums ввода, если класс оператора не изменяет значения от одного уровня к другому). Обратите внимание, что функция picksplit отвечает за выделение памяти для массивов nodeLabels, mapTuplesToNodes и leafTupleDatums.

Если предоставлено более одной листовой кортеж, ожидается, что функция picksplit классифицирует их в более чем один узел; в противном случае невозможно разделить листовые кортежи на несколько страниц, что является конечной целью этой операции. Поэтому, если функция picksplit размещает все листовые кортежи в одном узле, код ядра SP-GiST переопределит это решение и сгенерирует внутренний кортеж, в котором листовые кортежи случайным образом распределяются по нескольким узлам с одинаковыми метками. Такой кортеж помечен как allTheSame для обозначения этого события. Функции choose и inner_consistent должны принимать соответствующие меры с такими внутренними кортежами. См. Раздел 67.4.3 для получения дополнительной информации.

picksplit может быть применена только к одной листовой кортежу в случае, если функция config установила longValuesOK в true и было предоставлено значение, превышающее размер страницы. В этом случае цель операции - удалить префикс и создать новое, более короткое значение листа. Вызов будет повторяться, пока не будет создано значение листа достаточно короткое для помещения на страницу. См. Раздел 67.4.1 для получения дополнительной информации.

inner_consistent

Возвращает набор узлов (ветвей), которые нужно проследовать во время поиска по дереву.

Декларация функции SQL должна выглядеть так:

CREATE FUNCTION my_inner_consistent(internal, internal) RETURNS void ...

Первый аргумент - указатель на структуру C spgInnerConsistentIn, содержащую входные данные для функции. Второй аргумент - указатель на структуру C spgInnerConsistentOut, которую функция должна заполнить результатами.

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 означает, что все записи индекса удовлетворяют запросу). Обычно функция consistent заботится только о полях sk_strategy и sk_argument каждой записи массива, которые соответственно предоставляют оператор индексации и значение сравнения. В частности, необходимо проверять sk_flags для определения, является ли значение сравнения NULL, потому что ядро кода SP-GiST будет фильтровать такие условия. Массив orderbys, длиной norderbys, описывает операторы сортировки (если они есть) таким же образом. reconstructedValue - это значение, восстановленное для родительской кортежи; на корневом уровне или если функция inner_consistent не предоставила значение на родительском уровне, оно равно (Datum) 0. traversalValue - это указатель на любые данные обхода, переданные из предыдущего вызова inner_consistent для родительского индексного кортежа, или NULL на корневом уровне. traversalMemoryContext - это контекст памяти, в котором хранятся значения обхода (см. ниже). level - это текущий уровень внутреннего кортежа, начиная с нуля для корневого уровня. returnData - это true, если требуется восстановленные данные для этого запроса; это будет так только в том случае, если функция config утверждает canReturnData. allTheSame - true, если текущий внутренний кортеж помечен как all-the-same; в этом случае все узлы имеют одинаковую метку (если есть) и поэтому либо все, либо ни один из них соответствуют запросу (см. Раздел 67.4.3). hasPrefix - true, если текущий внутренний кортеж содержит префикс; если это так, 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. Каждое значение обхода должно быть выделено в отдельном блоке памяти, выделенном с помощью palloc.

leaf_consistent

Возвращает true, если листовая кортеж удовлетворяет запросу.

Декларация функции SQL должна выглядеть так:

CREATE FUNCTION my_leaf_consistent(internal, internal) RETURNS bool ...

Первый аргумент - указатель на структуру C spgLeafConsistentIn, содержащую входные данные для функции. Второй аргумент - указатель на структуру C spgLeafConsistentOut, которую функция должна заполнить данными результата.

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 означает, что все записи индекса удовлетворяют запросу). Обычно функция consistent заботится только о полях 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, если нет. В случае true, если returnData равно true, то leafValue должно быть установлено в значение (типа spgConfigIn.attType), которое было изначально предоставлено для индексации этого листового кортежа. Кроме того, recheck может быть установлено в true, если совпадение неопределенно, и поэтому оператор(ы) должны быть повторно применены к фактическому кортежу кучи для проверки совпадения. Если выполняется упорядоченный поиск, установите distances в массив значений расстояния в соответствии с массивом orderbys. В противном случае оставьте его NULL. Если хотя бы одно из возвращенных расстояний не является точным, установите recheckDistances в true. В этом случае исполнитель вычислит точные расстояния после получения кортежа из кучи и, при необходимости, переупорядочит кортежи.

Опциональные пользовательские методы:

Datum compress(Datum in)

Преобразует элемент данных в формат, подходящий для физического хранения в листовом кортеже индекса. Он принимает значение типа spgConfigIn.attType и возвращает значение типа spgConfigOut.leafType. Выходное значение не должно содержать указателя TOAST, находящегося вне строки.

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

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 будет сброшен после обработки каждой кортежи. Поэтому не очень важно беспокоиться о pfree'ing всего, что вы palloc'или. (Метод config является исключением: он должен стараться избегать утечки памяти. Но обычно метод config должен делать только присваивание констант в переданную структуру параметров).

Если индексируемый столбец имеет сортируемый тип данных, правило сортировки индекса будет передано всем поддерживаемым методам с использованием стандартного механизма PG_GET_COLLATION().