PostgreSQL 9.3.4 文档 | ||||
---|---|---|---|---|
Prev | Up | Chapter 52. 编写一个外部数据包装器 | Next |
FDW处理器函数返回一个palloc过的FdwRoutine结构,它包含下文描述的回调函数的指针。扫描相关的函数是必需的,剩下的是可选的。
FdwRoutine结构类型被声明在src/include/foreign/fdwapi.h中,可以查看它来获得额外的信息。
void GetForeignRelSize (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid);
获取一个外部表的关系尺寸估计。在对一个扫描外部表的查询进行规划的开头将调用该函数。root是规划器的关于该查询的全局信息;baserel是规划器的关于该表的信息;foreigntableid是外部表在pg_class中的 OID (foreigntableid可以从规划器的数据结构中获得,但是为了减少工作量,这里直接显式地将它传递给函数)。
这个函数应该更新baserel->rows为表扫描根据限制条件完成了过滤后将返回的预期行数。baserel->rows的初始值只是一个常数的默认估计值,应该尽可能把它替换掉。如果该函数能够计算出一个平均结果行宽度的更好的估计值,该函数也可能选择更新baserel->width。
更多信息请见Section 52.4。
void GetForeignPaths (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid);
为一个外部表上的扫描创建可能的访问路径。这个函数在查询规划过程中被调用。参数和GetForeignRelSize
相同,后者已经被调用过了。
这个函数必须为外部表上的扫描生成至少一个访问路径(ForeignPath节点),并且必须调用add_path
把每一个这样的路径加入到baserel->pathlist中。我们推荐使用create_foreignscan_path
来建立ForeignPath节点。该函数可以生成多个访问路径,例如一个具有合法pathkeys的路径表示一个预排序好的结果。每一个反问路径必须包含代价估计,并且能包含任何FDW的私有信息,这种信息被用来标识想要使用的指定扫描方法。
更多信息请见Section 52.4。
ForeignScan * GetForeignPlan (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid, ForeignPath *best_path, List *tlist, List *scan_clauses);
从选择的外部访问路径创建一个ForeignScan计划节点。这个函数在查询规划的末尾被调用。参数和GetForeignRelSize
的一样,外加选中的ForeignPath(在前面由GetForeignPaths
产生)、被计划节点发出的目标列表以及计划节点强制的限制子句。
这个函数必须创建并返回一个ForeignScan计划节点,我们对剑使用make_foreignscan
来建立ForeignScan节点。
更多信息见Section 52.4。
void BeginForeignScan (ForeignScanState *node, int eflags);
开始执行一个外部扫描。这个函数在执行器启动阶段被调用。它应该执行任何在扫描能够开始之前需要完成的初始化工作,但是并不开始执行真正的扫描(会在第一次调用IterateForeignScan
时完成)。ForeignScanState节点已经被创建好了,但是它的fdw_state域仍然为 NULL。关于要被扫描的表的信息可以通过ForeignScanState节点访问(特殊地,从底层的ForeignScan计划节点,它包含任何由GetForeignPlan
提供的FDW私有信息)。eflags包含描述执行器对该计划节点操作模式的标志位。
注意当(eflags & EXEC_FLAG_EXPLAIN_ONLY)为真时,这个函数不应该执行任何外部可见的动作;它应当只做最少的事情来创建对ExplainForeignScan
和EndForeignScan
有效的节点状态。
TupleTableSlot * IterateForeignScan (ForeignScanState *node);
从外部源获得一行,将它放在一个元组表槽中返回(节点的ScanTupleSlot应当被用于此目的)。如果没有更多的行可用则返回 NULL。元组表槽设施允许一个物理的或者虚拟的元组被返回;在大部分情况下出于性能的考虑会倾向于选择后者。注意这是在一个短期存在的内存上下文中被调用的,该内存上下文会在调用之间被重置。如果你需要长期存在的存储,请在BeginForeignScan
中创建内存上下文,或者使用节点的EState中的es_query_cxt。
被返回的行必须匹配被扫描的外部表的列签名。如果你选择将那些不需要的列优化掉,你应该在那些列位置上插入空值。
注意PostgreSQL的执行器并不关心被返回的行是否违反任何定义在外部表列上的NOT NULL约束 — 但是规划器却关心,并且如果NULL值出现在一个被声明为不可能包含它们的列中,优化器可能会不正确地优化查询。如果用户已经声明了不会出现空值时遇到一个NULL值,可能抛出一个错误更为合适(就像你在数据类型失配的情况下你需要做的一样)。
void ReScanForeignScan (ForeignScanState *node);
从头开始重启一个扫描。注意扫描所依赖的任何参数可能已经改变了值,因此新扫描不一定会返回完全相同的行。
void EndForeignScan (ForeignScanState *node);
结束扫描并释放资源。通常释放palloc过的内存并不重要,但是打开的文件和到远程服务器的连接等应该被清理。
如果一个FDW支持可写的外部表,根据FDW的需要和功能它应该提供某些或者全部下列回调函数:
void AddForeignUpdateTargets (Query *parsetree, RangeTblEntry *target_rte, Relation target_relation);
UPDATE和DELETE操作是在之前由表扫描函数取出的行上被执行的。FDW可能需要额外的信息(例如一个行ID或主键列的值)来保证它能够找到要更新或删除的准确行。要支持这些要求,这个函数可以项列列表中增加额外的隐藏或"junk"的目标列,它们在一个UPDATE或DELETE期间会被从外部表中获取。
要做到这一点,向parsetree->targetList中增加TargetEntry项,它们包含要被获取的额外值的表达式。每一个这样的项必须被标记为resjunk = true,并且必须有一个可区分的resname用于在执行期间标识它。请避免使用匹配ctidN或wholerowN的名字,因为核心系统可能会生成使用这些名字的junk列。
这个函数在重写器中被调用,而不是在规划器中,因此可用的信息与在规划例程中的有点区别。parsetree是UPDATE或DELETE命令的分析树,而target_rte和target_relation描述目标外部表。
如果AddForeignUpdateTargets
指针被设置为NULL,则不会有额外的目标表达式被加入(这将使得我们不可能实现DELETE操作,而UPDATE则还有可能是可行的,前提是FDW依赖一个未改变的主键来标识行)。
List * PlanForeignModify (PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index);
执行外部表上插入、更新或删除所需的任何附加规划动作。这个函数生成FDW私有信息,该信息将被附加到执行该更新动作的ModifyTable计划节点。这个私有信息的形式必须是一个List,并将会在执行阶段被传递给BeginForeignModify
。
root是规划器关于该查询的全局信息。 plan是ModifyTable计划节点,它除了fdwPrivLists域之外是完整的。 resultRelation通过目标外部表的范围表索引来标识它。subplan_index标识这是ModifyTable计划节点的哪个目标,从零开始计数;如果你希望索引到plan->plans或其他plan节点的子结构中,请使用它。
更多信息见Section 52.4。
如果PlanForeignModify
指针被设置为NULL,则不会有额外的计划时动作被执行,并且传递给BeginForeignModify
的fdw_private列表也将为 NIL。
void BeginForeignModify (ModifyTableState *mtstate, ResultRelInfo *rinfo, List *fdw_private, int subplan_index, int eflags);
开始执行一个外部表修改操作。这个例程在执行器启动期间被调用。它应该执行任何先于实际表修改的初始化工作。随后,ExecForeignInsert
、ExecForeignUpdate
或ExecForeignDelete
将被为每一个将被插入、更新或删除的元组调用。
mtstate是要被执行的ModifyTable计划节点的状态信息;通过这个结构可以得到关于规划和执行阶段的全局数据。rinfo是描述目标外部表的ResultRelInfo结构(ResultRelInfo的ri_FdwState域用于FDW来存储它在此操作中需要的任何私有状态)。fdw_private包含PlanForeignModify
生成的私有数据。subplan_index标识这是ModifyTable计划节点的哪个目标。eflags包含描述执行器对该计划节点操作模式的标志位。
注意当(eflags & EXEC_FLAG_EXPLAIN_ONLY)为真,这个函数不应执行任何外部可见的动作;它只应该做最少的工作来创建ExplainForeignModify
和EndForeignModify
可用的节点状态。
如果BeginForeignModify
指针被设置为NULL,在执行器启动期间将不会采取任何动作。
TupleTableSlot * ExecForeignInsert (EState *estate, ResultRelInfo *rinfo, TupleTableSlot *slot, TupleTableSlot *planSlot);
插入一个元组到外部表。estate是查询的全局执行状态。rinfo是描述目标外部表的ResultRelInfo结构。slot包含要被插入的元组;它将匹配外部表的行类型定义。planSlot包含由ModifyTable计划节点的子计划生成的元组;它与slot不同,它可能包含额外的"junk"列(INSERT情况通常不关心planSlot,但是为了完整性还是在这里提供它)。
返回值可以是一个包含实际被插入的数据的槽(这可能会和所提供的数据不同,例如一个触发器动作的结果),或者为 NULL 表示实际没有插入行(还是触发器的结果)。被传入的slot可以被重用于这个目的。
在返回槽中的数据只有在INSERT查询具有一个RETURNING子句时才被使用。因此,FDW应该选择优化成根据RETURNING子句的内容返回某些或全部列。但是,某些槽必须被返回来指示成功,或者查询报告的行计数将会是错误的。
如果ExecForeignInsert
指针被设置为NULL,尝试向外部表插入将会失败并报告一个错误消息。
TupleTableSlot * ExecForeignUpdate (EState *estate, ResultRelInfo *rinfo, TupleTableSlot *slot, TupleTableSlot *planSlot);
更新外部表中的一个元组。estate是查询的全局执行状态。rinfo是描述目标外部表的ResultRelInfo结构。slot包含元组的新数据;它将匹配外部表的行类型定义。planSlot包含由ModifyTable计划节点的子计划生成的元组;它与slot不同,它可能包含额外的"junk"列(INSERT情况通常不关心planSlot,但是为了完整性还是在这里提供它)。特殊地,任何AddForeignUpdateTargets
所要求的junk列在这个槽中都是有效的。
返回值可以是一个包含实际被更新的数据的槽(这可能会和所提供的数据不同,例如一个触发器动作的结果),或者为 NULL 表示实际没有更新行(还是触发器的结果)。被传入的slot可以被重用于这个目的。
在返回槽中的数据只有在UPDATE查询具有一个RETURNING子句时才被使用。因此,FDW应该选择优化成根据RETURNING子句的内容返回某些或全部列。但是,某些槽必须被返回来指示成功,或者查询报告的行计数将会是错误的。
如果ExecForeignUpdate
指针被设置为NULL,尝试更新外部表将会失败并报告一个错误消息。
TupleTableSlot * ExecForeignDelete (EState *estate, ResultRelInfo *rinfo, TupleTableSlot *slot, TupleTableSlot *planSlot);
从外部表删除一个元组。estate是查询的全局执行状态。rinfo是描述目标外部表的ResultRelInfo结构。slot在调用时不包含任何有用的东西,但是可以被用于保持被返回的元组。planSlot包含由ModifyTable计划节点的子计划生成的元组;特殊地,它将携带AddForeignUpdateTargets
所要求的任意垃圾列。垃圾列被用来标识要被删除的元组。
返回值可以是一个包含实际被删除的数据的槽(这可能会和所提供的数据不同,例如一个触发器动作的结果),或者为 NULL 表示实际没有删除行(还是触发器的结果)。被传入的slot可以被重用于这个目的。
在返回槽中的数据只有在DELETE查询具有一个RETURNING子句时才被使用。因此,FDW应该选择优化成根据RETURNING子句的内容返回某些或全部列。但是,某些槽必须被返回来指示成功,或者查询报告的行计数将会是错误的。
如果ExecForeignDelete
指针被设置为NULL,尝试从外部表中删除将会失败并报告一个错误消息。
void EndForeignModify (EState *estate, ResultRelInfo *rinfo);
结束表更新并释放资源。通常释放palloc的内存并不重要,但是打开的文件和到远程服务器的连接等应当被清除。
如果EndForeignModify
指针被设置为NULL,在执行器关闭期间不会采取任何动作。
int IsForeignRelUpdatable (Relation rel);
报告指定的外部表支持哪些更新操作。返回值应该是一个规则事件编号的位掩码,它指示了哪些操作被外部表支持,它使用CmdType枚举,及: (1 << CMD_UPDATE) = 4表示UPDATE、 (1 << CMD_INSERT) = 8表示INSERT以及 (1 << CMD_DELETE) = 16表示DELETE。
如果IsForeignRelUpdatable
指针被设置为NULL,而FDW提供了ExecForeignInsert
、ExecForeignUpdate
或ExecForeignDelete
,则外部表分别被假定为可插入、可更新或可删除。只有在FDW支持某些表是可更新的而某些不是可更新的时候,才需要这个函数(即便如此,也允许在执行例程中抛出一个错误而不是在这个函数中检查。但是,这个函数被用来决定显示在information_schema视图中的可更新性)。
void ExplainForeignScan (ForeignScanState *node, ExplainState *es);
为一个外部表扫描打印额外的EXPLAIN输出。这个函数可以调用ExplainPropertyText
和相关函数来向EXPLAIN输出中增加域。es中的标志域可以被用来决定什么将被打印,并且ForeignScanState节点的状态可以被检查来为EXPLAIN ANALYZE提供运行时统计数据。
如果ExplainForeignScan
指针被设置为NULL,在EXPLAIN期间不会打印任何额外的信息。
void ExplainForeignModify (ModifyTableState *mtstate, ResultRelInfo *rinfo, List *fdw_private, int subplan_index, struct ExplainState *es);
为一个外部表更新打印额外的EXPLAIN输出。这个函数可以调用ExplainPropertyText
和相关函数来向EXPLAIN输出中增加域。es中的标志域可以被用来决定什么将被打印,并且ModifyTableState节点的状态可以被检查来为EXPLAIN ANALYZE提供运行时统计数据。前四个参数和BeginForeignModify
相同。
如果ExplainForeignModify
指针被设置为NULL,在EXPLAIN期间不会打印任何额外的信息。
bool AnalyzeForeignTable (Relation relation, AcquireSampleRowsFunc *func, BlockNumber *totalpages);
当ANALYZE被执行在一个外部表上时会调用这个函数。如果FDW可以为这个外部表收集统计信息,它会返回true并提供一个函数指针,该函数将将从func中的表上收集采样行,外加totalpages中页面中的表尺寸估计值。否则,返回false。
如果FDW不支持为任何表收集统计信息,AnalyzeForeignTable
指针可以被设置为NULL。
如果提供,采样收集函数必须具有签名
int AcquireSampleRowsFunc (Relation relation, int elevel, HeapTuple *rows, int targrows, double *totalrows, double *totaldeadrows);
应该从该表上收集最多targrows行的一个随机采样并将它存放到调用者提供的rows数组中。实际被收集的行的数量必须被返回。另外,将表中有效行和死亡行的总数存储到输出参数totalrows和totaldeadrows中(如果FDW没有死亡行的概念,将totaldeadrows设置为 0 )。