Table of Contents
PostgreSQL 支持一组实验性功能,旨在允许扩展模块向系统中添加新的扫描类型。与外部数据包装器不同,后者只负责了解如何扫描其自身的外部表,而自定义扫描提供者则可以为系统中的任何关系提供一种替代扫描方法。通常,编写自定义扫描提供者的动机,是为了能够使用某些核心系统尚不支持的优化,例如缓存或某种形式的硬件加速。本章概述如何编写新的自定义扫描提供者。
实现一种新的自定义扫描类型分为三个步骤。首先,在规划期间,需要生成表示使用所提策略进行扫描的访问路径。其次,如果规划器从这些访问路径中选出一条,作为扫描某个特定关系的最优策略,就必须将该访问路径转换为计划。最后,还必须能够执行该计划,并生成与针对同一关系的其他访问路径原本会生成的结果相同的结果。
自定义扫描提供者通常会通过设置下面这个钩子,为一个基本关系添加路径。该钩子会在核心代码已经为该关系生成它所能生成的全部访问路径之后被调用(Gather 和 Gather Merge 路径除外,这两类路径会在此次调用之后生成,以便利用该钩子添加的部分路径):
typedef void (*set_rel_pathlist_hook_type) (PlannerInfo *root,
RelOptInfo *rel,
Index rti,
RangeTblEntry *rte);
extern PGDLLIMPORT set_rel_pathlist_hook_type set_rel_pathlist_hook;
尽管这个钩子函数可以用来检查、修改或移除核心系统生成的路径,但自定义扫描提供者通常只会生成 CustomPath 对象,并使用 add_path 将其加入 rel;如果它们是部分路径,则使用 add_partial_path。自定义扫描提供者负责初始化 CustomPath 对象,其声明如下:
typedef struct CustomPath
{
Path path;
uint32 flags;
List *custom_paths;
List *custom_restrictinfo;
List *custom_private;
const CustomPathMethods *methods;
} CustomPath;
path 必须像其他任何路径一样完成初始化,包括行数估计、启动代价和总代价,以及该路径提供的排序顺序。flags 是一个位掩码,用于指明扫描提供者是否支持某些可选能力。如果自定义路径支持反向扫描,flags 就应包含 CUSTOMPATH_SUPPORT_BACKWARD_SCAN;如果支持标记和恢复,就应包含 CUSTOMPATH_SUPPORT_MARK_RESTORE;如果能够执行投影,就应包含 CUSTOMPATH_SUPPORT_PROJECTION。(如果未设置 CUSTOMPATH_SUPPORT_PROJECTION,则只会要求扫描节点生成被扫描关系的 Var;而如果设置了该标志,扫描节点就必须能够对这些 Var 上的标量表达式求值。)可选字段 custom_paths 是该自定义路径节点所使用的 Path 节点列表;规划器会把它们转换成 Plan 节点。如下文所述,也可以为连接关系创建自定义路径;在这种情况下,应使用 custom_restrictinfo 保存要应用到该自定义路径所替代连接上的连接子句集合。否则它应为 NIL。custom_private 可用于存储自定义路径的私有数据。私有数据应以 nodeToString 能够处理的形式存储,这样尝试打印该自定义路径的调试例程才能按预期工作。methods 必须指向一个实现了所需自定义路径方法的对象(通常是静态分配的),下文会进一步说明。
自定义扫描提供者也可以提供连接路径。与基本关系一样,这种路径也必须产生与它所替代连接通常会产生的相同输出。为此,连接提供者应设置下列钩子,并在该钩子函数内部为连接关系创建 CustomPath 路径。
typedef void (*set_join_pathlist_hook_type) (PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
JoinType jointype,
JoinPathExtraData *extra);
extern PGDLLIMPORT set_join_pathlist_hook_type set_join_pathlist_hook;
对于同一个连接关系,这个钩子会针对不同的内外关系组合重复调用;如何尽量减少重复工作,是该钩子的职责。
还要注意,应用到该连接上的连接子句集合会以 extra->restrictlist 的形式传入,并且会随着内外关系组合的不同而变化。针对 joinrel 生成的 CustomPath 路径必须包含它所使用的那组连接子句;如果规划器将该路径选作 joinrel 的最佳路径,就会使用这些信息把该 CustomPath 路径转换成计划。
Plan *(*PlanCustomPath) (PlannerInfo *root,
RelOptInfo *rel,
CustomPath *best_path,
List *tlist,
List *clauses,
List *custom_plans);
将自定义路径转换为完成的计划。返回值通常是一个 CustomScan 对象,回调函数必须负责分配并初始化该对象。详见 Section 59.2。
List *(*ReparameterizeCustomPathByChild) (PlannerInfo *root,
List *custom_private,
RelOptInfo *child_rel);
在把由给定子关系 child_rel 的最顶层父关系参数化的路径,转换为由该子关系参数化的路径时,会调用此回调。该回调用于重新参数化保存在 CustomPath 的给定 custom_private 成员中的任何路径,或者转换其中保存的任何表达式节点。该回调可根据需要使用 reparameterize_path_by_child、adjust_appendrel_attrs 或 adjust_appendrel_attrs_multilevel。
如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。