↑↓ 选择 ↵ 打开 ⌫ 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
测试与开发版本: 19 / devel
不受支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1
历史版本PostgreSQL 8.2 已于 2011 年 12 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本。

49.2. 索引访问方法函数 #

索引访问方法必须提供如下索引构建和维护函数:

IndexBuildResult *
ambuild (Relation heapRelation,
         Relation indexRelation,
         IndexInfo *indexInfo);

构建新索引。索引关系已在物理上创建,但内容为空。必须填入访问方法所需的固定数据,以及表中所有已有元组对应的条目。通常,ambuild函数会调用IndexBuildHeapScan()扫描表中现有的元组,并计算需要插入索引的键。函数必须返回一个通过 palloc 分配的结构体,其中包含新索引的统计信息。

bool
aminsert (Relation indexRelation,
          Datum *values,
          bool *isnull,
          ItemPointer heap_tid,
          Relation heapRelation,
          bool check_uniqueness);

将一个新元组插入已有索引。values和isnull数组给出要索引的键值,heap_tid则是要索引的 TID。如果访问方法支持唯一索引(其pg_am.amcanunique标志为 true),则check_uniqueness可能为真,此时访问方法必须验证不存在冲突的行;这也是访问方法通常唯一需要heapRelation参数的情形。详情参见第 49.5 节。如果插入了索引条目则结果为 TRUE,否则为 FALSE。(FALSE 结果并不表示错误条件,而是用于诸如索引方法拒绝索引 NULL 值之类的情况。)

IndexBulkDeleteResult *
ambulkdelete (IndexVacuumInfo *info,
              IndexBulkDeleteResult *stats,
              IndexBulkDeleteCallback callback,
              void *callback_state);

从索引中删除元组。这是一个“批量删除”操作,旨在通过扫描整个索引并检查每个条目是否应被删除来实现。必须调用传入的 callback 函数,其调用形式为 callback(TID, callback_state) returns bool,以确定由其引用的 TID 所标识的某个索引条目是否应删除。该函数必须返回 NULL,或者返回一个通过 palloc 分配的结构体,其中包含此次删除操作影响的统计信息。如果不需要向 amvacuumcleanup 传递信息,返回 NULL 也是可以的。

由于 maintenance_work_mem 有限,当待删除的元组很多时,ambulkdelete 可能需要被调用多次。参数 stats 是此前对该索引上一次调用的结果(在一次 VACUUM 操作中的第一次调用时它为 NULL)。这使得 AM 可以在整个操作过程中累积统计信息。通常,如果传入的 stats 非 NULL,ambulkdelete 会修改并返回同一个结构体。

IndexBulkDeleteResult *
amvacuumcleanup (IndexVacuumInfo *info,
                 IndexBulkDeleteResult *stats);

在一次 VACUUM 操作(零次或多次 ambulkdelete 调用)之后执行清理。它不一定要做返回索引统计信息之外的事情,但也可能执行批量清理,例如回收空索引页面。stats 是最后一次 ambulkdelete 调用返回的结果;如果因为没有需要删除的元组而没有调用 ambulkdelete,则为 NULL。如果结果不是 NULL,它必须是一个通过 palloc 分配的结构体。其中的统计信息将用于更新 pg_class,并在指定了 VERBOSE 时由 VACUUM 报告。如果索引在整个 VACUUM 操作期间完全没有变化,返回 NULL 也是可以的;否则应返回正确的统计信息。

void
amcostestimate (PlannerInfo *root,
                IndexOptInfo *index,
                List *indexQuals,
                RelOptInfo *outer_rel,
                Cost *indexStartupCost,
                Cost *indexTotalCost,
                Selectivity *indexSelectivity,
                double *indexCorrelation);

估计索引扫描的代价。该函数在下文 第 49.6 节 中有完整描述。

bytea *
amoptions (ArrayType *reloptions,
           bool validate);

