| PostgreSQL 9.4.4 中文手册 | |||
|---|---|---|---|
| 上一页 | 上一级 | 章 53. 写一个外部数据封装器 | 下一页 |
FDW处理函数返回包含指向下面描述的回调函数指针的palloc'd FdwRoutine结构。 扫描相关函数是必须的,其余的是可选的。
在src/include/foreign/fdwapi.h中声明FdwRoutine结构类型, 参阅额外详情。
void
GetForeignRelSize (PlannerInfo *root,
RelOptInfo *baserel,
Oid foreigntableid);获得评估外表关系大小。这就是所谓的开始扫描外表的查询规划。 root是关于查询的规划器的全局信息; baserel是关于这个表的规划器信息; foreigntableid是外表的pg_class OID。 (foreigntableid可以从规划器数据结构中获得, 但是它明确被传递用来节省力气。)
在说明限制资格测试执行过滤之后, 该函数应该更新baserel->rows为通过表扫描返回的行期望数。 baserel->rows的初始值仅仅是恒定缺省估计, 如果可能的话,这应该被替换。如果它可以对平均结果行宽度计算出更好的评估,那么 该函数可能也会选择更新baserel->width。
参阅第 53.4 节可以获取额外信息。
void
GetForeignPaths (PlannerInfo *root,
RelOptInfo *baserel,
Oid foreigntableid);
创建外表扫描的可能访问路径。
这就是所谓的查询规划。
它被调用的参数和GetForeignRelSize相同。
该函数必须为外表扫描产生至少1个访问路径(ForeignPath节点)而且
必须调用add_path添加每个这样的路径到baserel->pathlist中。
推荐使用create_foreignscan_path建立ForeignPath节点。
该函数可以生成多个访问路径,比如具有有效pathkeys表示预排序结果路径。
每个访问路径必须包含成本估计,并且包含需要标识具体预期扫描方法的任何FDW-私有信息。
参阅第 53.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节点。
参阅第 53.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。元组表槽基础设施允许返回物理或者虚拟元组。
在大多数情况下后者选择从性能角度更可取。
注意这被称为在调用期间被重置的短暂内存语境。如果
你需要较长时间存储,或者使用节点的EState的es_query_cxt,
那么在BeginForeignScan中创建内存上下文。
返回的行必须匹配扫描外表的列标志。如果你选择优化掉不需要的列,那么 你应该在那些列位置插入空值。
注意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中从外表中检索列表中的目标列。
要做到这一点,添加TargetEntry项到 parsetree->targetList,包含读取的额外值的表达式。 每个这样的项必须被标记resjunk = true, 并且有一个不同的resname在执行期间标识它。 避免使用匹配ctidN、wholerow 或者wholerowN的名称,正如核心系统可以 产生这些名字的垃圾列。
在改写过程中调用该函数,而不是规划器,因此该可用信息不同于可用的规划程序。 当target_rte和 target_relation描述目标外表时,parsetree是UPDATE或者 DELETE命令的解析树。
如果AddForeignUpdateTargets指针被设置为NULL,
那么没有额外目标表达式被添加。
(这将不可能实现DELETE操作,尽管UPDATE可能仍然是可行的,
如果FDW依赖于一个标识行的未改变主键)。
List *
PlanForeignModify (PlannerInfo *root,
ModifyTable *plan,
Index resultRelation,
int subplan_index);
执行任何额外规划操作需要插入,更新或者删除外表。
该函数产生附属于执行更新操作的ModifyTable规划节点
的FDW-私有信息。这个私有信息必须有List形式,并且
在执行阶段将转交给BeginForeignModify。
root是关于查询的规划器的全局信息。 plan是ModifyTable规划节点, 除了fdwPrivLists字段外它是完整的。 resultRelation通过射程表索引识别目标外表。 subplan_index识别从零开始计算的ModifyTable规划节点是哪个目标; 如果你想要索引plan->plans或者其他plan节点的子结构,那么使用它。
参阅第 53.4 节获取更多额外信息。
如果PlanForeignModify指针被设置为NULL,
没有采取额外规划时间操作,并且fdw_private列表转交给
BeginForeignModify为零。
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包含要插入的元组;它将匹配外表rowtype定义。 planSlot包含通过ModifyTable规划节点的子计划产生的元组; 它不同于可能包含额外"junk"列的slot。
返回值要么是包含实际插入的数据的槽(这可能与提供的数据不同,比如作为触发器操作结果), 如果没有行实际被插入,那么返回NULL(再次,通常作为触发器结果)。 传入的slot可以重新用于这个目的。
只有INSERT查询有RETURNING子句,或该外部表有一个 AFTER ROW触发器时,才使用返回槽中的数据。触发器请求所有的字段, 但是FDW可能选择优化返回依赖于RETURNING子句内容的一些或者全部列。 不管怎样,必须返回一些槽以表示成功,否则该查询报告的行数是错误的。
如果ExecForeignInsert指针被设置为NULL,
尝试插入外表将带有错误消息而失败。
TupleTableSlot *
ExecForeignUpdate (EState *estate,
ResultRelInfo *rinfo,
TupleTableSlot *slot,
TupleTableSlot *planSlot);
更新外表上的元组。
estate是查询的全局执行状态。rinfo是描述目标外表的
ResultRelInfo结构。slot包含元组的新数据;
它将匹配外表rowtype定义。
planSlot包含通过ModifyTable规划节点的子计划产生的元组;
它不同于可能包含额外"junk"列的slot。
尤其是,通过AddForeignUpdateTargets请求的任何垃圾列将从该槽中提供。
返回值要么是包含实际更新的行的槽(这可能与提供的数据不同,比如作为触发器操作结果), 如果没有行实际被更新,那么返回NULL(再次,通常作为触发器结果)。 传入的slot可以重新用于这个目的。
只有UPDATE查询有RETURNING子句时,或该外部表有一个 AFTER ROW触发器时,才使用返回槽中的数据。触发器请求所有的字段, 但是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子句,或该外部表有一个 AFTER ROW触发器时,才使用返回槽中的数据。触发器请求所有的字段, 但是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可以收集外表的统计,它应该返回真, 并且提供一个指针给函数,该函数从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为零。)