Postgresql 中文操作指南

64.1. Basic API Structure for Indexes #

pg_am 系统目录中的每一行都描述了一个索引访问方法。 pg_am 条目为索引访问方法指定一个名称和一个 handler function 。可以使用 CREATE ACCESS METHODDROP ACCESS METHOD SQL 命令创建和删除这些条目。

必须声明索引访问方法处理函数以接受类型 internal_的单个自变量并返回伪类型 _index_am_handler。自变量是一个虚拟值,仅用于防止直接从 SQL 命令调用处理函数。该函数的结果必须是已分配内存的 IndexAmRoutine_类型结构,它包含核心代码使用索引访问方法所需的所有内容。被称为访问方法的 _IndexAmRoutine 结构(也称为访问方法的 API struct)包括指定访问方法的各种固定属性的字段,例如它是否支持多列索引。更重要的是,它包含指向访问方法的支持函数的指针,这些函数执行访问索引的所有实际工作。这些支持函数是普通 C 函数,在 SQL 级别不可见或不可调用。支持函数在 Section 64.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_opfamilypg_opclasspg_amoppg_amproc 中定义一个或多个 operator familiesoperator classes 。这些项允许计划器确定可与该访问方法的索引一起使用哪些类型的查询限定条件。操作符系列和类在 Section 38.16 中描述,这是阅读本章的先决条件材料。

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

IndexAmRoutine_的某些标志字段具有不明显的含义。_amcanunique_的要求在 Section 64.5中进行了讨论。_amcanmulticol_标志断言该访问方法支持多键列索引,而 _amoptionalkey_断言它允许扫描而未对第一个索引列给出可索引的限制子句。当 _amcanmulticol_为假时,_amoptionalkey_基本上表示访问方法是否支持没有任何限制子句的全部索引扫描。支持多个索引列 _must_的访问方法支持省略第一个索引列后任何或所有列上的限制的扫描;但是,允许它们要求对第一个索引列出现某些限制,并且这是通过将 _amoptionalkey_设置为假来实现的。索引 AM 将 _amoptionalkey_设置为假的其中一个原因是它不索引空值。由于大多数可索引运算符都是严格的,因此不能为 null 输入返回 true,因此乍一看不为 null 值存储索引条目很有吸引力:反正索引扫描永远不会返回它们。但是,当索引扫描对给定索引列没有限制子句时,此自变量将失败。实际上,这意味着具有 _amoptionalkey_true 的索引必须索引 null 值,因为计划程序可能会决定在没有任何扫描键的情况下使用此类索引。相关的限制是支持多个索引列 _must_的索引访问方法支持在第一个索引列后的列中索引 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 标志指示访问方法是否汇总索引元组,其汇总粒度至少为每个块。可能允许 HOT 优化继续进行,但不能指向单个元组而是指向块范围(如 BRIN)的访问方法。这不适用于索引谓词中引用的属性,对该属性的更新始终会禁用 HOT。