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, даже если фактический тип может быть чем-то другим в зависимости от оператора.