62.2. Функции метода доступа к индексам#

62.2. Функции метода доступа к индексам

62.2. Функции метода доступа к индексам

Все функции построения и поддержки индекса, которые должен предоставить метод доступа к индексу в 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 указывает тип проверки уникальности, которую нужно выполнить. Это может варьироваться в зависимости от того, является ли уникальное ограничение отложенным; см. Раздел 62.5 для получения подробной информации. Обычно метод доступа требуется только параметр heapRelation при выполнении проверки уникальности (поскольку в этом случае ему придется просматривать кучу, чтобы проверить жизнеспособность кортежа).

Значение Boolean indexUnchanged дает подсказку о природе кортежа, который нужно индексировать. Когда оно истинно, кортеж является дубликатом некоторого существующего кортежа в индексе. Новый кортеж является логически неизмененной версией кортежа MVCC-преемника. Это происходит, когда выполняется UPDATE, который не изменяет ни одного столбца, охваченного индексом, но, тем не менее, требует новой версии в индексе. Индекс AM может использовать эту подсказку, чтобы решить применить удаление индекса снизу вверх в частях индекса, где накапливается много версий одной и той же логической строки. Обратите внимание, что обновление неключевого столбца или столбца, который появляется только в предикате частичного индекса, не влияет на значение indexUnchanged. Основной код определяет значение indexUnchanged для каждого кортежа, используя метод с низкими накладными расходами, который допускает как ложные срабатывания, так и ложные отрицания. Индексы AM не должны рассматривать indexUnchanged как авторитетный источник информации о видимости или версионности кортежа.

Логическое значение результата функции имеет значение только тогда, когда checkUnique равно UNIQUE_CHECK_PARTIAL. В этом случае истинный результат означает, что новая запись известно уникальна, тогда как ложный результат означает, что она может быть неуникальной (и должна быть запланирована отложенная проверка уникальности). Для других случаев рекомендуется постоянный ложный результат.

Возможно, некоторые индексы не индексируют все кортежи. Если кортеж не должен быть проиндексирован, функция aminsert должна просто вернуться без выполнения каких-либо действий.

Если AM индекса хочет кешировать данные между последовательными вставками индекса в рамках одного SQL-запроса, он может выделить пространство в indexInfo->ii_Context и сохранить указатель на данные в indexInfo->ii_AmCache (который изначально будет равен NULL).

IndexBulkDeleteResult *
ambulkdelete (IndexVacuumInfo *info,
              IndexBulkDeleteResult *stats,
              IndexBulkDeleteCallback callback,
              void *callback_state);

Удалить кортеж(и) из индекса. Это операция массового удаления, которая предполагает просмотр всего индекса и проверку каждой записи, чтобы определить, должна ли она быть удалена. Переданная функция callback должна быть вызвана в стиле callback(TID, callback_state) returns bool, чтобы определить, должна ли быть удалена конкретная запись индекса, идентифицируемая своим TID. Должна возвращать либо NULL, либо структуру, выделенную с помощью palloc, содержащую статистику о влиянии операции удаления. Если необходимо передать информацию в amvacuumcleanup, можно вернуть NULL.

Из-за ограниченного значения maintenance_work_mem, функцию ambulkdelete может потребоваться вызывать несколько раз, когда требуется удалить много кортежей. Аргумент stats является результатом предыдущего вызова для этого индекса (для первого вызова внутри операции VACUUM он равен NULL). Это позволяет AM накапливать статистику на протяжении всей операции. Обычно функция ambulkdelete изменяет и возвращает ту же структуру, если переданный аргумент stats не является NULL.

IndexBulkDeleteResult *
amvacuumcleanup (IndexVacuumInfo *info,
                 IndexBulkDeleteResult *stats);

Очистка после операции VACUUM (ноль или более вызовов ambulkdelete). Это не обязано выполнять никаких действий, кроме возврата статистики индекса, но оно может выполнять массовую очистку, такую как освобождение пустых страниц индекса. stats - это то, что вернул последний вызов ambulkdelete, или NULL, если ambulkdelete не вызывался, потому что не было необходимости удалять кортежи. Если результат не является NULL, он должен быть структурой, выделенной с помощью palloc. Содержащаяся в нем статистика будет использоваться для обновления pg_class и будет отображаться при вызове VACUUM с флагом VERBOSE. Если индекс не изменился во время операции VACUUM, можно вернуть NULL, но в противном случае должна быть возвращена правильная статистика.

amvacuumcleanup также будет вызвана по завершении операции ANALYZE. В этом случае stats всегда равно NULL, и любое возвращаемое значение будет проигнорировано. Этот случай можно отличить, проверив info->analyze_only. Рекомендуется, чтобы метод доступа в таком вызове ничего не делал, кроме очистки после вставки, и только в рабочем процессе автоочистки.

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);

Оцените затраты на сканирование индекса. Эта функция полностью описана в разделе Раздел 62.6, ниже.

bytea *
amoptions (ArrayType *reloptions,
           bool validate);

Разбор и проверка массива reloptions для индекса. Это вызывается только когда существует ненулевой массив reloptions для индекса. Параметр reloptions - это массив типа text, содержащий записи вида name=value. Функция должна построить значение типа 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. (Обе переменные, на которые ссылается, инициализируются значением false перед вызовом). Если метод 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 и SP-GiST всегда устанавливают для операторов мягкие зависимости от opfamily, поскольку связь между оператором и opclass относительно слаба в этих типах индексов; поэтому разумно разрешить добавление и удаление операторов. Опциональные поддерживающие функции обычно также имеют мягкие зависимости, чтобы их можно было удалить при необходимости.

