Postgresql 中文操作指南

60.1. Sampling Method Support Functions #

TSM 处理程序函数返回一个 palloc’d TsmRoutine 结构,其中包含指向以下所述支持函数的指针。大多数函数都是必需的,但有些是可选的,并且这些指针可以为 NULL。

void
SampleScanGetSampleSize (PlannerInfo *root,
                         RelOptInfo *baserel,
                         List *paramexprs,
                         BlockNumber *pages,
                         double *tuples);

在计划期间调用此函数。它必须估计样本扫描期间将读取的关系页数以及扫描将选择的元组数。(例如,可以根据估计的抽样分数,然后将 baserel→pagesbaserel→tuples 数字乘以该分数,确保将结果舍入为整数来确定。)paramexprs 列表包含 TABLESAMPLE 子句的参数表达式。建议使用 estimate_expression_value() 尝试将这些表达式缩减为常量,如果其值用于估计目的;但是,即使无法缩减,函数也必须提供大小估计,并且即使值似乎无效也不应失败(请记住,它们只是对运行时值估计)。pagestuples 参数是输出。

void
InitSampleScan (SampleScanState *node,
                int eflags);

初始化 SampleScan 计划节点的执行。这在执行器启动期间调用。它应在处理开始前执行任何必要的初始化。SampleScanState 节点已创建,但其 tsm_state 字段为 NULL。InitSampleScan 函数可以 palloc 抽样方法需要的任何内部状态数据,并在 node→tsm_state 中将其指针存储。有关要扫描的表的相关信息可以通过 SampleScanState 节点的其他字段访问(但请注意,node→ss.ss_currentScanDesc 扫描描述符尚未设置)。eflags 包含描述此计划节点执行器操作模式的标志位。

(eflags & EXEC_FLAG_EXPLAIN_ONLY) 为真时,扫描将实际执行,因此此函数应仅执行 EXPLAINEndSampleScan 所需的最小操作以使节点状态有效。

这个函数可以被省略(将指针设置为 NULL),在这种情况下,BeginSampleScan 必须执行采样方法所需的所有初始化。

void
BeginSampleScan (SampleScanState *node,
                 Datum *params,
                 int nparams,
                 uint32 seed);

开始执行采样扫描。这在首次尝试提取元组之前调用,并且在需要重新启动扫描时可能再次调用。有关要扫描的表的详细信息可以通过 SampleScanState 节点的字段获得(但请注意,node→ss.ss_currentScanDesc 扫描描述符尚未设置)。params 数组的长度为 nparams,它包含 TABLESAMPLE 子句中提供的参数的值。这些值将具有采样方法的 parameterTypes 列表中指定的数量和类型,并且已检查它们不为 null。seed 包含要在采样方法中生成的任何随机数中使用的种子; 如果给出了一个,它要么是源自 @{12} 的哈希,要么如果不是,那么就是 random() 的结果。

此函数可能会调整 node→use_bulkreadnode→use_pagemode 字段。如果 node→use_bulkreadtrue,默认情况下它就是如此,则扫描将使用鼓励在使用后回收缓冲区的缓冲区访问策略。如果扫描只会访问表格页面的一小部分,则将其设置为 false 可能比较合理。如果 node→use_pagemodetrue,默认情况下它就是如此,则扫描将对每个已访问页面的所有元组执行一次可见性检查。如果扫描将仅选择每个已访问页面上的一小部分元组,则将其设置为 false 可能是合理的。这样将导致执行较少的元组可见性检查,尽管每一个都会更昂贵,因为它需要更多的锁定。

如果采样方法被标记为 repeatable_across_scans,它必须能够在重新扫描期间选择与最初相同的元组集,即 BeginSampleScan 的一次全新调用必须会导致选择与之前相同的元组(如果 TABLESAMPLE 参数和种子没有改变)。

BlockNumber
NextSampleBlock (SampleScanState *node, BlockNumber nblocks);

返回要扫描的下一页的块号,如果没有要扫描的页面,则返回 InvalidBlockNumber

此函数可以被省略(将指针设置为 NULL),在这种情况下,核心代码将对整个关系执行顺序扫描。这样的扫描可以使用同步扫描,这样采样方法无法假设关系页面在每次扫描中都以相同的顺序进行访问。

OffsetNumber
NextSampleTuple (SampleScanState *node,
                 BlockNumber blockno,
                 OffsetNumber maxoffset);

返回指定页面上要采样的下一个元组的偏移号,如果没有要采样的元组,则返回 InvalidOffsetNumbermaxoffset 是页面上使用的最大偏移号。

Note

NextSampleTuple 没有被明确告知 1 .. maxoffset 范围中的哪些偏移量实际上包含有效的元组。这通常不是问题,因为核心代码会忽略获取缺失或不可见的元组的请求;这不会导致样本有任何偏差。但是,如果需要,该函数可以使用 node→donetuples 检查它返回的元组中有多少是有效且可见的。

Note

NextSampleTuple 必须 not 假设 blockno 是最近一次 NextSampleBlock 调用返回的相同页码。它是由某个先前的 NextSampleBlock 调用返回的,但内核代码允许在实际扫描页面之前调用 NextSampleBlock,以支持预取。可以假设,一旦某个给定页面的采样开始,连续的 NextSampleTuple 调用会始终引用同一页面,直到返回 InvalidOffsetNumber

void
EndSampleScan (SampleScanState *node);

结束扫描并释放资源。通常在不必释放 palloc’d 内存,但应该清理所有外部可见的资源。在不存在此类资源的常见情况下,此函数可以被省略(将指针设置为 NULL)。