TSM 处理程序函数返回包含指向下面描述的支持函数的指针的 palloc'd TsmRoutine
结构。大多数函数都是必需的,但有些是可选的,这些指针可以为 NULL。
void SampleScanGetSampleSize (PlannerInfo *root, RelOptInfo *baserel, List *paramexprs, BlockNumber *pages, double *tuples);
这个函数在计划期间被调用。它必须估算样本扫描期间将读取的关系页数,以及扫描将选择的元组数。(例如,可以通过估算抽样分数,然后将 baserel->pages
和 baserel->tuples
数字乘以该分数来确定这些数字,确保将结果四舍五入为整数。)paramexprs
列表保存了 TABLESAMPLE
子句的参数表达式。建议使用 estimate_expression_value()
尝试将这些表达式简化为常量,如果其值对于估算目的必需;但该函数必须提供大小估算,即使不能简化,并且即使值看起来无效也不应失败(记住它们只是运行时值会是多少的估算)。pages
和 tuples
参数是输出。
void InitSampleScan (SampleScanState *node, int eflags);
初始化以执行 SampleScan 计划节点。这在执行程序启动期间调用。在处理开始之前,它应执行所有所需的初始化。SampleScanState
节点已创建,但其 tsm_state
字段为空。InitSampleScan
函数可以为抽样方法分配任何所需的内部状态数据,并将指向它的指针存储在 node->tsm_state
中。有关要扫描的表的信息可通过 SampleScanState
节点的其他字段访问(但请注意,node->ss.ss_currentScanDesc
扫描描述符尚未设置)。eflags
包含描述执行程序对此计划节点的操作模式的标志位。
当 (eflags & EXEC_FLAG_EXPLAIN_ONLY)
为真时,扫描将不会实际执行,因此此函数只应执行使节点状态对 EXPLAIN
和 EndSampleScan
有效所需的最小操作。
此函数可以省略(将指针设置为 NULL),在这种情况下,BeginSampleScan
必须执行抽样方法所需的所有初始化。
void BeginSampleScan (SampleScanState *node, Datum *params, int nparams, uint32 seed);
开始执行采样扫描。这在首次尝试获取元组之前调用,如果需要重新启动扫描,则可能会再次调用。有关要扫描的表的的信息可通过 SampleScanState
节点的字段访问(但请注意,node->ss.ss_currentScanDesc
扫描描述符尚未设置)。长度为 nparams
的 params
数组包含在 TABLESAMPLE
子句中提供的参数值。这些值将具有一些采样方法的 parameterTypes
列表中指定的数量和类型,并且已检查过它们不为 null。seed
包含用于在采样方法中生成的所有随机数的种子;如果给定了 REPEATABLE
值,则它是由该值派生的哈希,如果没有给定,则是 random()
的结果。
此函数可以调整字段 node->use_bulkread
和 node->use_pagemode
。如果 node->use_bulkread
为 true
(默认情况下为 true),则扫描将使用一种缓冲区访问策略,在使用后鼓励回收缓冲区。如果扫描只访问表中的一小部分页,则可以合理地将其设置为 false
。如果 node->use_pagemode
为 true
(默认情况下为 true),则扫描将在每次访问页面上的所有元组的单次扫描中执行可见性检查。如果扫描只选择每次访问页面上的一小部分元组,则可将其合理地设置为 false
。这样做将导致执行更少的元组可见性检查,尽管每次检查都会更昂贵,因为需要更多锁定。
如果采样方法被标记为 repeatable_across_scans
,则它必须能够在重新扫描期间选择与最初相同的元组集,即 BeginSampleScan
的新鲜调用必须导致选择与之前相同的元组(如果 TABLESAMPLE
参数和种子没有改变)。
BlockNumber NextSampleBlock (SampleScanState *node, BlockNumber nblocks);
返回要扫描的下一页的块号,或 InvalidBlockNumber
(如果没有剩余要扫描的页)。
此函数可以省略(将指针设置为 NULL),在这种情况下,核心代码将对整个关联关系执行顺序扫描。这样的扫描可以使用同步扫描,因此采样方法不能假设在每次扫描时按相同顺序访问相关页面。
OffsetNumber NextSampleTuple (SampleScanState *node, BlockNumber blockno, OffsetNumber maxoffset);
返回将在指定页上进行采样的下一个元组的偏移量编号,如果不再有元组供采样,则返回 InvalidOffsetNumber
。 maxoffset
是页上使用的最大偏移量编号。
NextSampleTuple
并没有被明确告知 1 .. maxoffset
范围内哪些偏移量编号实际上包含有效元组。这通常不是问题,因为核心代码会忽略对采样缺失或不可见元组的请求;这样不会导致样本中有任何偏差。但是,如果需要,此函数可以使用 node->donetuples
检查返回了多少有效且可见的元组。
NextSampleTuple
不得假定 blockno
就是最近一次 NextSampleBlock
调用返回的页码。它是由某个较早的 NextSampleBlock
调用返回的,但是核心代码被允许在实际扫描页之前调用 NextSampleBlock
,以支持预取。可以假定一旦开始对给定页进行采样,连续的 NextSampleTuple
调用都会引用同一页,直至返回 InvalidOffsetNumber
。
void EndSampleScan (SampleScanState *node);
结束扫描并释放资源。通常无需释放 palloc 分配的内存,但应清理所有外部可见资源。在不存在此类资源的常见情况下,此函数可以省略(将指针设置为 NULL)。