58.1. 采样方法支持功能

TSM 处理程序函数返回一个 palloc 的TsmRoutine结构,该结构包含指向下面描述的支持函数的指针。大多数功能是必需的,但有些是可选的,并且这些指针可以为 NULL。

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

在计划期间调用此功能。它必须估计在 samples 扫描期间将读取的关系页面的数量,以及将由扫描选择的 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扫描 Descriptors)。 eflags包含标志位,这些标志位描述此计划节点的执行者的操作模式。

(eflags & EXEC_FLAG_EXPLAIN_ONLY)为 true 时,实际上将不执行扫描,因此此功能应仅执行使节点状态对EXPLAINEndSampleScan有效的最低要求。

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

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

开始执行采样扫描。在第一次尝试获取 Tuples 之前调用此方法,如果需要重新启动扫描,则可以再次调用此方法。可通过SampleScanState节点的字段访问有关要扫描的表的信息(但请注意,尚未设置node->ss.ss_currentScanDesc扫描 Descriptors)。长度为nparamsparams数组包含TABLESAMPLE子句中提供的参数的值。这些将具有在采样方法的parameterTypes列表中指定的数量和类型,并且已被检查为不为 null。 seed包含用于在采样方法内生成的任何随机数的种子;如果给出了REPEATABLE值,则它是从REPEATABLE值派生的哈希值;如果没有给出,则是random()的结果。

此功能可以调整字段node->use_bulkreadnode->use_pagemode。如果node->use_bulkreadtrue(默认情况下为true),则扫描将使用缓冲区访问策略,该策略鼓励在使用后回收缓冲区。如果扫描仅访问表页面的一小部分,则将其设置为false可能是合理的。如果node->use_pagemodetrue(默认情况下为true),则扫描将在一次通过中对每个访问页面上的所有 Tuples 执行可见性检查。如果扫描仅在每个访问页面上选择一小部分 Tuples,则将其设置为false可能是合理的。这将导致执行更少的 Tuples 可见性检查,尽管每个检查都将更加昂贵,因为它将需要更多的锁定。

如果采样方法标记为repeatable_across_scans,则它必须能够在重新扫描期间选择与原来相同的 Tuples 集,即重新调用BeginSampleScan必须导致选择与以前相同的 Tuples(如果TABLESAMPLE参数和种子请勿更改)。

BlockNumber
NextSampleBlock (SampleScanState *node);

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

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

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

返回在指定页面上要采样的下一个 Tuples 的偏移号;如果没有剩余要采样的 Tuples,则返回InvalidOffsetNumbermaxoffset是页面上使用的最大偏移量。

Note

没有明确告知NextSampleTuple 1 .. maxoffset范围中的哪个偏移量实际上包含有效的 Tuples。这通常不是问题,因为核心代码会忽略对缺少或不可见的 Tuples 进行采样的请求。不应导致 samples 出现任何偏差。但是,如有必要,该函数可以检查node->ss.ss_currentScanDesc->rs_vistuples[]以确定哪些 Tuples 有效且可见。 (这要求node->use_pagemodetrue.)

Note

NextSampleTuple不能(*)*假设blockno是最近一次NextSampleBlock调用返回的页码。它是由先前的NextSampleBlock调用返回的,但是允许核心代码在实际扫描页面之前调用NextSampleBlock以便支持预取。可以确定,一旦开始采样给定的页面,随后的NextSampleTuple调用都将引用同一页面,直到返回InvalidOffsetNumber

void
EndSampleScan (SampleScanState *node);

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