一个自定义扫描提供者将通过设置下面的钩子函数来为基本关系增加路径,在核心代码已经为该关系产生了完全且正确的访问路径集后,这个钩子函数将被调用。
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
中。自定义扫描提供者负责初始化CustomPath
对象,它被声明为这样:
typedef struct CustomPath { Path path; uint32 flags; List *custom_paths; List *custom_private; const CustomPathMethods *methods; } CustomPath;
path
必须像任何其他路径一样被初始化,包括行计数估计、启动和总代价以及这条路径提供的排序顺序。flags
是一个位掩码,如果该自定义路径能够支持反向扫描则它应该包括CUSTOMPATH_SUPPORT_BACKWARD_SCAN
,如果支持标记和恢复则应该包括CUSTOMPATH_SUPPORT_MARK_RESTORE
。这两种能力都是可选的。可选的custom_paths
域是由这个自定义路径节点使用的Path
节点的列表,这些将被规划器转换成Plan
节点。custom_private
可被用来存储该自定义路径的私有数据。私有数据应该被存储为能被nodeToString
处理的形式,这样尝试打印该自定义路径的调试例程才能正常地工作。methods
必须指向一个实现了所需自定义路径方法的对象(通常是静态分配的),当前只有一种方法。LibraryName
和SymbolName
域必须也被初始化,这样动态载入器才能解析它们来定位方法表。
一个自定义扫描提供者还能提供连接路径。就和基本关系一样,这样一条路径也应该产生和它将要替换的连接所产生的相同的输出。要做到这一点,连接提供程序应该设置下面的钩子函数,并且在该钩子函数里为连接关系创建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;
对于同一个连接关系,这个钩子将被反复调用,因为要对不同的内外关系组合生成路径,所以如何最小化可能的重复工作是钩子函数的责任。
Plan *(*PlanCustomPath) (PlannerInfo *root, RelOptInfo *rel, CustomPath *best_path, List *tlist, List *clauses, List *custom_plans);
将一条自定义路径转换为一个完成的计划。返回值通常将是一个CustomScan
对象,回调函数必须负责分配并且初始化这个对象。详见第 58.2 节。