Базовая структура API для индексов
Каждый метод доступа к индексу описывается строкой в системном каталоге pg_am. В записи pg_am
указывается имя и функция-обработчик для метода доступа к индексу. Эти записи можно создавать и удалять с помощью SQL-команд CREATE ACCESS METHOD
и DROP ACCESS METHOD
.
Функция-обработчик метода доступа к индексу должна быть объявлена так, чтобы принимать единственный аргумент типа internal
и возвращать псевдотип index_am_handler
. Аргумент является фиктивным значением, которое просто служит для того, чтобы функции-обработчики не вызывались непосредственно из команд SQL. Результатом функции должна быть структура типа IndexAmRoutine
(в памяти palloc), которая содержит все, что необходимо знать коду ядра для использования метода доступа к индексу. Структура IndexAmRoutine
, также называемая API-структурой метода доступа, включает поля, определяющие различные фиксированные свойства метода доступа, например, может ли он поддерживать многос толбцовые индексы. Что более важно, она содержит указатели на функции поддержки метода доступа, которые выполняют всю реальную работу по доступу к индексам. Эти вспомогательные функции - обычные функции языка C, они не видны и не вызываются на уровне SQL. Функции поддержки описаны в разделе «Функции для индексных методов доступа».
Структура 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. Эти записи позволяют планировщику определить, какие типы квалификаций запросов могут использоваться с индексами данного метода доступа.
Отдельный индекс определяется записью pg_class, которая описывает его как физическое отношение, и записью pg_index, которая показывает логическое содержание индекса - то есть набор индексных столбцов, которые он имеет, и семантику этих столбцов, отраженную в соответствующих классах операторов. Колонки индекса (значения ключей) могут быть как простыми колонками базовой таблицы, так и выражениями над строками таблицы. Метод доступа к индексу обычно не интересуется, откуда берутся значения ключей индекса (ему всегда передаются предварительно вычисленные значения ключей), но его очень интересует информация о классах операторов в pg_index
. Обе эти записи каталога могут быть доступны как часть структуры данных Relation, которая передается всем операциям над индексом.
Некоторые поля флагов IndexAmRoutine
имеют неочевидные последствия. Требования amcanunique
обсуждаются в разделе «Проверки уникальности индексов». Флаг amcanmulticol
утверждает, что метод доступа поддерживает многоключевые колоночные индексы, а amoptionalkey
утверждает, что он разрешает сканирование, когда для первого индексного столбца не задано условие ограничения индексируемости. Когда amcanmulticol
равен false
, amoptionalkey
по сути говорит, поддерживает ли метод доступа полноиндексное сканирование без каких-либо ограничительных условий. Методы доступа, поддерживающие несколько индексных столбцов*, должны* поддерживать сканирование, которое не содержит ограничений для любого или всех столбцов после первого; однако им разрешается требовать, чтобы для первого индексного столбца появлялось некоторое ограничение, и это сигнализируется установкой amoptionalkey
в false
. Одна из причин, по которой индексный AM может установить значение amoptionalkey
в false
, заключается в том, что он не индексирует нулевые значения. Поскольку большинство индексируемых операторов являются строгими и, следовательно, не могут возвращать true для нулевых входов, на первый взгляд, привлекательно не хранить индексные записи для нулевых значений: они все равно никогда не будут возвращены при индексном сканировании. Однако этот аргумент не работает, если индексное сканирование не имеет ограничительного условия для данного индексного столбца. На практике это означает, что индексы с переменным ключом true должны индексировать нули, поскольку планировщик может решить использовать такой индекс вообще без ключей сканирования. Связанное с этим ограничение заключается в том, что метод доступа к индексу, поддерживающий несколько столбцов индекса*, должен* поддерживать индексирование нулевых значений в столбцах после первого, поскольку планировщик будет считать, что индекс можно использовать для запросов, которые не ограничивают эти столбцы. Например, рассмотрим индекс на (a,b) и запрос с WHERE a = 4
. Система предположит, что индекс можно использовать для сканирования строк с a = 4
, что будет неправильно, если индекс пропустит строки, в которых b
равно null
. Однако можно пропустить строки, в которых первый индексируемый столбец равен null
. Метод доступа к индексу, который делает индексные нули, может также установить amsearchnulls
, указывая, что он поддерживает условия поиска IS NULL
и IS NOT NULL
.
Флаг amcaninclude
указывает, поддерживает ли метод доступа «включенные столбцы», то есть может ли он хранить (без обработки) дополнительные столбцы, помимо ключевого столбца (столбцов). Требования предыдущего параграфа относятся только к ключевым столбцам. В частности, комбинация amcanmulticol=false
и amcaninclude=true
является разумной: она означает, что может быть только один ключевой столбец, но могут быть и включаемые столбцы. Кроме того, включенные столбцы должны иметь возможность быть нулевыми, независимо от amoptionalkey
.