60.3. Расширяемость
Интерфейс GIN характеризуется высоким уровнем абстракции и таким образом требует от разработчика метода доступа реализовать только смысловое наполнение обрабатываемого типа данных. Уровень GIN берёт на себя заботу о параллельном доступе, поддержке журнала и поиске в структуре дерева.
Всё, что нужно, чтобы получить работающий метод доступа GIN — это реализовать несколько пользовательских методов, определяющих поведение ключей в дереве и отношения между ключами, индексируемыми объектами и поддерживаемыми запросами. Словом, GIN сочетает расширяемость с универсальностью, повторным использованием кода и аккуратным интерфейсом.
Класс операторов должен предоставить GIN следующие три метода:
- int compare(Datum a, Datum b)
- Сравнивает два ключа (не индексированные объекты!) и возвращает целое меньше нуля, ноль или целое больше нуля, показывающее, что первый ключ меньше, равен или больше второго. Ключи NULL никогда не передаются этой функции. 
- Datum *extractValue(Datum itemValue, int32 *nkeys, bool **nullFlags)
- Возвращает массив ключей (выделенный через palloc) для индексируемого объекта. Число возвращаемых ключей должно записываться в - *nkeys. Если какой-либо из ключей может быть NULL, нужно так же выделить через palloc массив из- *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.14.2). Часто функция- extractQueryдолжна проанализировать- n, чтобы определить тип данных аргумента- queryи выбрать метод для извлечения значений ключей. Число возвращаемых ключей должно быть записано в- *nkeys. Если какие-либо ключи могут быть NULL, нужно так же выделить через palloc массив из- *nkeysполей- bool, сохранить его адрес в- *nullFlags, и установить эти флаги NULL как требуется. В- *nullFlagsможно оставить значение- NULL(это начальное значение), если все ключи отличны от NULL. Эта функция может возвратить- NULL, если- queryне содержит ключей.- Выходной аргумент - searchModeпозволяет функции- extractQueryвыбрать режим, в котором должен выполняться поиск. Если- *searchModeимеет значение- GIN_SEARCH_MODE_DEFAULT(это значение устанавливается перед вызовом), подходящими кандидатами считаются только те объекты, которые соответствуют минимум одному из возвращённых ключей. Если в- *searchModeустановлено значение- GIN_SEARCH_MODE_INCLUDE_EMPTY, то в дополнение к объектам с минимум одним совпадением ключа, подходящими кандидатами будут считаться и объекты, вообще не содержащие ключей. (Этот режим полезен для реализации, например, операторов A-является-подмножеством-B.) Если в- *searchModeустановлено значение- GIN_SEARCH_MODE_ALL, подходящими кандидатами считаются все отличные от NULL объекты в индексе, независимо от того, встречаются ли в них возвращаемые ключи. (Этот режим намного медленнее двух других, так как он по сути требует сканирования всего индекса, но он может быть необходим для корректной обработки крайних случаев. Оператор, который выбирает этот режим в большинстве ситуаций, скорее всего не подходит для реализации в классе операторов 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передаётся соответствующий его элемент.
 Класс операторов должен также предоставить функцию для проверки, соответствует ли индексированный объект запросу. Поддерживаются две её вариации: логическая 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присутствует в индексированном объекте. Исходное значение- queryпередаётся на случай, если оно потребуется методу- consistent; с той же целью ему передаются массивы- queryKeys[]и- nullFlags[], ранее возвращённые функцией- extractQuery. В аргументе- extra_dataпередаётся массив дополнительных данных, возвращённый функцией- extractQuery, или- NULL, если дополнительных данных нет.- Когда - extractQueryвозвращает ключ NULL в- queryKeys[], соответствующий элемент- check[]содержит TRUE, если индексированный объект содержит ключ NULL; то есть можно считать, что элементы- check[]отражают условие- IS NOT DISTINCT FROM. Функция- consistentможет проверить соответствующий элемент- nullFlags[], если ей нужно различать соответствие с обычным значением и соответствие с NULL.- В случае успеха в - *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.
Дополнительно класс операторов может предоставить GIN следующий метод:
- int comparePartial(Datum partial_key, Datum key, StrategyNumber n, Pointer extra_data)
- Сравнивает ключ запроса с частичным соответствием с ключом индекса. Возвращает целое число, знак которого отражает результат сравнения: отрицательное число означает, что ключ индекса не соответствует запросу, но нужно продолжать сканирование индекса; ноль означает, что ключ индекса соответствует запросу; положительное число означает, что сканирование индекса нужно прекратить, так как других соответствий не будет. Функции передаётся номер стратегии - nоператора, сформировавшего запрос частичного соответствия, на случай, если нужно знать его смысл, чтобы определить, когда прекращать сканирование. Кроме того, ей передаётся- extra_data— соответствующий элемент массива дополнительных данных, сформированного функцией- extractQuery, либо- NULL, если дополнительных данных нет. Значения NULL этой функции никогда не передаются.
Для поддержки проверок на «частичное соответствие» класс операторов должен предоставить метод comparePartial, а метод extractQuery должен устанавливать параметр pmatch, когда встречается запрос на частичное соответствие. За подробностями обратитесь к Подразделу 60.4.2.
Фактические типы данных различных значений Datum, упоминаемых выше, зависят от класса операторов. Значения объектов, передаваемые в extractValue, всегда имеют входной тип класса операторов, а все значения ключей должны быть типа, заданного параметром STORAGE для класса. Типом аргумента query, передаваемого функциям extractQuery, consistent и triConsistent, будет тот тип, что указан в качестве типа правого операнда оператора-члена класса, определяемого по номеру стратегии. Это не обязательно должен быть индексируемый тип, достаточно лишь, чтобы из него можно было извлечь значения ключей, имеющие нужный тип. Однако рекомендуется, чтобы в SQL-объявлениях этих трёх опорных функций для аргумента query назначался индексируемый тип класса операторов, даже несмотря на то, что фактический тип может быть другим, в зависимости от оператора.