Redrock Postgres 搜索 英文
版本: 9.3 / 9.4 / 9.5 / 9.6 / 10 / 11 / 12 / 13 / 14 / 15 / 16

58.2. 索引访问方法函数

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

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

创建一个新索引。索引关系已经被物理创建,但是是空的。 必须用索引访问方法要求的固定数据填充它,外加所有已经在表里的行的项。 通常,ambuild函数会调用IndexBuildHeapScan() 来扫描表以获取现有元组并计算需要被插入到索引的键。 该函数必须返回一个已分配内存的结构,其中包含关于新索引的统计信息。

void
ambuildempty (Relation indexRelation);

构建一个空索引,并且把它写入到给定关系的初始化分叉中( INIT_FORKNUM)。只会为不做日志的表调用这个方法, 被写入到初始化分叉的空索引在每次服务器启动时将被复制到主关系分叉中。

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

向现有索引插入一个新元组。valuesisnull 数组给出需要被索引的键值,而heap_tid是要被索引的 TID。 如果该访问方法支持唯一索引(它的pg_am.amcanunique 标志为真),那么checkUnique指示要执行的唯一性检查类型。 这根据唯一约束是否为可推迟的而变化,详见第 58.5 节。 通常在执行唯一性检查时访问方法仅需要heapRelation参数 (因为那时它将不得不到堆中验证元组的存活性)。

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

有些索引可能不会索引所有元组。如果元组不被索引,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非空,ambulkdelete 将会修改并返回相同的结构。

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

在一个VACUUM操作(零个或更多次ambulkdelete调用) 后清空。虽然不必做任何返回索引统计信息之外的事情,但是它可能执行批量清理, 例如回收空索引页面。stats是最后一次ambulkdelete 调用返回的东西或者 NULL(如果没有元组需要删除而未调用ambulkdelete)。 如果结果不是 NULL,那么它必须是一个已经被 palloc 的结构。 它包含的统计信息将用于更新pg_class并且由VACUUM报告 (如果给出了VERBOSE)。如果索引在VACUUM 操作期间根本没有改变,那么返回 NULL 也是可以的,否则必须返回正确的统计信息。

PostgreSQL 8.4 开始,amvacuumcleanup 将也会在一个ANALYZE操作结束时被调用。这种情况中stats 总是 NULL 并且任何返回值都将会被忽略。这种情况可以通过检测 info->analyze_only来区分。我们建议, 在这样的调用中访问方法除了做插入后的清理之外什么也不做, 并且那是仅仅是在一个自动清理工作者进程中。

bool
amcanreturn (Relation indexRelation, int attno);

通过返回形式为IndexTuple的索引项的被索引列值, 检查索引在给定字段上是否能支持仅索引的扫描。 属性编号是基于1的,即第一列的attno是1。如果支持返回 TRUE,否则返回 FALSE。 如果访问方法根本不支持仅索引的扫描,可以把pg_am行中的 amcanreturn字段设置为零。

void
amcostestimate (PlannerInfo *root,
                IndexPath *path,
                double loop_count,
                Cost *indexStartupCost,
                Cost *indexTotalCost,
                Selectivity *indexSelectivity,
                double *indexCorrelation);

估计一次索引扫描的开销。这个函数在下面的 第 58.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条件的元组, 常常也被称为限定词扫描键。 索引扫描的语义在下面的第 58.3 节中有完整的描述。 一个索引访问方法可以支持"普通"索引扫描、"位图" 索引扫描或者两者。一个索引访问方法必须或可能提供的与扫描相关的函数是:

IndexScanDesc
ambeginscan (Relation indexRelation,
             int nkeys,
             int norderbys);

为一个索引扫描做准备。nkeysnorderbys 参数说明要被用在扫描中的条件和排序操作符的数目,它们可以用于空间分配目的。 注意扫描键的实际值还没有被提供。结果必须是一个 palloc 过的结构。 由于实现的原因,索引访问方法必须通过调用 RelationGetIndexScan()来创建这个结构。在大多数情况中, ambeginscan除了做这个调用和获取锁之外不会做很多工作, 索引扫描启动中有趣的部分在amrescan中。

