62.1. Основная структура API для индексов#

62.1. Основная структура API для индексов

62.1. Основная структура API для индексов #

Каждый метод доступа к индексу описывается строкой в системном каталоге pg_am. Запись pg_am указывает имя и функцию-обработчик для метода доступа к индексу. Эти записи могут быть созданы и удалены с помощью SQL-команд CREATE ACCESS METHOD и DROP ACCESS METHOD.

Для объявления функции обработчика метода доступа к индексу необходимо указать, что она принимает единственный аргумент типа internal и возвращает псевдотип index_am_handler. Аргумент является фиктивным значением, которое просто предотвращает вызов функций обработчика напрямую из SQL-команд. Результатом функции должна быть структура типа IndexAmRoutine, выделенная с помощью palloc, которая содержит всю информацию, которую ядро кода должно знать, чтобы использовать метод доступа к индексу. Структура IndexAmRoutine, также называемая API-структурой метода доступа, включает поля, определяющие различные фиксированные свойства метода доступа, такие как поддержка многоколоночных индексов. Более важно то, что она содержит указатели на функции поддержки метода доступа, которые выполняют всю реальную работу по доступу к индексам. Эти функции поддержки являются обычными функциями на языке C и не видимы и не вызываемы на уровне SQL. Функции поддержки описаны в разделе Раздел 62.2.

Структура IndexAmRoutine определена следующим образом:

typedef struct IndexAmRoutine
{
    NodeTag     type;

    /*
     * Total number of strategies (operators) by which we can traverse/search
     * this AM.  Zero if AM does not have a fixed set of strategy assignments.
     */
    uint16      amstrategies;
    /* total number of support functions that this AM uses */
    uint16      amsupport;
    /* opclass options support function number or 0 */
    uint16      amoptsprocnum;
    /* does AM support ORDER BY indexed column's value? */
    bool        amcanorder;
    /* does AM support ORDER BY result of an operator on indexed column? */
    bool        amcanorderbyop;
    /* does AM support backward scanning? */
    bool        amcanbackward;
    /* does AM support UNIQUE indexes? */
    bool        amcanunique;
    /* does AM support multi-column indexes? */
    bool        amcanmulticol;
    /* does AM require scans to have a constraint on the first index column? */
    bool        amoptionalkey;
    /* does AM handle ScalarArrayOpExpr quals? */
    bool        amsearcharray;
    /* does AM handle IS NULL/IS NOT NULL quals? */
    bool        amsearchnulls;
    /* can index storage data type differ from column data type? */
    bool        amstorage;
    /* can an index of this type be clustered on? */
    bool        amclusterable;
    /* does AM handle predicate locks? */
    bool        ampredlocks;
    /* does AM support parallel scan? */
    bool        amcanparallel;
    /* does AM support parallel build? */
    bool        amcanbuildparallel;
    /* does AM support columns included with clause INCLUDE? */
    bool        amcaninclude;
    /* does AM use maintenance_work_mem? */
    bool        amusemaintenanceworkmem;
    /* does AM summarize tuples, with at least all tuples in the block
     * summarized in one summary */
    bool        amsummarizing;
    /* OR of parallel vacuum flags */
    uint8       amparallelvacuumoptions;
    /* type of data stored in index, or InvalidOid if variable */
    Oid         amkeytype;

    /* interface functions */
    ambuild_function ambuild;
    ambuildempty_function ambuildempty;
    aminsert_function aminsert;
    aminsertcleanup_function aminsertcleanup;
    ambulkdelete_function ambulkdelete;
    amvacuumcleanup_function amvacuumcleanup;
    amcanreturn_function amcanreturn;   /* can be NULL */
    amcostestimate_function amcostestimate;
    amoptions_function amoptions;
    amproperty_function amproperty;     /* can be NULL */
    ambuildphasename_function ambuildphasename;   /* can be NULL */
    amvalidate_function amvalidate;
    amadjustmembers_function amadjustmembers; /* can be NULL */
    ambeginscan_function ambeginscan;
    amrescan_function amrescan;
    amgettuple_function amgettuple;     /* can be NULL */
    amgetbitmap_function amgetbitmap;   /* can be NULL */
    amendscan_function amendscan;
    ammarkpos_function ammarkpos;       /* can be NULL */
    amrestrpos_function amrestrpos;     /* can be NULL */

    /* interface functions to support parallel index scans */
    amestimateparallelscan_function amestimateparallelscan;    /* can be NULL */
    aminitparallelscan_function aminitparallelscan;    /* can be NULL */
    amparallelrescan_function amparallelrescan;    /* can be NULL */
} IndexAmRoutine;

