Перейти к основному содержимому

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

Интерфейс GIN имеет высокий уровень абстракции, требуя от исполнителя метода доступа только реализации семантики типа данных, к которым осуществляется доступ. Уровень GIN сам заботится о согласовании, протоколировании и поиске в древовидной структуре.

Чтобы заставить работать метод доступа GIN, достаточно реализовать несколько пользовательских методов, которые определяют поведение ключей в дереве и отношения между ключами, индексированными элементами и индексируемыми запросами. Одним словом, GIN сочетает в себе расширяемость с универсальностью, повторным использованием кода и чистым интерфейсом.

Есть два метода, которые должен предоставлять класс оператора для GIN:

Datum *extractValue(Datum itemValue, int32 *nkeys, bool **nullFlags)

Возвращает массив ключей palloc, заданный индексируемым элементом. Количество возвращаемых ключей должно храниться в *nkeys. Если какой-либо из ключей может быть нулевым, также выделите массив bool-полей *nkeys, сохраните его адрес в *nullFlags и установите эти нулевые флаги по мере необходимости. *nullFlags можно оставить NULL (его начальное значение), если все ключи не являются нулевыми. Возвращаемое значение может быть NULL, если элемент не содержит ключей.

Datum *extractQuery(Datum query, int32 *nkeys, StrategyNumber n, bool **pmatch, Pointer **extra_data, bool **nullFlags, int32 *searchMode)

Возвращает массив ключей palloc, заданный значением для запроса; то есть запрос - это значение в правой части индексируемого оператора, левая часть которого - индексируемый столбец. n - это номер стратегии оператора в классе операторов. Часто extractQuery приходится обращаться к n, чтобы определить тип данных запроса и метод, который он должен использовать для извлечения значений ключей. Количество возвращаемых ключей должно храниться в *nkeys. Если какой-либо из ключей может быть нулевым, также выделите массив bool-полей *nkeys, сохраните его адрес в *nullFlags и установите эти нулевые флаги по мере необходимости. *nullFlags можно оставить NULL (его начальное значение), если все ключи не являются нулевыми. Возвращаемое значение может быть NULL, если запрос не содержит ключей.

searchMode - выходной аргумент, который позволяет extractQuery указать детали того, как будет выполняться поиск. Если *searchMode установлен в GIN_SEARCH_MODE_DEFAULT (это значение инициализируется перед вызовом), то только элементы, которые совпадают хотя бы с одним из возвращаемых ключей, считаются совпадениями-кандидатами. Если *searchMode установлен в GIN_SEARCH_MODE_INCLUDE_EMPTY, то в дополнение к элементам, содержащим хотя бы один совпадающий ключ, элементами-кандидатами считаются элементы, не содержащие вообще никаких ключей. (Этот режим полезен, например, для реализации операторов A-является-подмножеством-B.) Если *searchMode установлен в GIN_SEARCH_MODE_ALL, то все ненулевые элементы в индексе считаются совпадениями-кандидатами, независимо от того, соответствуют они какому-либо из возвращаемых ключей или нет. (Этот режим намного медленнее, чем два других варианта, поскольку требует сканирования практически всего индекса, но он может быть необходим для корректной реализации угловых случаев. Оператор, которому этот режим нужен в большинстве случаев, вероятно, не является хорошим кандидатом для класса оператора GIN.) Символы, используемые для установки этого режима, определены в файле access/gin.h.

pmatch - выходной аргумент для использования в случае поддержки частичного совпадения. Чтобы использовать его, extractQuery должен выделить массив из *nkeys логических элементов и сохранить его адрес в *pmatch. Каждый элемент массива должен быть установлен в true, если соответствующий ключ требует частичного совпадения, и false, если нет. Если *pmatch установлен в NULL, то GIN считает, что частичное совпадение не требуется. Переменная инициализируется в NULL перед вызовом, поэтому этот аргумент может быть просто проигнорирован операторными классами, не поддерживающими частичное совпадение.

extra_data - это выходной аргумент, который позволяет extractQuery передавать дополнительные данные методам consistent и comparePartial. Чтобы использовать его, extractQuery должен выделить массив указателей *nkeys и сохранить его адрес по адресу *extra_data, а затем записать в отдельные указатели все, что захочет. Перед вызовом переменная инициализируется в NULL, поэтому этот аргумент может быть просто проигнорирован операторными классами, которым не нужны дополнительные данные. Если *extra_data задана, то весь массив передается в метод consistent, а соответствующий элемент - в метод comparePartial.

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

bool consistent(bool check[], StrategyNumber n, Datum query, int32 nkeys, Pointer extra_data[], bool *recheck, Datum queryKeys[], bool nullFlags[])

Возвращает true, если индексированный элемент удовлетворяет оператору запроса со стратегией номер n (или может удовлетворять, если возвращается признак перепроверки). Эта функция не имеет прямого доступа к значению индексированного элемента, поскольку GIN не хранит элементы в явном виде. Скорее, доступны знания о том, какие значения ключей, извлеченные из запроса, встречаются в данном индексированном элементе. Массив проверок имеет длину nkeys, которая равна количеству ключей, ранее возвращенных extractQuery для данного элемента запроса. Каждый элемент массива check равен true, если индексированный элемент содержит соответствующий ключ запроса, т.е. если (check[i] == true) i-й ключ из массива результатов extractQuery присутствует в индексированном элементе. Исходный массив данных запроса передается на случай, если консистентному методу потребуется обратиться к нему, как и массивы queryKeys[] и nullFlags[], ранее возвращенные extractQuery. extra_data - массив дополнительных данных, возвращенных extractQuery, или NULL, если их нет.