Целью индекса, конечно, является поддержка сканирования для кортежей, соответствующих индексируемому условию WHERE, часто называемому квалификатором или ключом сканирования. Семантика сканирования индекса подробно описана в разделе Раздел 62.3, ниже. Метод доступа к индексу может поддерживать обычные сканирования индекса, битовые сканирования индекса или оба вида. Функции, связанные со сканированием, которые метод доступа к индексу должен или может предоставлять, включают:

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);

Начать или перезапустить сканирование индекса, возможно, с новыми ключами сканирования. (Для перезапуска с использованием ранее переданных ключей, для keys и/или orderbys передается NULL). Обратите внимание, что количество ключей или операторов сортировки не может быть больше, чем то, что было передано в ambeginscan. На практике функция перезапуска используется, когда новый внешний кортеж выбирается с помощью соединения вложенным циклом, и поэтому требуется новое значение сравнения ключа, но структура ключа сканирования остается неизменной.

bool
amgettuple (IndexScanDesc scan,
            ScanDirection direction);

Получить следующий кортеж в данном сканировании, перемещаясь в заданном направлении (вперед или назад в индексе). Возвращает true, если был получен кортеж, false, если не осталось соответствующих кортежей. В случае true, кортеж TID сохраняется в структуре scan. Обратите внимание, что успех означает только то, что индекс содержит запись, соответствующую ключам сканирования, а не то, что кортеж все еще существует в куче или пройдет проверку снимка вызывающей стороны. В случае успеха amgettuple также должна установить scan->xs_recheck в true или false. False означает, что уверенность в том, что запись в индексе соответствует ключам сканирования. True означает, что это неизвестно, и условия, представленные ключами сканирования, должны быть проверены снова по кортежу в куче после его получения. Это обеспечивает поддержку неполных операторов индекса. Обратите внимание, что проверка будет распространяться только на условия сканирования; частичный предикат индекса (если есть) никогда не проверяется вызывающими amgettuple.

Если индекс поддерживает только сканирование индекса (т.е. amcanreturn возвращает true для любого из его столбцов), то при успешном выполнении AM также должен проверить scan->xs_want_itup, и если это верно, он должен вернуть исходные данные для записи индекса. Столбцы, для которых amcanreturn возвращает false, могут быть возвращены как null. Данные могут быть возвращены в виде указателя на IndexTuple, хранящегося в scan->xs_itup, с дескриптором кортежа scan->xs_itupdesc; или в виде указателя на HeapTuple, хранящегося в scan->xs_hitup, с дескриптором кортежа scan->xs_hitupdesc. (Последний формат следует использовать при восстановлении данных, которые возможно не помещаются в IndexTuple). В любом случае управление данными, на которые ссылается указатель, является ответственностью метода доступа. Данные должны оставаться валидными по крайней мере до следующего вызова amgettuple, amrescan или amendscan для данного сканирования.

Функция amgettuple должна быть предоставлена только в том случае, если метод доступа поддерживает сканирование индекса plain. Если это не так, поле amgettuple в структуре IndexAmRoutine должно быть установлено в NULL.

int64
amgetbitmap (IndexScanDesc scan,
             TIDBitmap *tbm);

Извлекает все кортежи из данного сканирования и добавляет их в предоставленный вызывающей стороне TIDBitmap (то есть объединяет множество идентификаторов кортежей с уже существующим множеством в битовой карте). Возвращается количество извлеченных кортежей (это может быть только приблизительное количество, например, некоторые AM не обнаруживают дубликаты). При вставке идентификаторов кортежей в битовую карту amgetbitmap может указать, что требуется повторная проверка условий сканирования для конкретных идентификаторов кортежей. Это аналогично выходному параметру xs_recheck функции amgettuple. Примечание: в текущей реализации поддержка этой функции смешана с поддержкой потерь при хранении самой битовой карты, и поэтому вызывающие стороны повторно проверяют как условия сканирования, так и частичный предикат индекса (если есть) для повторно проверяемых кортежей. Однако это может быть не всегда верно. amgetbitmap и amgettuple не могут использоваться в одном и том же сканировании индекса; также есть и другие ограничения при использовании amgetbitmap, как объясняется в разделе Раздел 62.3.

Функция amgetbitmap должна быть предоставлена только в том случае, если метод доступа поддерживает сканирование индекса bitmap. Если это не так, поле 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);

Оцените и верните количество байтов динамической общей памяти, которое будет необходимо для выполнения параллельного сканирования доступным методом. (Это число добавляется к, а не заменяет, объем памяти, необходимый для AM-независимых данных в ParallelIndexScanDescData).

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

void
aminitparallelscan (void *target);

Эта функция будет вызвана для инициализации динамической общей памяти в начале параллельного сканирования. target будет указывать, по крайней мере, на количество байтов, ранее возвращенных amestimateparallelscan, и эта функция может использовать это количество памяти для хранения любых данных, которые ей необходимы.

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

void
amparallelrescan (IndexScanDesc scan);

Эта функция, если реализована, будет вызываться, когда необходимо перезапустить параллельное сканирование индекса. Она должна сбросить любое общее состояние, установленное с помощью функции aminitparallelscan, чтобы сканирование началось сначала.