2025年9月25日: PostgreSQL 18 发布!
支持的版本: 当前 (18) / 17 / 16 / 15 / 14 / 13
开发版本: devel
不支持的版本: 12 / 11 / 10 / 9.6 / 9.5

59.1. 采样方法支持函数 #

TSM 处理函数返回一个 palloc 分配的 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 包含用于采样方法中生成的任何随机数的种子;如果提供了 REPEATABLE 值,则它是从该值派生的哈希,否则它是 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 是页面上使用的最大偏移号。

注意

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

注意

NextSampleTuple 不能 假定 blockno 是上一次 NextSampleBlock 调用返回的相同页面号。它是从之前的某个 NextSampleBlock 调用返回的,但核心代码允许在实际扫描页面之前预先调用 NextSampleBlock,以支持预取。可以假定一旦开始对某个页面进行采样, successive NextSampleTuple 调用都将引用同一个页面,直到返回 InvalidOffsetNumber

void
EndSampleScan (SampleScanState *node);

结束扫描并释放资源。通常不需要释放 palloc 分配的内存,但任何外部可见的资源都应被清理。此函数可以省略(将指针设置为 NULL),在通常情况下,没有此类资源。

提交更正

如果您在文档中发现任何不正确、不符合您使用该特定功能时的经验或需要进一步说明的内容,请使用 此表单 报告文档问题。