索引访问方法必须提供的索引构造和维护函数有:
IndexBuildResult * 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 标志是真),那么 check_uniqueness 可以是真,在这种情况下,该索引访问方法必须校验表中不存在冲突的行;通常这是该索引访问方法会需要 heapRelation 参数的唯一的情况。参阅节49.5获取细节。如果插入了索引记录,则返回 TRUE ,否则返回 FALSE 。FALSE 结果并不表明发生了错误,只是用于类似一种索引访问方法(AM)拒绝给 NULL 建索引或者类似的场合。
IndexBulkDeleteResult * ambulkdelete (IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state);
从索引中删除行。这是一个"大批删除"的操作,通常都是通过扫描整个索引,检查每条记录,看看它是否需要被删除来实现的。可以调用传递进来的 callback 函数,调用风格是:callback(TID, callback_state) returns bool ,其作用是判断某个用其引用的 TID 标识的索引条目是否需要删除。必须返回 NULL 或者是一个 palloc 出来的,包含删除操作之效果的统计的结构。如果不需要向 amvacuumcleanup
传递信息,返回 NULL 也是 OK 的。
由于 maintenance_work_mem 的限制,在删除多行的时候 ambulkdelete
可能需要被调用多次,stats 参数是先前在这个索引上的调用结果(在一个 VACUUM 操作内部第一次调用的话则是 NULL)。这将允许 AM 在整个操作过程中积累统计信息。典型的,如果传递的 stats 不是 null 的话,ambulkdelete
将会修改并返回相同的结构。
IndexBulkDeleteResult * amvacuumcleanup (IndexVacuumInfo *info, IndexBulkDeleteResult *stats);
在一个 VACUUM 操作(一个或多个 ambulkdelete
调用)之后清理。虽然不必做任何返回索引状态之外的任何其他事情,但是它通常用于批量清理,比如说回收空的索引页面。stats 是最后的 ambulkdelete
调用返回的东西或者 NULL(如果因为没有行需要删除而未调用 ambulkdelete
的话)。如果结果不是 NULL ,那么它必须是一个 palloc 出来的结构。它包含的统计信息将用于更新 pg_class 并且由 VACUUM 报告(如果给出了 VERBOSE)。如果索引在 VACUUM 操作的过程中根本没有改变,那么返回 NULL 也是 OK 的,否则必须返回当前状态。
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 数组,仅当一个索引存在非空 reloptions 数组时才会被调用。reloptions 是一个 text 数组,包含 name=value 格式的项。该函数应当创建一个 bytea 值,该值将被拷贝进索引的 relcache 项的 rd_options 字段。bytea 值的数据内容可以由访问方法定义,不过目前所有的标准访问方法都使用 StdRdOptions 结构。当 validate 为真时,如果任何一个选项不可识别或者含有非法值,该函数都应当报告一个适当的错误信息;当 validate 为假时,非法项应该被悄悄的忽略。当载入已经存储在 pg_catalog 中的选项时,validate 为假,仅在访问方法已经改变了选项规则的时候才可能找到非法项,在此情况下可以忽略废弃的项。如果默认行为正是想要的,那么返回 NULL 也 OK 。
索引的目的当然是支持那些包含一个可以索引的 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 结构里。请注意"成功"只是意味着索引包含一个匹配扫描键字的条目,并不是说该行仍然在堆中存在,或者是能够通过调用着的快照检查(译注:MVCC 快照,用于判断事务边界内的行可视性)。
boolean amgetmulti (IndexScanDesc scan, ItemPointer tids, int32 max_tids, int32 *returned_tids);
在给出的扫描里抓取多个行。如果扫描需要继续,则返回 TRUE ,如果没有剩下的匹配行,返回 FALSE 。tids 指向一个调用着提供的 max_tids 条 ItemPointerData 记录的数组,用于填充匹配行的 TID 。*returned_tids 设置为实际返回的 TID 的数目。这个数目可以小于 max_tids 或者甚至是零,即使返回值是 TRUE 也如此。这样的设计就允许访问方法可以选择对其扫描的最高效的停止点,比如,在索引页的边界上。amgetmulti
和 amgettuple
不能在同义词索引扫描中使用;在使用 amgetmulti
的时候还有其它限制,在节49.3里给出解释。
void amrescan (IndexScanDesc scan, ScanKey key);
重启开始给出的扫描,可能使用的是一个新的扫描键字(要想继续使用原来的键字,给 key 传递一个 NULL)。请注意,不可能改变键字的个数。实际上这个重新开始的特性是在一个嵌套循环连接选取了一个新的外层行,因此需要一个新的键字比较值,但扫描键字的结构仍然相同的时候使用的。这个函数也被 RelationGetIndexScan()
调用,因此这个函数既用于索引扫描的初始化设置,也用于重复扫描。
void amendscan (IndexScanDesc scan);
结束扫描并释放资源。不应该释放 scan 本身,但访问方法内部使用的任何锁或者销都应该释放。
void ammarkpos (IndexScanDesc scan);
标记当前扫描位置。访问方法只需要支持每次扫描里面有一个被记住的扫描位置。
void amrestrpos (IndexScanDesc scan);
把扫描恢复到最近标记的位置。
通常,任何索引访问方法函数的 pg_proc 记录都应该显示正确数目的参数,只是把类型都声明为类型 internal(因为大多数参数的类型都是 SQL 不识别的类型,并且不希望用户直接调用该函数)。返回类型根据具体情况声明为 void, internal, boolean 。唯一的例外是 amoptions
,它应当被声明为接受 text[] 和 bool 并返回 bytea 。这样就允许客户端代码执行 amoptions
以选项设置的有效性。