Postgresql 中文操作指南
60.1. Sampling Method Support Functions #
TSM 处理程序函数返回一个 palloc’d TsmRoutine 结构,其中包含指向以下所述支持函数的指针。大多数函数都是必需的,但有些是可选的,并且这些指针可以为 NULL。
The TSM handler function returns a palloc’d TsmRoutine struct containing pointers to the support functions described below. Most of the functions are required, but some are optional, and those pointers can be NULL.
void
SampleScanGetSampleSize (PlannerInfo *root,
RelOptInfo *baserel,
List *paramexprs,
BlockNumber *pages,
double *tuples);
在计划期间调用此函数。它必须估计样本扫描期间将读取的关系页数以及扫描将选择的元组数。(例如,可以根据估计的抽样分数,然后将 baserel→pages 和 baserel→tuples 数字乘以该分数,确保将结果舍入为整数来确定。)paramexprs 列表包含 TABLESAMPLE 子句的参数表达式。建议使用 estimate_expression_value() 尝试将这些表达式缩减为常量,如果其值用于估计目的;但是,即使无法缩减,函数也必须提供大小估计,并且即使值似乎无效也不应失败(请记住,它们只是对运行时值估计)。pages 和 tuples 参数是输出。
This function is called during planning. It must estimate the number of relation pages that will be read during a sample scan, and the number of tuples that will be selected by the scan. (For example, these might be determined by estimating the sampling fraction, and then multiplying the baserel→pages and baserel→tuples numbers by that, being sure to round the results to integral values.) The paramexprs list holds the expression(s) that are parameters to the TABLESAMPLE clause. It is recommended to use estimate_expression_value() to try to reduce these expressions to constants, if their values are needed for estimation purposes; but the function must provide size estimates even if they cannot be reduced, and it should not fail even if the values appear invalid (remember that they’re only estimates of what the run-time values will be). The pages and tuples parameters are outputs.
void
InitSampleScan (SampleScanState *node,
int eflags);
初始化 SampleScan 计划节点的执行。这在执行器启动期间调用。它应在处理开始前执行任何必要的初始化。SampleScanState 节点已创建,但其 tsm_state 字段为 NULL。InitSampleScan 函数可以 palloc 抽样方法需要的任何内部状态数据,并在 node→tsm_state 中将其指针存储。有关要扫描的表的相关信息可以通过 SampleScanState 节点的其他字段访问(但请注意,node→ss.ss_currentScanDesc 扫描描述符尚未设置)。eflags 包含描述此计划节点执行器操作模式的标志位。
Initialize for execution of a SampleScan plan node. This is called during executor startup. It should perform any initialization needed before processing can start. The SampleScanState node has already been created, but its tsm_state field is NULL. The InitSampleScan function can palloc whatever internal state data is needed by the sampling method, and store a pointer to it in node→tsm_state. Information about the table to scan is accessible through other fields of the SampleScanState node (but note that the node→ss.ss_currentScanDesc scan descriptor is not set up yet). eflags contains flag bits describing the executor’s operating mode for this plan node.
当 (eflags & EXEC_FLAG_EXPLAIN_ONLY) 为真时,扫描将实际执行,因此此函数应仅执行 EXPLAIN 和 EndSampleScan 所需的最小操作以使节点状态有效。
When (eflags & EXEC_FLAG_EXPLAIN_ONLY) is true, the scan will not actually be performed, so this function should only do the minimum required to make the node state valid for EXPLAIN and EndSampleScan.
这个函数可以被省略(将指针设置为 NULL),在这种情况下,BeginSampleScan 必须执行采样方法所需的所有初始化。
This function can be omitted (set the pointer to NULL), in which case BeginSampleScan must perform all initialization needed by the sampling method.
void
BeginSampleScan (SampleScanState *node,
Datum *params,
int nparams,
uint32 seed);
开始执行采样扫描。这在首次尝试提取元组之前调用,并且在需要重新启动扫描时可能再次调用。有关要扫描的表的详细信息可以通过 SampleScanState 节点的字段获得(但请注意,node→ss.ss_currentScanDesc 扫描描述符尚未设置)。params 数组的长度为 nparams,它包含 TABLESAMPLE 子句中提供的参数的值。这些值将具有采样方法的 parameterTypes 列表中指定的数量和类型,并且已检查它们不为 null。seed 包含要在采样方法中生成的任何随机数中使用的种子; 如果给出了一个,它要么是源自 @{12} 的哈希,要么如果不是,那么就是 random() 的结果。
Begin execution of a sampling scan. This is called just before the first attempt to fetch a tuple, and may be called again if the scan needs to be restarted. Information about the table to scan is accessible through fields of the SampleScanState node (but note that the node→ss.ss_currentScanDesc scan descriptor is not set up yet). The params array, of length nparams, contains the values of the parameters supplied in the TABLESAMPLE clause. These will have the number and types specified in the sampling method’s parameterTypes list, and have been checked to not be null. seed contains a seed to use for any random numbers generated within the sampling method; it is either a hash derived from the REPEATABLE value if one was given, or the result of random() if not.
此函数可能会调整 node→use_bulkread 和 node→use_pagemode 字段。如果 node→use_bulkread 为 true,默认情况下它就是如此,则扫描将使用鼓励在使用后回收缓冲区的缓冲区访问策略。如果扫描只会访问表格页面的一小部分,则将其设置为 false 可能比较合理。如果 node→use_pagemode 为 true,默认情况下它就是如此,则扫描将对每个已访问页面的所有元组执行一次可见性检查。如果扫描将仅选择每个已访问页面上的一小部分元组,则将其设置为 false 可能是合理的。这样将导致执行较少的元组可见性检查,尽管每一个都会更昂贵,因为它需要更多的锁定。
This function may adjust the fields node→use_bulkread and node→use_pagemode. If node→use_bulkread is true, which it is by default, the scan will use a buffer access strategy that encourages recycling buffers after use. It might be reasonable to set this to false if the scan will visit only a small fraction of the table’s pages. If node→use_pagemode is true, which it is by default, the scan will perform visibility checking in a single pass for all tuples on each visited page. It might be reasonable to set this to false if the scan will select only a small fraction of the tuples on each visited page. That will result in fewer tuple visibility checks being performed, though each one will be more expensive because it will require more locking.
如果采样方法被标记为 repeatable_across_scans,它必须能够在重新扫描期间选择与最初相同的元组集,即 BeginSampleScan 的一次全新调用必须会导致选择与之前相同的元组(如果 TABLESAMPLE 参数和种子没有改变)。
If the sampling method is marked repeatable_across_scans, it must be able to select the same set of tuples during a rescan as it did originally, that is a fresh call of BeginSampleScan must lead to selecting the same tuples as before (if the TABLESAMPLE parameters and seed don’t change).
BlockNumber
NextSampleBlock (SampleScanState *node, BlockNumber nblocks);
返回要扫描的下一页的块号,如果没有要扫描的页面,则返回 InvalidBlockNumber。
Returns the block number of the next page to be scanned, or InvalidBlockNumber if no pages remain to be scanned.
此函数可以被省略(将指针设置为 NULL),在这种情况下,核心代码将对整个关系执行顺序扫描。这样的扫描可以使用同步扫描,这样采样方法无法假设关系页面在每次扫描中都以相同的顺序进行访问。
This function can be omitted (set the pointer to NULL), in which case the core code will perform a sequential scan of the entire relation. Such a scan can use synchronized scanning, so that the sampling method cannot assume that the relation pages are visited in the same order on each scan.
OffsetNumber
NextSampleTuple (SampleScanState *node,
BlockNumber blockno,
OffsetNumber maxoffset);
返回指定页面上要采样的下一个元组的偏移号,如果没有要采样的元组,则返回 InvalidOffsetNumber。maxoffset 是页面上使用的最大偏移号。
Returns the offset number of the next tuple to be sampled on the specified page, or InvalidOffsetNumber if no tuples remain to be sampled. maxoffset is the largest offset number in use on the page.
Note
NextSampleTuple 没有被明确告知 1 .. maxoffset 范围中的哪些偏移量实际上包含有效的元组。这通常不是问题,因为核心代码会忽略获取缺失或不可见的元组的请求;这不会导致样本有任何偏差。但是,如果需要,该函数可以使用 node→donetuples 检查它返回的元组中有多少是有效且可见的。
NextSampleTuple is not explicitly told which of the offset numbers in the range 1 .. maxoffset actually contain valid tuples. This is not normally a problem since the core code ignores requests to sample missing or invisible tuples; that should not result in any bias in the sample. However, if necessary, the function can use node→donetuples to examine how many of the tuples it returned were valid and visible.
Note
NextSampleTuple 必须 not 假设 blockno 是最近一次 NextSampleBlock 调用返回的相同页码。它是由某个先前的 NextSampleBlock 调用返回的,但内核代码允许在实际扫描页面之前调用 NextSampleBlock,以支持预取。可以假设,一旦某个给定页面的采样开始,连续的 NextSampleTuple 调用会始终引用同一页面,直到返回 InvalidOffsetNumber 。
NextSampleTuple must not assume that blockno is the same page number returned by the most recent NextSampleBlock call. It was returned by some previous NextSampleBlock call, but the core code is allowed to call NextSampleBlock in advance of actually scanning pages, so as to support prefetching. It is OK to assume that once sampling of a given page begins, successive NextSampleTuple calls all refer to the same page until InvalidOffsetNumber is returned.
void
EndSampleScan (SampleScanState *node);
结束扫描并释放资源。通常在不必释放 palloc’d 内存,但应该清理所有外部可见的资源。在不存在此类资源的常见情况下,此函数可以被省略(将指针设置为 NULL)。
End the scan and release resources. It is normally not important to release palloc’d memory, but any externally-visible resources should be cleaned up. This function can be omitted (set the pointer to NULL) in the common case where no such resources exist.