61.1. 索引的基本 API 结构

每种索引访问方法在pg_am系统目录中的一行中进行描述。 pg_am条目为访问方法指定名称和* handler 函数*。可以使用创建访问方法下降访问方法 SQL 命令创建和删除这些条目。

必须声明索引访问方法处理程序函数以接受类型internal的单个参数并返回伪类型index_am_handler。该参数是一个伪值,仅用于防止直接从 SQL 命令调用处理函数。该函数的结果必须是类型为IndexAmRoutine的 palloc 结构,其中包含核心代码使用索引访问方法需要了解的所有内容。 IndexAmRoutine结构(也称为访问方法的* API struct *)包括指定访问方法的各种固定属性的字段,例如它是否可以支持多列索引。更重要的是,它包含支持访问方法功能的指针,这些方法完成访问索引的所有实际工作。这些支持函数是普通的 C 函数,在 SQL 级别不可见或不可调用。支持功能在Section 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;
    /* 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;
    /* 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 */
    amvalidate_function amvalidate;
    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_opfamilypg_opclasspg_amoppg_amproc中定义的* operator family operator classes *。这些条目使计划者可以确定哪些查询条件可以与该访问方法的索引一起使用。 Section 38.15中描述了操作符族和类,这是阅读本章的前提材料。

单个索引由pg_class条目定义的,该条目将其描述为物理关系,再加上pg_index条目,该条目显示索引的逻辑内容,即该索引所具有的索引列的集合以及这些列的语义,由捕获关联的运算符类。索引列(键值)可以是基础表的简单列,也可以是表行上的表达式。索引访问方法通常对索引键值的来源不感兴趣(它总是传递给预先计算的键值),但是它将对pg_index中的运算符类信息非常感兴趣。这两个目录条目都可以作为Relation数据结构的一部分进行访问,该数据结构传递给索引上的所有操作。

IndexAmRoutine的某些标志字段具有不明显的含义。 Section 61.5中讨论了amcanunique的要求。 amcanmulticol标志 assert 访问方法支持多列索引,而amoptionalkeyassert 它允许扫描,其中没有为第一索引列提供可索引的限制子句。当amcanmulticol为 false 时,amoptionalkey本质上说访问方法是否支持无任何限制子句的全索引扫描。支持多个索引列的访问方法必须支持扫描,该扫描将忽略对第一个索引之后的任何或所有列的限制;但是,允许它们要求对第一个索引列显示一些限制,这可以通过设置amoptionalkey false 来表示。索引 AM 可能将amoptionalkey设置为 false 的原因之一是它没有索引空值。由于大多数可索引的运算符都是严格的,因此不能为空 Importing 返回 true,因此乍一看,不存储空值的索引条目很吸引人:无论如何它们都不会被索引扫描返回。但是,当索引扫描没有给定索引列的限制子句时,此参数将失败。实际上,这意味着具有amoptionalkey true 的索引必须索引为空,因为计划者可能决定使用完全没有扫描键的索引。一个相关的限制是,支持多个索引列的索引访问方法必须(必须)支持在第一个列之后的列中为空值构建索引,因为计划者将假定索引可用于不限制这些列的查询。例如,考虑(a,b)上的索引和WHERE a = 4的查询。系统将假定索引可用于扫描具有a = 4的行,如果索引省略了b为 null 的行,则这是错误的。但是,可以省略第一个索引列为空的行。索引为空的索引访问方法也可以设置amsearchnulls,表示它支持IS NULLIS NOT NULL子句作为搜索条件。