支持的版本:当前 (17) / 16 / 15 / 14 / 13
开发版本:devel
不支持的版本:12 / 11 / 10 / 9.6 / 9.5

58.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) 为 true 时,实际上不会执行扫描,因此此函数应仅执行使节点状态对 EXPLAINEndSampleScan 有效的最低要求。

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

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

开始执行抽样扫描。这在第一次尝试获取元组之前调用,如果需要重新启动扫描,可能会再次调用。有关要扫描的表的信息可以通过 SampleScanState 节点的字段访问(但请注意,node->ss.ss_currentScanDesc 扫描描述符尚未设置)。长度为 nparamsparams 数组包含 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,以便在实际扫描页面之前支持预取。可以假设,一旦给定页面的抽样开始,连续的 NextSampleTuple 调用都指向同一页面,直到返回 InvalidOffsetNumber

void
EndSampleScan (SampleScanState *node);

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

提交更正

如果您在文档中看到任何不正确、与您使用特定功能的经验不符或需要进一步澄清的内容,请使用此表单报告文档问题。