66.3. Расширяемость#
66.3. Расширяемость #
SP-GiST предлагает интерфейс с высоким уровнем абстракции, требуя от разработчика метода доступа реализовать только методы, специфичные для определенного типа данных. Основная часть SP-GiST отвечает за эффективное отображение на диске и поиск в структуре дерева. Он также заботится о параллельности и рассмотрении журнала.
Все листовые кортежи дерева SP-GiST обычно содержат значения того же типа данных, что и индексируемый столбец, хотя также возможно, что они содержат упрощенные представления индексируемого столбца. Листовые кортежи, хранящиеся на корневом уровне, будут напрямую представлять исходное значение индексируемых данных, но листовые кортежи на более низких уровнях могут содержать только частичное значение, например, суффикс. В этом случае функции поддержки класса операторов должны быть способны восстановить исходное значение, используя информацию, накопленную из внутренних кортежей, которые передаются для достижения листового уровня.
Когда индекс SP-GiST создается с использованием
колонок INCLUDE, значения этих колонок также
хранятся в листовых кортежах. Колонки INCLUDE не
имеют значения для класса операторов SP-GiST, поэтому
они здесь не обсуждаются далее.
Внутренние кортежи более сложны, поскольку они являются точками разветвления в дереве поиска. Каждый внутренний кортеж содержит набор одного или более узлов, которые представляют группы похожих листовых значений. Узел содержит ссылку, которая ведет либо к другому, более низкоуровневому внутреннему кортежу, либо к короткому списку листовых кортежей, которые все находятся на одной странице индекса. Каждый узел обычно имеет метку, которая его описывает; например, в радиксном дереве меткой узла может быть следующий символ строки значения. (В качестве альтернативы, класс оператора может опустить метки узлов, если он работает с фиксированным набором узлов для всех внутренних кортежей; см. Раздел 66.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, содержащую входные данные для функции. Второй аргумент - указатель на структуру CspgConfigOut, которую функция должна заполнить данными результата.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имеет переменную длину, и класс операторов способен разделить длинные значения путем повторного добавления суффиксов (см. Раздел 66.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, содержащую входные данные для функции. Второй аргумент - указатель на структуру CspgChooseOut, которую функция должна заполнить данными результата.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, если текущий внутренний кортеж помечен как содержащий несколько эквивалентных узлов (см. Раздел 66.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, содержащую входные данные для функции. Второй аргумент - указатель на структуру CspgPickSplitOut, которую функция должна заполнить результатами.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должны принимать соответствующие меры с такими внутренними кортежами. См. Раздел 66.4.3 для получения дополнительной информации.picksplitможет быть применена только к одной листовой кортежу в случае, если функцияconfigустановилаlongValuesOKв true и было предоставлено значение, превышающее размер страницы. В этом случае цель операции - удалить префикс и создать новое, более короткое значение листа. Вызов будет повторяться, пока не будет создано значение листа достаточно короткое для помещения на страницу. См. Раздел 66.4.1 для получения дополнительной информации.inner_consistentВозвращает набор узлов (ветвей), которые нужно проследовать во время поиска по дереву.
Декларация функции SQL должна выглядеть так:
CREATE FUNCTION my_inner_consistent(internal, internal) RETURNS void ...
Первый аргумент - указатель на структуру C
spgInnerConsistentIn, содержащую входные данные для функции. Второй аргумент - указатель на структуру CspgInnerConsistentOut, которую функция должна заполнить результатами.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”; в этом случае все узлы имеют одинаковую метку (если есть) и поэтому либо все, либо ни один из них соответствуют запросу (см. Раздел 66.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, содержащую входные данные для функции. Второй аргумент - указатель на структуру CspgLeafConsistentOut, которую функция должна заполнить данными результата.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().