Для того чтобы быть полезным, метод доступа к индексу также должен иметь одну или несколько семейств операторов и классов операторов, определенных в pg_opfamily, pg_opclass, pg_amop и pg_amproc. Эти записи позволяют планировщику определить, какие виды квалификаций запроса могут быть использованы с индексами этого метода доступа. Семейства и классы операторов описаны в Раздел 35.15, что является предварительным материалом для чтения этой главы.

Индивидуальный индекс определяется записью pg_class, которая описывает его как физическое отношение, а также записью pg_index, которая показывает логическое содержимое индекса - то есть набор столбцов индекса и семантику этих столбцов, как они определены связанными классами операторов. Столбцы индекса (ключевые значения) могут быть либо простыми столбцами базовой таблицы, либо выражениями над строками таблицы. Метод доступа к индексу обычно не интересуется, откуда берутся ключевые значения индекса (ему всегда передаются предварительно вычисленные ключевые значения), но он будет очень заинтересован в информации о классе операторов в pg_index. Оба этих записи каталога могут быть получены как часть структуры данных Relation, которая передается всем операциям с индексом.

Некоторые флаговые поля IndexAmRoutine имеют неочевидные последствия. Требования amcanunique обсуждаются в Раздел 62.5. Флаг amcanmulticol утверждает, что метод доступа поддерживает индексы с несколькими ключевыми столбцами, в то время как amoptionalkey утверждает, что он позволяет сканировать без указания ограничительной клаузулы для первого столбца индекса. Когда amcanmulticol ложен, amoptionalkey по сути говорит о том, поддерживает ли метод доступа полные сканирования индекса без какой-либо ограничительной клаузулы. Методы доступа, которые поддерживают несколько столбцов индекса, должны поддерживать сканирования, которые опускают ограничения на любой или все столбцы после первого; однако им разрешено требовать, чтобы какое-то ограничение появлялось для первого столбца индекса, и это сигнализируется установкой amoptionalkey в ложь. Одна из причин, по которой индекс AM может установить amoptionalkey в ложь, заключается в том, что он не индексирует значения null. Поскольку большинство индексируемых операторов строгие и, следовательно, не могут возвращать true для null-значений, на первый взгляд привлекательно не хранить записи индекса для значений null: они все равно никогда не могут быть возвращены сканированием индекса. Однако этот аргумент не работает, когда сканирование индекса не имеет ограничительной клаузулы для данного столбца индекса. На практике это означает, что индексы, у которых amoptionalkey истинно, должны индексировать null-значения, поскольку планировщик может решить использовать такой индекс вообще без ключей сканирования. Связанное ограничение заключается в том, что метод доступа к индексу, который поддерживает несколько столбцов индекса, должен поддерживать индексирование null-значений в столбцах после первого, потому что планировщик будет предполагать, что индекс может быть использован для запросов, которые не ограничивают эти столбцы. Например, рассмотрим индекс на (a,b) и запрос с WHERE a = 4. Система будет предполагать, что индекс может быть использован для сканирования строк с a = 4, что неверно, если индекс опускает строки, где b равно null. Однако допустимо опускать строки, где первый индексируемый столбец равен null. Метод доступа к индексу, который индексирует null-значения, может также установить amsearchnulls, указывая, что он поддерживает IS NULL и IS NOT NULL клаузулы в качестве условий поиска.

Флаг amcaninclude указывает, поддерживает ли метод доступа включенные столбцы, то есть может ли он хранить (без обработки) дополнительные столбцы помимо ключевого столбца(-ов). Требования предыдущего абзаца применяются только к ключевым столбцам. В частности, комбинация amcanmulticol=false и amcaninclude=true имеет смысл: это означает, что может быть только один ключевой столбец, но также могут быть включенные столбцы. Кроме того, включенным столбцам должно быть разрешено быть пустыми, независимо от amoptionalkey.

Флаг amsummarizing указывает, суммирует ли метод доступа индексированные кортежи, с гранулярностью суммирования как минимум на уровне блока. Методы доступа, которые не указывают на отдельные кортежи, а на диапазоны блоков (как BRIN), могут позволить продолжить оптимизацию HOT. Это не относится к атрибутам, упомянутым в предикатах индекса, обновление такого атрибута всегда отключает HOT.