每个索引访问方法都通过 pg_am 系统目录中的一行进行描述。 pg_am 条目指定索引访问方法的名称和处理函数。可以使用 CREATE ACCESS METHOD 和 DROP ACCESS METHOD SQL 命令创建和删除这些条目。
索引访问方法处理函数必须声明接受一个 internal 类型的参数,并返回伪类型 index_am_handler。该参数是一个 dummy 值,仅用于阻止从 SQL 命令直接调用处理函数。函数的返回值必须是一个 palloc 分配的 IndexAmRoutine 类型的结构体,其中包含核心代码需要知道的有关使用索引访问方法的所有信息。 IndexAmRoutine 结构体,也称为访问方法的API 结构,包含指定访问方法各种固定属性的字段,例如它是否支持多列索引。更重要的是,它包含指向访问方法支持函数的指针,这些函数执行访问索引的所有实际工作。这些支持函数是普通的 C 函数,在 SQL 级别不可见或不可调用。支持函数在 第 63.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 hashing using API consistent with the hash AM? */
bool amcanhash;
/* do operators within an opfamily have consistent equality semantics? */
bool amconsistentequality;
/* do operators within an opfamily have consistent ordering semantics? */
bool amconsistentordering;
/* 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; /* can be NULL */
ambulkdelete_function ambulkdelete;
amvacuumcleanup_function amvacuumcleanup;
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
amgettreeheight_function amgettreeheight; /* can be NULL */
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 */
/* interface functions to support planning */
amtranslate_strategy_function amtranslatestrategy; /* can be NULL */
amtranslate_cmptype_function amtranslatecmptype; /* can be NULL */
} IndexAmRoutine;
要有用,索引访问方法还必须在 pg_opfamily、pg_opclass、pg_amop 和 pg_amproc 中定义一个或多个操作符族和操作符类。这些条目允许规划器确定可以使用这种访问方法的索引进行哪些类型的查询限定。操作符族和类在 第 36.16 节 中进行了描述,这是阅读本章的先决条件。
单个索引由一个 pg_class 条目定义,该条目将其描述为一个物理关系,再加上一个 pg_index 条目,该条目显示了索引的逻辑内容 — 即,它拥有的索引列集及其关联的操作符类捕获的列的语义。索引列(键值)可以是基础表的简单列,也可以是基于表行的表达式。索引访问方法通常对索引键值来自何处不感兴趣(它总是会获得预先计算好的键值),但它会非常关心 pg_index 中的操作符类信息。这两个目录条目都可以作为传递给索引所有操作的 Relation 数据结构的一部分进行访问。
IndexAmRoutine 的一些标志字段具有非显而易见的含义。 amcanunique 的要求在 第 63.5 节 中进行了讨论。 amcanmulticol 标志断言该访问方法支持多键列索引,而 amoptionalkey 断言它允许在未提供第一个索引列的可索引限制子句的情况下进行扫描。当 amcanmulticol 为 false 时, amoptionalkey 实际上表示访问方法是否支持在没有限制子句的情况下进行全索引扫描。支持多个索引列的访问方法必须支持可以省略第一个列之后任何或所有列的限制的扫描;但是,它们可以要求第一个索引列出现某种限制,这通过设置 amoptionalkey 为 false 来信号。一个索引之所以AM可能设置 amoptionalkey 为 false,是因为它不索引 NULL 值。由于大多数可索引运算符是严格的,因此对于 NULL 输入它们无法返回 true,因此不存储 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 的组合是合理的:它意味着只能有一个键列,但也可以有包含的列。此外,包含的列必须允许为 NULL,这与 amoptionalkey 无关。
amsummarizing 标志指示访问方法是否按块(例如BRIN)对索引的元组进行汇总,汇总粒度至少为每个块。不支持单独指向元组,而是指向块范围的访问方法(如HOT),可能允许该HOT.