void
amrescan (IndexScanDesc scan,
          ScanKey keys,
          int nkeys,
          ScanKey orderbys,
          int norderbys);

开始或者重新开始一个索引扫描,可能使用的是一个新的扫描键。 (要想使用之前传递的键重新开始,给keys 和/或orderbys 传递 NULL)。请注意,使用的键或排序操作符的个数不能大于传递给 ambeginscan的个数。实际上这个重新开始特性的使用场景是: 在一个嵌套循环连接选取了一个新的 outer 元组时,因此需要一个新的键比较值, 但扫描键结构仍然保持相同。

boolean
amgettuple (IndexScanDesc scan,
            ScanDirection direction);

在给定扫描中取下一个元组,向给定方向移动(在索引中向前或者向后)。 如果取到了元组,则返回 TRUE,如果没有匹配的元组,返回 FALSE。 在 TRUE 的情况中,该元组的 TID 被存储在scan结构中。 请注意"成功"只意味着索引包含一个匹配扫描键的项, 并不意味该元组仍然在堆中存在,或者是能够通过调用者的快照测试。 在成功时,amgettuple也必须把scan->xs_recheck 设置成 TRUE 或者 FALSE。FALSE 意味着它确定索引项匹配扫描键。 TRUE 意味着它并不确定,而且必须在取得堆元组之后对它重新检查扫描键表示的条件。 这条规定支持"有损的"索引操作符。注意重新检查仅仅对扫描条件扩展; 一个部分索引谓语(如果有)从不被amgettuple调用者重新检查。

如果索引支持仅索引扫描(即amcanreturn对它返回 TRUE), 则在成功时 AM 也必须检查scan->xs_want_itup, 并且如果检查为真它必须返回索引项的原始被索引数据, 以存储在scan->xs_itup和元组描述符scan->xs_itupdesc 中的IndexTuple指针的形式。 (由指针引用的数据的管理是访问方法的责任。至少在为扫描下一次调用 amgettupleamrescanamendscan之前, 该数据必须是完好的)。

如果访问方法支持"普通"索引扫描,只需要提供amgettuple 函数。如果不支持,它的pg_am行的amgettuple 域必须被设置为零。

int64
amgetbitmap (IndexScanDesc scan,
             TIDBitmap *tbm);

在给定扫描中取所有元组并且把它们添加到调用者提供的TIDBitmap中 (即,把元组 ID 的集合 OR 到已经存在于位图中的东西里面)。 返回被取得的元组的数量(这可能仅仅是一个近似计数,例如一些 AM 不会去重)。 在把元组 ID 插入到位图时,amgetbitmap 可以指明对指定元组 ID 要求重新检查扫描条件。这与amgettuplexs_recheck输出参数类似。注意:在当前的实现中, 这个特性的支持是和对位图本身有损存储的支持合并在一起的, 并且调用者会对可重新检查的元组检查扫描条件和部分索引谓词(如果有)。 但是,那不会总是真的。amgetbitmapamgettuple 不能被用于同一个索引扫描;正如第 58.3 节中所解释的, 在使用amgetbitmap时也有其他的限制条件。

如果访问方法支持"bitmap"索引扫描,则仅需要提供amgetbitmap函数。 如果不支持,它的pg_am行中的amgetbitmap 域必须被设置为零。

void
amendscan (IndexScanDesc scan);

结束扫描并释放资源。不应该释放scan结构本身, 但访问方法内部使用的任何锁或者 pin 都应该被释放。

void
ammarkpos (IndexScanDesc scan);

标记当前扫描位置。访问方法只需要支持每个扫描里面有一个被标记的扫描位置。

void
amrestrpos (IndexScanDesc scan);

把扫描恢复到最近标记的位置。

通常,一个索引访问方法函数的pg_proc 项都应该显示参数的正确数目,但是把它们都声明为类型internal (因为大多数参数的类型对于 SQL 都是未知的, 并且我们不希望用户以任何方式直接调用这些函数)。 返回类型根据具体情况被声明为voidinternalboolean。 唯一的例外是amoptions,它应当被正确地声明为接受text[]bool并返回bytea。这个规定允许客户端代码执行 amoptions来测试选项设置的有效性。