TSM 处理函数返回一个使用 palloc 分配的 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
字段为 NULL。InitSampleScan
函数可以使用 palloc 分配抽样方法所需的任何内部状态数据,并将指向它的指针存储在 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
扫描描述符尚未设置)。params
数组(长度为 nparams
)包含在 TABLESAMPLE
子句中提供的参数的值。这些将具有抽样方法的 parameterTypes
列表中指定的数量和类型,并且已检查不为 null。seed
包含一个用于在抽样方法中生成的任何随机数的种子;如果提供了 REPEATABLE
值,则它是一个从该值派生的哈希值,否则为 random()
的结果。
此函数可能会调整字段 node->use_bulkread
和 node->use_pagemode
。如果 node->use_bulkread
为 true
(默认情况下为真),则扫描将使用一种缓冲区访问策略,该策略鼓励在使用后回收缓冲区。如果扫描仅访问表的一小部分页面,则将其设置为 false
可能合理。如果 node->use_pagemode
为 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)。
如果您在文档中看到任何不正确的内容,与您对特定功能的体验不符,或者需要进一步澄清,请使用此表单报告文档问题。