67.3. Расширяемость#
67.3. Расширяемость #
Интерфейс GIN имеет высокий уровень абстракции, требуя от реализатора метода доступа только реализации семантики обрабатываемого типа данных. Сам уровень GIN заботится о параллельности, журналировании и поиске в структуре дерева.
Все, что нужно сделать, чтобы заставить работать метод доступа GIN, это реализовать несколько пользовательских методов, которые определяют поведение ключей в дереве и отношения между ключами, индексируемыми элементами и индексируемыми запросами. Вкратце, GIN объединяет расширяемость с общностью, повторное использование кода и чистый интерфейс.
Есть два метода, которые операторный класс для GIN должен предоставить:
Datum *extractValue(Datum itemValue, int32 *nkeys, bool **nullFlags)
Возвращает массив ключей, выделенный с помощью palloc, для заданного элемента, который должен быть проиндексирован. Количество возвращенных ключей должно быть сохранено в переменной
*nkeys
. Если какие-либо ключи могут быть null, также выделяется массив полей*nkeys
типаbool
, адрес которого сохраняется в **nullFlags
, и устанавливаются соответствующие флаги null.*nullFlags
может быть оставленNULL
(его начальное значение), если все ключи не являются null. Возвращаемое значение может бытьNULL
, если элемент не содержит ключей.Datum *extractQuery(Datum query, int32 *nkeys, StrategyNumber n, bool **pmatch, Pointer **extra_data, bool **nullFlags, int32 *searchMode)
Возвращает массив ключей, выделенный с помощью palloc, при заданном значении для запроса; то есть,
query
- это значение справа от индексируемого оператора, левая часть которого - индексируемый столбец.n
- это номер стратегии оператора внутри класса операторов (см. Раздел 35.15.2). ЧастоextractQuery
должна обратиться кn
, чтобы определить тип данныхquery
и метод, который следует использовать для извлечения ключевых значений. Количество возвращаемых ключей должно быть сохранено в*nkeys
. Если какие-либо ключи могут быть null, также нужно выделить массив*nkeys
полейbool
, сохранить его адрес в*nullFlags
и установить эти флаги null по мере необходимости.*nullFlags
может быть оставленоNULL
(его начальное значение), если все ключи не являются null. Возвращаемое значение может бытьNULL
, еслиquery
не содержит ключей.searchMode
- это выходной аргумент, который позволяетextractQuery
указать детали о том, как будет выполняться поиск. Если*searchMode
установлено вGIN_SEARCH_MODE_DEFAULT
(что является его исходным значением перед вызовом), рассматриваются только элементы, которые соответствуют хотя бы одному из возвращенных ключей. Если*searchMode
установлено вGIN_SEARCH_MODE_INCLUDE_EMPTY
, то помимо элементов, содержащих хотя бы один совпадающий ключ, рассматриваются также элементы, не содержащие ключей вообще. (Этот режим полезен для реализации операторов подмножества, например). Если*searchMode
установлено вGIN_SEARCH_MODE_ALL
, то все ненулевые элементы в индексе рассматриваются как возможные совпадения, независимо от того, соответствуют ли они какому-либо из возвращенных ключей или нет. (Этот режим гораздо медленнее других двух выборов, так как требует практически полного сканирования индекса, но может быть необходим для правильной реализации крайних случаев. Оператор, который в большинстве случаев требует этот режим, вероятно, не является хорошим кандидатом для класса операторов GIN). Символы для установки этого режима определены вaccess/gin.h
.pmatch
- это выходной аргумент, используемый при поддержке частичного совпадения. Чтобы использовать его,extractQuery
должна выделить массив из*nkeys
элементов типаbool
и сохранить его адрес в*pmatch
. Каждый элемент массива должен быть установлен в true, если соответствующий ключ требует частичного совпадения, и в false, если нет. Если*pmatch
установлено вNULL
, то GIN предполагает, что частичное совпадение не требуется. Переменная инициализируется значениемNULL
перед вызовом, поэтому этот аргумент можно просто игнорировать операторными классами, которые не поддерживают частичное совпадение.extra_data
- это выходной аргумент, который позволяетextractQuery
передавать дополнительные данные в методыconsistent
иcomparePartial
. Чтобы использовать его,extractQuery
должен выделить массив указателей размером*nkeys
и сохранить его адрес в*extra_data
, а затем сохранить в него любые данные. Переменная инициализируется значениемNULL
перед вызовом, поэтому этот аргумент можно просто игнорировать классами операторов, которым не требуются дополнительные данные. Если*extra_data
установлено, весь массив передается в методconsistent
, а соответствующий элемент - в методcomparePartial
.
Класс оператора также должен предоставлять функцию для проверки, соответствует ли индексированный элемент запросу. Она имеет два варианта: логическую функцию consistent
и тернарную функцию triConsistent
. Функция triConsistent
объединяет функциональность обоих вариантов, поэтому достаточно предоставить только ее. Однако, если логический вариант значительно дешевле вычислять, может быть выгодно предоставить оба варианта. Если предоставлен только логический вариант, некоторые оптимизации, которые зависят от опровержения индексированных элементов перед получением всех ключей, отключаются.
bool consistent(bool check[], StrategyNumber n, Datum query, int32 nkeys, Pointer extra_data[], bool *recheck, Datum queryKeys[], bool nullFlags[])
Возвращает true, если индексированный элемент удовлетворяет оператору запроса с номером стратегии
n
(или может удовлетворять его, если возвращается индикация повторной проверки). Эта функция не имеет прямого доступа к значению индексированного элемента, так как GIN не хранит элементы явно. Вместо этого доступна информация о том, какие ключевые значения, извлеченные из запроса, присутствуют в данном индексированном элементе. Массивcheck
имеет длинуnkeys
, которая совпадает с количеством ключей, ранее возвращенных функциейextractQuery
для этого элемента запросаquery
. Каждый элемент массиваcheck
равен true, если индексированный элемент содержит соответствующий ключ запроса, то есть если (check[i] == true), i-й ключ массива результатовextractQuery
присутствует в индексированном элементе. Исходный элемент запроса передается в случае, если методconsistent
должен обратиться к нему, а также массивыqueryKeys[]
иnullFlags[]
, ранее возвращенные функциейextractQuery
.extra_data
- это массив дополнительных данных, возвращенных функциейextractQuery
, илиNULL
, если таковых нет.Когда функция
extractQuery
возвращает пустой ключ вqueryKeys[]
, соответствующий элементcheck[]
становится true, если индексированный элемент содержит пустой ключ; то есть семантикаcheck[]
аналогичнаIS NOT DISTINCT FROM
. Функцияconsistent
может проверить соответствующий элементnullFlags[]
, если ей нужно различать обычное совпадение значений и совпадение с пустым значением.При успешном выполнении, значение
*recheck
должно быть установлено в true, если кортеж кучи должен быть проверен снова с помощью оператора запроса, или в false, если индексный тест является точным. То есть, значение false гарантирует, что кортеж кучи не соответствует запросу; значение true с установленным значением*recheck
в false гарантирует, что кортеж кучи соответствует запросу; и значение true с установленным значением*recheck
в true означает, что кортеж кучи может соответствовать запросу, поэтому его необходимо получить и проверить, выполнив оператор запроса непосредственно против исходного индексированного элемента.GinTernaryValue triConsistent(GinTernaryValue check[], StrategyNumber n, Datum query, int32 nkeys, Pointer extra_data[], Datum queryKeys[], bool nullFlags[])
triConsistent
похожа наconsistent
, но вместо логических значений в вектореcheck
есть три возможных значения для каждого ключа:GIN_TRUE
,GIN_FALSE
иGIN_MAYBE
.GIN_FALSE
иGIN_TRUE
имеют тот же смысл, что и обычные логические значения, в то время какGIN_MAYBE
означает, что наличие этого ключа неизвестно. Когда присутствуют значенияGIN_MAYBE
, функция должна возвращать толькоGIN_TRUE
, если элемент точно соответствует, независимо от того, содержит ли он соответствующие ключи запроса. Аналогично, функция должна возвращатьGIN_FALSE
только если элемент точно не соответствует, независимо от того, содержит ли он ключиGIN_MAYBE
. Если результат зависит от записейGIN_MAYBE
, то есть, соответствие нельзя подтвердить или опровергнуть на основе известных ключей запроса, функция должна возвращатьGIN_MAYBE
.Когда в векторе проверки
check
нет значенийGIN_MAYBE
, возвращаемое значениеGIN_MAYBE
эквивалентно установке флагаrecheck
в функцииconsistent
типа Boolean.
Кроме того, GIN должен иметь способ сортировки значений ключей, хранящихся в индексе. Класс операторов может определить порядок сортировки, указав метод сравнения:
int compare(Datum a, Datum b)
Сравнивает два ключа (неиндексированных элемента!) и возвращает целое число, меньшее нуля, ноль или большее нуля, указывающее, является ли первый ключ меньше, равным или больше второго. Нулевые ключи никогда не передаются этой функции.
В противном случае, если класс оператора не предоставляет метод compare
,
GIN будет искать класс оператора по умолчанию для типа данных ключа индекса и использовать его функцию сравнения. Рекомендуется указывать функцию сравнения в классе оператора GIN, предназначенном только для одного типа данных, так как поиск класса оператора btree требует несколько тактов. Однако полиморфные классы операторов GIN (например, array_ops
) обычно не могут указать одну функцию сравнения.
Класс операторов для GIN может дополнительно предоставлять следующие методы:
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
при обнаружении запроса с частичным совпадением. Подробности см. в разделе Раздел 67.4.2.
Самые фактические типы данных различных значений Datum
, упомянутых выше, зависят от класса оператора. Значения элементов, передаваемые в extractValue
, всегда являются типом ввода класса оператора, и все ключевые значения должны быть типом STORAGE
класса. Тип аргумента query
, передаваемого в extractQuery
, consistent
и triConsistent
, является типом правого входного значения оператора-члена класса, идентифицированного номером стратегии. Это не обязательно должен быть тот же самый тип, что и индексируемый тип, при условии, что ключевые значения правильного типа могут быть извлечены из него. Однако рекомендуется, чтобы SQL-объявления этих трех вспомогательных функций использовали индексируемый тип данных opclass для аргумента query
, даже если фактический тип может быть чем-то другим в зависимости от оператора.