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

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.1 已于 2010 年 11 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本。

48.2. 索引访问方法函数 #

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

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

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

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参数的情形。详情参见第 48.5 节。如果插入了索引条目则结果为 TRUE,否则为 FALSE。(FALSE 结果并不表示错误条件,而是用于诸如索引方法拒绝索引 NULL 值之类的情况。)

IndexBulkDeleteResult *
ambulkdelete (Relation indexRelation,
              IndexBulkDeleteCallback callback,
              void *callback_state);

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

IndexBulkDeleteResult *
amvacuumcleanup (Relation indexRelation,
                 IndexVacuumCleanupInfo *info,
                 IndexBulkDeleteResult *stats);

在一次 VACUUM 操作(一次或多次 ambulkdelete 调用)之后执行清理。索引访问方法不必提供此函数(如果不提供,pg_am 中的相应条目必须为零)。如果提供了此函数,它通常用于批量清理,例如回收空索引页面。info 提供一些额外的参数,例如统计报告的消息级别;stats 是最后一次 ambulkdelete 调用返回的结果。amvacuumcleanup 可以在返回之前替换或修改这个结构体。如果结果不是 NULL,它必须是一个通过 palloc 分配的结构体。其中的统计信息将在指定了 VERBOSE 时由 VACUUM 报告。

索引的目的当然是支持扫描那些匹配可索引 WHERE 条件的元组,这种条件常被称为限定词或扫描键。关于索引扫描的语义,将在下面的 第 48.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时还有其他限制,详见第 48.3 节。

void
amrescan (IndexScanDesc scan,
          ScanKey key);

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

void
amendscan (IndexScanDesc scan);

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

void
ammarkpos (IndexScanDesc scan);

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

void
amrestrpos (IndexScanDesc scan);

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

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

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

按照惯例,索引访问方法函数的 pg_proc 条目应显示正确的参数个数,但把所有参数都声明为 internal 类型(因为大多数参数的类型对 SQL 而言是未知的,而且我们也不希望用户直接调用这些函数)。返回类型根据实际情况声明为 void、internal 或 boolean。

提交更正

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