Когда extractQuery возвращает нулевой ключ в queryKeys[], соответствующий элемент check[] будет истинным, если индексируемый элемент содержит нулевой ключ; то есть семантика check[] похожа на IS NOT DISTINCT FROM. Консистентная функция может проверить соответствующий элемент nullFlags[], если ей нужно определить разницу между совпадением обычного значения и совпадением с нулем.

В случае успеха значение *recheck должно быть установлено в true, если кортеж кучи нуждается в повторной проверке на соответствие оператору запроса, или в false, если проверка индекса является точной. То есть, ложное возвращаемое значение гарантирует, что кортеж кучи не соответствует запросу; истинное возвращаемое значение с *recheck, установленным в false, гарантирует, что кортеж кучи соответствует запросу; а истинное возвращаемое значение с *recheck, установленным в true, означает, что кортеж кучи может соответствовать запросу, поэтому его нужно получить и перепроверить, оценив оператор запроса непосредственно по первоначально проиндексированному элементу.

GinTernaryValue triConsistent(GinTernaryValue check[], StrategyNumber n, Datum query, int32 nkeys, Pointer extra_data[], Datum queryKeys[], bool nullFlags[])

triConsistent аналогичен consistent, но вместо булевых чисел в проверочном векторе для каждого ключа есть три возможных значения: GIN_TRUE, GIN_FALSE и GIN_MAYBE. GIN_FALSE и GIN_TRUE имеют тот же смысл, что и обычные булевы значения, а GIN_MAYBE означает, что наличие данного ключа неизвестно. Когда присутствуют значения GIN_MAYBE, функция должна возвращать GIN_TRUE только в том случае, если элемент определенно совпадает, независимо от того, содержит ли элемент индекса соответствующие ключи запроса или нет. Аналогично, функция должна возвращать GIN_FALSE только в том случае, если элемент точно не совпадает, независимо от того, содержит он ключи GIN_MAYBE или нет. Если результат зависит от записей GIN_MAYBE, т.е. совпадение не может быть подтверждено или опровергнуто на основе известных ключей запроса, функция должна возвращать GIN_MAYBE.

Если в векторе проверок нет значений GIN_MAYBE, возвращаемое значение GIN_MAYBE эквивалентно установке флага перепроверки в булевой последовательной функции.

Кроме того, GIN должен иметь способ сортировки значений ключей, хранящихся в индексе. Класс оператора может определить порядок сортировки, указав метод сравнения:

int compare(Datum a, Datum b)

Сравнивает два ключа (не индексированные элементы!) и возвращает целое число меньше нуля, ноль или больше нуля, указывающее, меньше, равен или больше первый ключ, чем второй. Нулевые ключи никогда не передаются в эту функцию.

В качестве альтернативы, если класс оператора не предоставляет метод compare, GIN найдет класс оператора B-дерева по умолчанию для типа данных индексного ключа и использует его функцию сравнения. Рекомендуется указывать функцию сравнения в операторном классе GIN, предназначенном только для одного типа данных, поскольку поиск класса оператора B-дерева занимает несколько циклов. Однако полиморфные классы GIN-операторов (например, array_ops) обычно не могут указать единственную функцию сравнения.

int comparePartial(Datum partial_key, Datum key, StrategyNumber n, Pointer extra_data)

Сравнивает ключ частичного совпадения запроса с ключом индекса. Возвращает целое число, знак которого указывает на результат: меньше нуля означает, что индексный ключ не соответствует запросу, но сканирование индекса следует продолжить; ноль означает, что индексный ключ соответствует запросу; больше нуля означает, что сканирование индекса следует прекратить, поскольку больше совпадений невозможно. Указывается номер стратегии n оператора, сгенерировавшего запрос частичного совпадения, на случай, если его семантика понадобится для определения момента завершения сканирования. Кроме того, extra_data – это соответствующий элемент массива extra-data, созданного extractQuery, или NULL, если его нет. Нулевые ключи никогда не передаются в эту функцию.

void options(local_relopts *relopts)

Определяет набор видимых пользователю параметров, которые управляют поведением класса операторов.

Функции options передается указатель на структуру local_relopts, которая должна быть заполнена набором опций, специфичных для класса оператора. Доступ к опциям можно получить из других функций поддержки с помощью макросов PG_HAS_OPCLASS_OPTIONS() и PG_GET_OPCLASS_OPTIONS().

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

Для поддержки запросов «частичного совпадения» класс оператора должен предоставлять метод comparePartial, а его метод extractQuery должен устанавливать параметр pmatch, когда выполняется запрос частичного совпадения. Подробности см. в разделе «Алгоритм частичного совпадения»

Фактические типы данных различных значений Datum, упомянутых выше, зависят от класса оператора. Значения элементов, передаваемые в extractValue, всегда имеют входной тип класса оператора, а все значения ключей должны иметь тип STORAGE класса. Тип аргумента запроса, передаваемого в extractQuery, consistent и triConsistent, равен правому входному типу оператора-члена класса, обозначенного номером стратегии. Он не обязательно должен совпадать с индексированным типом, если из него могут быть извлечены ключевые значения нужного типа. Однако рекомендуется, чтобы в SQL-декларациях этих трех вспомогательных функций для аргумента запроса использовался индексированный тип данных класса opclass, даже если фактический тип может быть другим в зависимости от оператора.