51.2. 索引访问方法函数

索引访问方法必须提供的索引构造和维护函数有:

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

创建一个新索引。索引关系已经物理上创建好了,但是是空的。必须用索引访问方 法要求的固定数据填充它,还有就是所有已经在表里的行。通常,ambuild 函数会调用 IndexBuildHeapScan() 扫描该表以获取现 有行并计算需要插入索引的键字。

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

向现有索引插入一个新行。valuesisnull数组给出需要制作索引的键字值, 而 heap_tid 是要被索引的TID。 如果该访问方法支持唯一索引(它的 pg_am. amcanunique 标志是真),那么checkUnique可以是真,在这种情况下, 该索引访问方法必须校验表中不存在冲突的行;参阅Section 51.5获取细节。 通常在执行唯一性检查时访问方法仅仅需要heapRelation参数(尽管那时它将通过查堆来 检查元组活性)。

这个函数的布尔结果值仅仅在checkUniqueUNIQUE_CHECK_PARTIAL 时有意义。这种情况下一个“真”值意味着这个新条目是唯一可知的,反之,“假”意味着它可能不 是唯一的(而且一个延迟的唯一性校验必须是预定的)。对于其他情况的结果建议使用常量“假”。

有些索引可能不会为所有元组做索引。如果元组不被编入索引, aminsert应该什么都不做而仅仅返回。

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);

在一个(置空)操作(一个或多个 ambulkdelete 调用)之后清理。虽然不必做任何返回索引状态之外的任何其他事情,但是它通常用于 批量清理,比如说回收空的索引页面。stats 是最后的 ambulkdelete 调用返回的东西或者NULL(如果因为没有行需要删除而未调用 ambulkdelete的话)。如果结果不是NULL,那么它必须是一个 palloc 出来的结构。它包含的 统计信息将用于更新 pg_class并且由VACUUM报 告(如果给出了VERBOSE)。如果索引在VACUUM 操作 的过程中根本没有改变,那么返回NULL也是可以的,否则必须返回当前状态。

PostgreSQL8.4时,amvacuumcleanup 将也会在一个ANALYZE完成时被调用。这时stats总是空 而且任何返回值都将会被忽略。这种情况可以通过检测info-> analyze_only来区分。我们建议,在这样的调用中访问方法除了做插入后 的清理外什么也不做,并且那是仅仅是在一个自动清理的工作流程中。

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

估算一个索引扫描的开销。该函数在下面的Section 51.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条件 的行的扫描,这个条件通常叫修饰词扫描键字。索引扫描的语义在下面的Section 51.3里面有更完整的描述。 一个索引访问方法必须提供的与扫描有关的函数有:

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

开始一个新的扫描。key数组(长度是 nkeys)为该索引扫 描描述索引键字(可能是多个)。结果必须是一个palloc出来的结构。 由于实现的原因,索引访问方法必须通过调用RelationGetIndexScan() 来创建这个结构。在大多数情况下,amrescan本身除了调用上面这个 函数之外几乎不干别的事情;索引扫描启动时的有趣部分在amrescan里。

boolean
amgettuple (IndexScanDesc scan,
            ScanDirection direction);

在给出的扫描里抓取下一个行,向给出的方向移动(在索引里向前或者向后)。如果抓取 到了行,则返回TRUE,如果没有抓到匹配的行,返回FALSE。 在为TRUE的时候,该行的TID存储在 scan 结构里。请注意"成功"只 是意味着索引包含一个匹配扫描键字的条目,并不是说该行仍然在堆中存在, 或者是能够通过调用着的快照检查(译注:MVCC 快照,用于判断事务边界内的行可视性)。 在成功时,amgettuple 必须把 scan->xs_recheck 也设 置成TRUE或者FALSE。FSLSA意味着索引条目确定匹配搜索关键值。 TRUE意味着这并不确定,而且用搜索关键值表示的条件在被读取之后必须再与堆元组核对。 这条规定支持"lossy"索引操作符。 注意复查仅仅对搜索条件扩大;一个部分索引谓语(if any)从不被amgettuple调用程序复查。

amgetbitmap函数仅仅在访问方法支持"bitmap"索引扫描 时需要被提供。如果不是的话,在pg_am行的amgetbitmap 区域必须被设为零。

int64
amgetbitmap(IndexScanDesc scan,
             TIDBitmap *tbm);

在给出的扫描抓取多个行并且把它们添加到提供的调用TIDBitmap 中去,返回获取的元组的数量(这可能仅仅是一个粗略计数,事实上一些AMs不会 检测副本)。当把元组地址插入到点阵时,amgetbitmap可以指明复校 扫描条件对特定的元组地址是必需的。 这个与amgettuple xs_recheck输出函数是相似的。注意:照当前情况,维持这个特性是与维持维持位 图本身的损坏存储相关联的,因而调用方会为可复核的元重新检查扫描情况和部分索 引谓词(若有的话)。无论如何,那不会总是为真。 amgetbitmapamgettuple 不能被用于相同的索引扫描;正如在Section 51.3,当使用amgetbitmap的时候也有其他的限制条件。

amgetbitmap函数仅仅在访问方法支持"bitmap"索引扫描时 需要被提供。如果不是的话,在pg_am行的 amgetbitmap区域必须被设为零。

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 以选项设置的有效性。