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

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

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

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

Для объявления функции обработчика метода доступа к индексу необходимо указать, что она принимает единственный аргумент типа internal и возвращает псевдотип index_am_handler. Аргумент является фиктивным значением, которое просто предотвращает вызов функций обработчика напрямую из SQL-команд. Результатом функции должна быть структура типа IndexAmRoutine, выделенная с помощью palloc, которая содержит всю информацию, которую ядро кода должно знать, чтобы использовать метод доступа к индексу. Структура IndexAmRoutine, также называемая API-структурой метода доступа, включает поля, определяющие различные фиксированные свойства метода доступа, такие как поддержка многоколоночных индексов. Более важно то, что она содержит указатели на функции поддержки метода доступа, которые выполняют всю реальную работу по доступу к индексам. Эти функции поддержки являются обычными функциями на языке C и не видимы и не вызываемы на уровне SQL. Функции поддержки описаны в разделе Раздел 61.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 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;
    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 обсуждаются в разделе Раздел 61.5. Флаг amcanmulticol утверждает, что метод доступа поддерживает индексы с несколькими ключевыми столбцами, в то время как флаг amoptionalkey утверждает, что он позволяет сканировать без указания ограничительного условия для первого столбца индекса. Когда amcanmulticol равно false, amoptionalkey по сути говорит, поддерживает ли метод доступа полное сканирование индекса без ограничительного условия. Методы доступа, поддерживающие несколько столбцов индекса, должны поддерживать сканирование без ограничений на любой или все столбцы после первого; однако они могут требовать некоторого ограничения для первого столбца индекса, и это сигнализируется установкой amoptionalkey в false. Одной из причин, по которой индексный AM может установить amoptionalkey в false, является отсутствие индексации значений null. Поскольку большинство индексируемых операторов строги и, следовательно, не могут возвращать true для null-входов, на первый взгляд кажется привлекательным не хранить записи индекса для значений null: они все равно не могут быть возвращены индексным сканированием. Однако это аргумент не работает, когда индексное сканирование не имеет ограничительного условия для данного столбца индекса. На практике это означает, что индексы, у которых amoptionalkey равно true, должны индексировать 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.