Функции для индексных методов доступа
Функции построения и поддержки индекса, которые должен обеспечить метод доступа к индексу в IndexAmRoutine
:
IndexBuildResult *
ambuild (Relation heapRelation,
Relation indexRelation,
IndexInfo *indexInfo);
Строит новый индекс. Индексное отношение было физически создано, но оно пусто е. Оно должно быть заполнено фиксированными данными, которые требует метод доступа, плюс записи для всех кортежей, уже существующих в таблице. Обычно функция ambuild
вызывает table_index_build_scan()
для сканирования таблицы на предмет существующих кортежей и вычисления ключей, которые нужно вставить в индекс. Эта функция должна возвращать структуру, выделенную вызовом palloc и содержащую статистику нового индекса.
void
ambuildempty (Relation indexRelation);
Создает пустой индекс и записывает его в инициали зационную вилку (INIT_FORKNUM
) данного отношения. Этот метод вызывается только для незалогиненных индексов; пустой индекс, записанный в инициализационную вилку, будет копироваться в основную вилку отношения при каждом перезапуске сервера.
bool
aminsert (Relation indexRelation,
Datum *values,
bool *isnull,
ItemPointer heap_tid,
Relation heapRelation,
IndexUniqueCheck checkUnique,
bool indexUnchanged,
IndexInfo *indexInfo);
Вставляет новый кортеж в существующий индекс. Массивы values
и isnull
задают значения ключей, которые будут индексироваться, а heap_tid
- TID, который будет индексироваться. Если метод доступа поддерживает уникальные индексы (его флаг amcanunique
равен true
), то checkUnique
указывает тип проверки уникальности. Это зависит от того, является ли ограничение уникальности откладываемым; подробности см. в разделе «Проверки уникальности индексов». Обычно метод доступа нуждается в параметре heapRelation
только при выполнении проверки уникальности (поскольку в этом случае ему придется заглянуть в кучу, чтобы проверить актуальность кортежей).
Булево значение indexUnchanged
дает подсказку о характере индексируемого кортежа. Если значение равно true, то кортеж является дубликатом какого-то существующего кортежа в ин дексе. Новый кортеж - это логически неизмененная версия кортежа MVCC, являющаяся его преемником. Это происходит, когда происходит UPDATE
, который не изменяет ни одного столбца, покрываемого индексом, но, тем не менее, требует новой версии в индексе. Индексный МД может использовать эту подсказку для принятия решения о применении удаления индекса снизу вверх в тех частях индекса, где накапливается много версий одной и той же логической строки. Обратите внимание, что обновление неключевого столбца или столбца, который появляется только в частичном индексном предикате, не влияет на значение indexUnchanged
. Основной код определяет значение indexUnchanged
для каждого кортежа, используя подход с низкими накладными расходами, который допускает как ложноположительные, так и ложноотрицательные результаты. Индексные МД не должны рассматривать indexUnchanged
как авторитетный источник информации о видимости кортежа или версионировании.
Булево значение результата функции имеет значение только в том случае, если checkUnique
имеет значение UNIQUE_CHECK_PARTIAL
. В этом случае истинный результат означает, ч то новая запись заведомо уникальна, а ложный - что она может быть неуникальной (и должна быть назначена отложенная проверка уникальности). Для других случаев рекомендуется постоянный ложный результат.
Некоторые индексы могут индексировать не все кортежи. Если кортеж не должен быть проиндексирован, aminsert
должен просто вернуться, ничего не делая.
Если индексный МД хочет кэшировать данные при последовательных вставках индекса в SQL-операторе, он может выделить место в indexInfo->ii_Context
и хранить указатель на данные в indexInfo->ii_AmCache
(который изначально будет NULL).
IndexBulkDeleteResult *
ambulkdelete (IndexVacuumInfo *info,
IndexBulkDeleteResult *stats,
IndexBulkDeleteCallback callback,
void *callback_state);
Удаление кортежа(ов) из индекса. Это операция «массового удаления», которая должна быть реализована путем сканирования всего индекса и проверки каждой записи на предмет того, следует ли ее удалять. Переданная функция обратного вызова должна быть вызвана в стиле callback(TID, callback_state) returns bool
, чтобы определить, должна ли быть удалена какая-либо конкретная запись индекса, идентифицированная ссылающимся на нее TID. Возвращать эта функция должна NULL или структуру, выделенную вызовом palloc и содержащую статистику результата удаления. Можно вернуть NULL, если никакая информация не должна быть передана amvacuumcleanup
.
Из-за ограниченного объема памяти maintenance_work_mem
вызов ambulkdelete
может потребоваться несколько раз, если требуется удалить много кортежей. Аргумент stats
- это результат предыдущего вызова для данного индекса (для первого вызова в рамках операции VACUUM
он NULL). Это позволяет МД накапливать статистику по всей операции. Обычно ambulkdelete
изменяет и возвращает одну и ту же структуру, если переданная статистика не равна NULL.
IndexBulkDeleteResult *
amvacuumcleanup (IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
Очистка после операции VACUUM
(ноль или более вызовов ambulkdelete
). Она не должна де лать ничего, кроме возврата статистики индекса, но может выполнять массовую очистку, например, освобождать пустые страницы индекса. stats
- это то, что вернул последний вызов ambulkdelete
, или NULL, если ambulkdelete
не вызывался, потому что не было необходимости удалять кортежи. Эта функция должна возвращать NULL или структуру, выделенную вызовом palloc. Содержащаяся в ней статистика будет использована для обновления pg_class
и будет сообщена VACUUM
, если задан VERBOSE
. Можно вернуть NULL, если индекс не был изменен во время операции VACUUM
, но в противном случае должна быть возвращена корректная статистика.
amvacuumcleanup
также будет вызван при завершении операции ANALYZE
. В этом случае stats всегда будет NULL и любое возвращаемое значение будет проигнорировано. Этот случай можно отличить, проверив info->analyze_only
. Рекомендуется, чтобы при таком вызове метод доступа не делал ничего, кроме очистки после вставки, и то только в рабочем процессе autovacuum
.
bool
amcanreturn (Relation indexRelation, int attno);
Проверяет, поддерживает ли индекс только индексное сканирование по заданному столбцу, возвращая исходное индексированное значение столбца. Номер атрибута основан на 1, т.е. attno
первого столбца равен 1. Возвращает true
, если поддерживается, иначе false
. Эта функция всегда должна возвращать true
для включенных столбцов (если они поддерживаются), так как нет смысла во включенном столбце, который нельзя извлечь. Если метод доступа вообще не поддерживает сканирование только по индексу, поле amcanreturn
в его структуре IndexAmRoutine
может быть установлено в NULL.
void
amcostestimate (PlannerInfo *root,
IndexPath *path,
double loop_count,
Cost *indexStartupCost,
Cost *indexTotalCost,
Selectivity *indexSelectivity,
double *indexCorrelation,
double *indexPages);
Оценка затрат на сканирование индекса. Эта функция подробно описана в разделе «Функции оценки стоимости индекса».
bytea *
amoptions (ArrayType *reloptions,
bool validate);
Разбирает и проверяет массив reloptions
для индекса. Вызывается только в том случае, если для индекса существует ненулевой массив reloptions
. reloptions
- это текстовый массив, содержащий записи вида имя=значение. Функци я должна получить значение bytea
, которое будет скопировано в поле rd_options записи relcache
индекса. Содержание данных в значении bytea
может определять метод доступа; большинство стандартных методов доступа используют структуру StdRdOptions
. Когда validate
равен true
, функция должна выдать соответствующее сообщение об ошибке, если какие-либо опции не распознаны или имеют неверные значения; когда validate
равен false
, недействительные записи должны быть молча проигнорированы. (Значение validate
равно false
при загрузке опций, уже хранящихся в pg_catalog
; недействительная запись может быть найдена только в том случае, если метод доступа изменил свои правила для опций, и в этом случае игнорирование устаревших записей будет уместным). Можно вернуть NULL, если требуется поведение по умолчанию.
bool
amproperty (Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
Метод amproperty
позволяет методам доступа к индексам переопределять поведение по умолчанию pg_index_column_has_property
и связанных с ним функций. Если метод доступа не имеет специального поведения для запросов свойств индекса, поле amproperty
в его структуре IndexAmRoutine
может быть установлено в NULL. В противном случае метод amproperty
будет вызван с index_oid
и attno
, равными нулю для вызовов pg_indexam_has_property
, или с валидным index_oid
и attno
, равными нулю для вызовов pg_index_has_property
, или с валидным index_oid
и attno
больше нуля для вызовов pg_index_column_has_property
. prop
- значение перечисления, идентифицирующее проверяемое свойство, а propname
- исходная строка имени свойства. Если код ядра не распознает имя свойства, то prop
будет AMPROP_UNKNOWN
. Методы доступа могут определять пользовательские имена свойств, проверяя propname
на совпадение (для согласованности с основным кодом используйте pg_strcasecmp
); для имен, известных основному коду, лучше проверять prop
. Если метод amproperty
возвращает true
, значит, он определил результат проверки свойства: он должен установить *res
в булево значение, которое нужно вернуть, или установить *isnull
в true
, чтобы вернуть NULL. Если метод amproperty
возвращает false
, то код ядра продолжит работу с обычной логикой определения результата проверки свойства.
Методы доступа, поддерживающие операторы упорядочивания, должны реализовать тестирование свойства AMPROP_DISTANCE_ORDERABLE
, поскольку основной код не знает, как это сделать, и вернет NULL. Также может быть выгодно реализовать тестирование AMPROP_RETURNABLE
, если это можно сделать дешевле, чем открывать индекс и вызывать amcanreturn
, что является поведением ядра по умолчанию. Поведение по умолчанию должно быть удовлетворительным для всех остальных стандартных свойств.
char *
ambuildphasename (int64 phasenum);
Возвращает текстовое имя заданного номера фазы сборки. Номера фаз сообщаются во время построения индекса через интерфейс pgstat_progress_update_param
. Имена фаз затем отображаются в представлении pg_stat_progress_create_index
.
bool
amvalidate (Oid opclassoid);
Проверяет записи в каталоге для указанного класса оператора, насколько метод доступа может это сделать. Например, это может включать проверку того, что все необходимые функции поддержки предоставлены. Функция amvalidate
должна возвращать false
, если класс оператора недействителен. О проблемах следует сообщать в сообщениях ereport
, обычно на уровне INFO
.
void
amadjustmembers (Oid opfamilyoid,
Oid opclassoid,
List *operators,
List *functions);
Проверяет предлагаемые новые члены семейства операторов и функций, насколько это может сделать метод доступа, и устанавливает их типы зависимостей, если значение по умолчанию неудовлетворительно. Вызывается во время CREATE OPERATOR CLASS
и во время ALTER OPERATOR FAMILY ADD
; в последнем случае opclassoid
равен InvalidOid
. Аргументы List
представляют собой списки структур OpFamilyMember
, как определено в amapi.h
. Тесты, выполняемые этой функцией, обычно являются подмножеством тех, которые формируются функцией amvalidate
, поскольку amadjustmembers
не может предполагать, что видит полный набор членов. Например, было бы разумно проверить сигнатуру вспомогательной функции, но не проверять, все ли необходимые вспомогательные функции предоставлены. О любых проблемах можно сообщить, выбросив ошибку. Связанные с зависимостями поля структур OpFamilyMember
инициализируются кодом ядра для создания жестких зависимостей от opclass
, если это CREATE OPERATOR CLASS
, или мягких зависимостей от opfamily
, если это ALTER OPERATOR FAMILY ADD
. amadjustmembers
может настроить эти поля, если более подходящим является другое поведение. Например, GIN
, GiST
и SPGiST
всегда устанавливают для членов оператора мягкую зависимость от opfamily
, поскольку связь между оператором и opclass
относительно слабая в этих индексных типах; поэтому разумно позволить членам оператора свободно добавляться и удаляться. Необязательные вспомогательные функции обычно также имеют мягкие зависимости, чтобы их можно было удалить при необходимости.
Цель индекса, конечно, состоит в том, чтобы поддерживать сканирование кортежей, соответствующих индексируемому условию WHERE
, часто называемому классификатором или ключом сканирования. Семантика сканирования индекса более подробно описана в разделе «Сканирование индексов». Метод доступа к индексу может поддерживать «обычное» сканирование индекса, «растровое» сканирование индекса или и то, и другое. Функции, связанные со сканированием, которые должен или может предоставлять метод доступа к индексу, следующие:
IndexScanDesc
ambeginscan (Relation indexRelation,
int nkeys,
int norderbys);
Подготавливает к сканированию индекса. Параметры nkeys
и norderbys
указывают количество операторов условия и сортировки, которые будут использоваться при сканировании; они могут быть полезны для целей распределения пространства. Обратите внимание, что фактические значения ключей сканирования пока не указываются. В результате функция должна выдать структуру, выделенную средствами palloc. По причинам имплементации метод доступа к индексу должен создать эту структуру, вызвав RelationGetIndexScan()
. В большинстве случаев ambeginscan
делает немногое, кроме выполнения этого вызова и, возможно, получения блокировок; интересные части запуска индексного сканирования находятся в amrescan
.
void
amrescan (IndexScanDesc scan,
ScanKey keys,
int nkeys,
ScanKey orderbys,
int norderbys);
Запускает или перезапускает сканирование индекса, возможно, с новыми ключами сканирования. (Чтобы перезапустить сканирование с использованием ранее переданных ключей, передается NULL для ключей и/или orderbys
). Обратите внимание, что количество ключей или операторов orderby
не может быть больше, чем было передано в ambeginscan
. На практике функция перезапуска используется, когда новый внешний кортеж выбирается вложенным циклическим соединением и поэтому требуется новое значение сравнения ключей, но структура ключей сканирования остается прежней.
bool
amgettuple (IndexScanDesc scan,
ScanDirection direction);
Получает следующий кортеж в заданном сканировании, двигаясь в заданном направлении (вперед или назад по индексу). Возвращает true
, если кортеж был получен, false
, если не осталось ни одного подходящего кортежа. В истинном случае TID кортежа сохраняется в стр уктуре сканирования. Обратите внимание, что «успех» означает только то, что индекс содержит запись, соответствующую ключам сканирования, а не то, что кортеж обязательно существует в куче или пройдет тест на снимок вызывающей стороны. В случае успеха, amgettuple
также должен установить scan->xs_recheck
в true
или false
. False
означает, что есть уверенность в том, что запись в индексе соответствует ключам сканирования. True
означает, что уверенности в этом нет, и условия, представленные ключами сканирования, должны быть перепроверены по кортежу кучи после его извлечения. Это положение поддерживает операторы индексов с «потерями». Обратите внимание, что перепроверка распространяется только на условия сканирования; предикат частичного индекса (если таковой имеется) никогда не перепроверяется вызывающими amgettuple
.
Если индекс поддерживает сканирование только по индексу (т.е. amcanreturn
возвращает true
для любого из его столбцов), то при успехе метод доступа должен также проверить scan->xs_want_itup
, и если это true
, то вернуть первоначально проиндексированные данные для записи индекса. Столбцы, для которых amcanreturn
возвращает false
, могут быть возвращены как нули. Данные могут быть возвращены в виде указателя IndexTuple
, хранящегося по адресу scan->xs_itup
, с дескриптором кортежа scan->xs_itupdesc
; или в виде указателя HeapTuple
, хранящегося по адресу scan->xs_hitup
, с дескриптором кортежа scan->xs_hitupdesc
. (Последний формат следует использовать при восстановлении данных, которые могут не поместиться в IndexTuple
). В любом случае, управление данными, на которые ссылается указатель, является обязанностью метода доступа. Данные должны оставаться пригодными, по крайней мере, до следующего вызова amgettuple
, amrescan
или amendscan
для сканирования.
Функция amgettuple
должна быть предоставлена только в том случае, если метод доступа поддерживает «обычное» сканирование индексов. В противном случае поле amgettuple
в структуре IndexAmRoutine
должно быть установлено в NULL.
int64
amgetbitmap (IndexScanDesc scan,
TIDBitmap *tbm);
Получает все кортежи в заданном сканировании и добавляет их в предоставленную вызывающей стороной битовую карту TIDBitmap
(т.е. ИЛИ набор идентификаторов кортежей в любой набор, уже имеющийся в битовой карте). Возвращается количество извлеченных кортежей (это может быть только приблизительный подсчет, например, некоторые методы доступа не обнаруживают дубликаты). Вставляя идентификаторы кортежей в битовую карту, amgetbitmap
может указать, что для определенных идентификаторов кортежей требуется перепроверка условий сканирования. Это аналогично выхо дному параметру xs_recheck
функции amgettuple
. Примечание: в текущей реализации поддержка этой возможности объединена с поддержкой хранения битовой карты с потерями, поэтому вызывающие команды перепроверяют как условия сканирования, так и предикат частичного индекса (если он есть) для кортежей с возможностью перепроверки. Однако это не всегда верно. amgetbitmap
и amgettuple
не могут быть использованы в одном и том же сканировании индекса; существуют и другие ограничения при использовании amgetbitmap
, как объясняется в разделе «Сканирование индексов».
Функция amgetbitmap
должна быть предоставлена только в том случае, если метод доступа поддерживает сканирование индексов «по растровой карте». Если нет, то поле amgetbitmap
в структуре IndexAmRoutine
должно быть установлено в NULL.
void
amendscan (IndexScanDesc scan);
Завершает сканирования и освобождение ресурсов. Сама структура scan
не должна освобождаться, но любые блокировки или пины, взятые внутри метода доступа, должны быть освобождены, так же как и любая другая память, выделенная ambeginscan
и другими функциями, связанными со сканированием.
void
ammarkpos (IndexScanDesc scan);
Помечает текущую позицию сканирования. Метод доступа должен поддерживать только одну запомненную позицию сканирования за одно сканирование.
Функция ammarkpos
должна быть предоставлена только в том случае, если метод доступа поддерживает упорядоченное сканирование. В противном случае поле ammarkpos
в структуре IndexAmRoutine
может быть установлено в NULL.
void
amrestrpos (IndexScanDesc scan);
Восстанавливает сканирование на последнюю отмеченную позицию.
Функция amrestrpos
должна быть реализована только в том случае, если метод доступа поддерживает упорядоченное сканирование. В противном случае поле amrestrpos
в структуре IndexAmRoutine
может быть установлено в NULL.
Помимо поддержки обычного сканирования индекса, некоторые типы индексов могут поддерживать параллельное сканирование индекса, которое позволяет нескольким бэкендам сотрудничать при выполнении сканирования индекса. Метод доступа к индексу должен быть устроен так, чтобы каждый взаимодействующий процесс возвращал подмножество кортежей, которое было бы выполнено при обычном, непараллельном сканировании индекса, но таким образом, чтобы объединение этих подмножеств было равно набору кортежей, которые были бы возвращены при обычном, непараллельном сканировании индекса. Кроме того, хотя глобальное упорядочивание кортежей, возвращаемых при параллельном сканировании, не обязательно, упорядочивание подмножества кортежей, возвращаемых в каждом взаимодействующем бэкенде, должно соответствовать запрошенному упорядочиванию. Для поддержки параллельного сканирования индексов могут быть реализованы следующие функции:
Size
amestimateparallelscan (void);
Оценивает и возвращает количество байтов динамической общей памяти, которые потребуются методу доступа для выполнения параллельного сканирования. (Этот объем дополняет, а не заменяет объем памяти, затребованный для данных, независимо от МД, в ParallelIndexScanDescData
).
Нет необходимости реализовывать эту функцию для методов доступа, которые не поддерживают параллельное сканирование или для которых количество требуемых дополнительных байт памяти равно нулю.
void
aminitparallelscan (void *target);
Эта функция будет вызываться для инициализации динамической общей памяти в начале параллельного сканирования. target
будет указывать по крайней мере на количество байт, ранее возвращенное функцией amestimateparallelscan
, и эта функция может использовать это пространство для хранения любых данных по своему усмотрению.
Нет необходимости реализовывать эту функцию для методов доступа, не поддерживающих параллельное сканирование, или в случаях, когда требуемое прост ранство общей памяти не нуждается в инициализации.
void
amparallelrescan (IndexScanDesc scan);
Эта функция, если она реализована, будет вызываться, когда необходимо перезапустить параллельное индексное сканирование. Она должна сбросить все общие состояния, установленные aminitparallelscan
, чтобы сканирование было перезапущено с самого начала.