解析并验证索引的 reloptions 数组。只有当该索引存在非 NULL 的 reloptions 数组时才会调用此函数。reloptions 是一个 text 数组,其中的条目形如 name=value。该函数应构造一个 bytea 值,并将其复制到索引 relcache 条目的 rd_options 字段中。这个 bytea 值中的数据内容由访问方法自行定义;大多数标准访问方法使用结构体 StdRdOptions。当 validate 为真时,若存在未识别选项或无效取值,函数应报告合适的错误消息;当 validate 为假时,无效条目应被静默忽略。(当装载已存储在 pg_catalog 中的选项时,validate 为假;此时只有在访问方法改变了选项规则时才可能发现无效条目,而忽略过时条目是合适的。)如果希望采用默认行为,返回 NULL 也是可以的。

索引的目的当然是支持扫描那些匹配可索引 WHERE 条件的元组,这种条件常被称为限定词或扫描键。关于索引扫描的语义,将在下面的 第 49.3 节 中更详细地说明。索引访问方法必须提供的扫描相关函数如下:

IndexScanDesc
ambeginscan (Relation indexRelation,
             int nkeys,
             ScanKey key);

开始一次新扫描。key 数组(长度为 nkeys)描述该索引扫描的扫描键。结果必须是一个通过 palloc 分配的结构体。出于实现上的原因,索引访问方法 必须通过调用 RelationGetIndexScan() 来创建这个结构体。大多数 情况下,ambeginscan 除了做这次调用之外不会做太多 工作;索引扫描启动中真正有意思的部分在 amrescan 中。

boolean
amgettuple (IndexScanDesc scan,
            ScanDirection direction);

沿给定方向(在索引中向前或向后)取出给定扫描中的下一个元组。若取得元组则返回 TRUE,若没有剩余匹配元组则返回 FALSE。返回 TRUE 时,元组 TID 存入scan结构体中。注意,“成功”仅表示索引中包含与扫描键匹配的条目,并不表示该元组一定仍存在于堆中,或者能够通过调用方的快照测试。

boolean
amgetmulti (IndexScanDesc scan,
            ItemPointer tids,
            int32 max_tids,
            int32 *returned_tids);

在给定扫描中取出多个元组。如果扫描还应继续则返回 TRUE,如果没有剩余匹配元组则返回 FALSE。tids指向一个由调用方提供的、包含max_tids个ItemPointerData记录的数组,本调用会把匹配元组的 TID 填入其中。*returned_tids被设为实际返回的 TID 数。即使返回值为 TRUE,这个数也可以小于max_tids,甚至为零。(这一规定允许访问方法在扫描中选择最高效的停止点,例如索引页边界。)amgetmulti和amgettuple不能在同一次索引扫描中同时使用;使用amgetmulti时还有其他限制,详见第 49.3 节。

void
amrescan (IndexScanDesc scan,
          ScanKey key);

重新开始给定的扫描,并且可以使用新的扫描键(若要继续使用旧键, 则为 key 传入 NULL)。注意,键的数量不允许 改变。实践中,这种重启特性通常用于嵌套循环连接选中了新的外层 元组、因此需要新的键比较值,但扫描键结构体保持不变的场景。 RelationGetIndexScan() 也会调用此函数,因此它既用于 索引扫描的初始设置,也用于重新扫描。

void
amendscan (IndexScanDesc scan);

结束扫描并释放资源。scan 结构体本身不应被 释放,但访问方法内部获取的任何锁或引用(pin)都必须被释放。

void
ammarkpos (IndexScanDesc scan);

标记当前扫描位置。访问方法只需要为每次扫描支持一个被记住的扫描位置。

void
amrestrpos (IndexScanDesc scan);

将扫描恢复到最近一次标记的位置。

按照惯例,索引访问方法函数的 pg_proc 条目应显示正确的参数个数,但把所有参数都声明为 internal 类型(因为大多数参数的类型对 SQL 而言是未知的,而且我们也不希望用户直接调用这些函数)。返回类型根据实际情况声明为 void、internal 或 boolean。唯一的例外是 amoptions,它应当正确地声明为接受 text[] 和 bool 并返回 bytea。这一规定允许客户端代码执行 amoptions 来测试选项设置的有效性。

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。