每个索引访问方法都通过 pg_am
系统目录中的一个列进行描述。pg_am
条目指定索引访问方法的名称和处理程序函数。可以通过 CREATE ACCESS METHOD 和 DROP ACCESS METHOD SQL 命令创建和删除这些条目。
必须声明一个索引访问方法处理程序函数以接受一个类型为 internal
的单个参数,并返回伪类型 index_am_handler
。这个参数是一个仅用来防止处理程序函数从 SQL 命令直接被调用的虚拟值。函数的结果必须是一个分配 palloc 的 IndexAmRoutine
类型结构,该结构包含核心代码使用索引访问方法所需知道的所有内容。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
中定义一个或多个运算符族和运算符类。这些条目允许计划程序确定使用该访问方法的索引可以使用的查询限制的类型。运算符族和类在第 36.16 节中进行了描述,这是阅读本章的先决条件资料。
单个索引由一个 pg_class
条目进行定义,该条目将索引描述为物理关系;此外还有一个 pg_index
条目,显示索引的逻辑内容(即它拥有的索引列集,以及该列的语义,由相关运算符类捕获)。索引列(键值)既可以是基础表的简单列,也可以是表行的表达式。通常情况下,索引访问方法对索引键值来自哪里不感兴趣(始终处理预先计算的键值),但它对 pg_index
中的运营商类信息非常感兴趣。这两个编目条目都可以作为 Relation
数据结构的一部分进行访问,而该数据结构被传递到索引的所有操作中。
一些 IndexAmRoutine
Flag 域具有不明确的含义。在 第 62.5 节 中讨论了 amcanunique
的需求。amcanmulticol
Flag 断言访问方法支持多键列索引,而 amoptionalkey
断言它允许对未为第一个索引列提供可索引限制子句的扫描进行扫描。当 amcanmulticol
为 false 时,amoptionalkey
实际上说明访问方法是否支持没有任何限制子句的完全索引扫描。支持多个索引列的访问方法必须支持跳过列中第一个列之后的任意或全部列的限制的扫描;然而它们允许要求对第一个索引列应用一些限制,这通过将 amoptionalkey
设置为 false 来表示。如果索引AM将 amoptionalkey
设置为 false,原因之一是它不索引空值。由于大多数可索引运算符严格,因此不能为 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
Flag 表示访问方法是否支持“包含”列,即它可以在处理之外存储主键之外的其他列。前一段的要求只适用于主键。尤其是,amcanmulticol
=false
和 amcaninclude
=true
的组合是明智的:这意味着只能有一个主键,但也可以包含列。此外,必须允许包含列为 null,与 amoptionalkey
无关。
amsummarizing
标志指示访问方法是否总结索引元组,总结粒度至少为每个块。不指向各个元组而是指向块范围(例如BRIN)的访问方法可能允许HOT优化继续。这并非适用于索引谓词中引用的属性,此类属性的更新将始终禁用HOT.