57.2. 外部数据包装程序回调例程

FDW 处理程序函数返回一个 palloc 的FdwRoutine结构,该结构包含指向下面描述的回调函数的指针。与扫描相关的功能是必需的,其余功能是可选的。

FdwRoutine结构类型在src/include/foreign/fdwapi.h中声明,有关其他详细信息,请参见。

57 .2.1. 扫描外表的 FDW 例程

void
GetForeignRelSize(PlannerInfo *root,
                  RelOptInfo *baserel,
                  Oid foreigntableid);

获取外部表的关系大小估计。在计划扫描外部表的查询开始时调用此方法。 root是计划者有关查询的全局信息; baserel是计划者关于此表的信息; foreigntableid是外部表的pg_class OID。 (foreigntableid可以从计划程序的数据结构中获取,但是为了节省工作量而明确传递了它.)

考虑到限制质量完成的过滤后,此函数应将baserel->rows更新为表扫描返回的预期行数。 baserel->rows的初始值只是一个恒定的默认估计值,应尽可能将其替换。如果该函数可以计算平均结果行宽度的更好估计,则该函数还可以选择更新baserel->width

有关其他信息,请参见Section 57.4

void
GetForeignPaths(PlannerInfo *root,
                RelOptInfo *baserel,
                Oid foreigntableid);

创建用于在外部表上进行扫描的可能的访问路径。这是在查询计划期间调用的。参数与已调用的GetForeignRelSize相同。

此函数必须为在外部表上进行扫描至少生成一个访问路径(ForeignPath节点),并且必须调用add_path以将每个这样的路径添加到baserel->pathlist。建议使用create_foreignscan_path构建ForeignPath节点。该函数可以生成多个访问路径,例如,具有有效pathkeys表示预排序结果的路径。每个访问路径必须包含成本估算,并且可以包含标识预期的特定扫描方法所需的任何 FDW 专用信息。

有关其他信息,请参见Section 57.4

ForeignScan *
GetForeignPlan(PlannerInfo *root,
               RelOptInfo *baserel,
               Oid foreigntableid,
               ForeignPath *best_path,
               List *tlist,
               List *scan_clauses,
               Plan *outer_plan);

从选定的外部访问路径创建一个ForeignScan计划节点。这是在查询计划结束时调用的。参数与GetForeignRelSize相同,再加上所选的ForeignPath(先前由GetForeignPathsGetForeignJoinPathsGetForeignUpperPaths生成),计划节点要运行的目标列表,计划节点要实施的限制子句以及的外部子计划ForeignScan,用于RecheckForeignScan执行的重新检查。 (如果路径用于联接而不是基本关系,则foreigntableidInvalidOid.)

此函数必须创建并返回ForeignScan计划节点;建议使用make_foreignscan构建ForeignScan节点。

有关其他信息,请参见Section 57.4

void
BeginForeignScan(ForeignScanState *node,
                 int eflags);

开始执行外部扫描。在执行程序启动期间调用。它应执行扫描开始之前所需的任何初始化,但不开始执行实际扫描(应在首次调用IterateForeignScan时完成)。 ForeignScanState节点已经创建,但是其fdw_state字段仍然为 NULL。可通过ForeignScanState节点访问有关要扫描的表的信息(特别是从基础ForeignScan计划节点,其中包含GetForeignPlan提供的任何 FDW 专用信息)。 eflags包含标志位,这些标志位描述此计划节点的执行者的操作模式。

请注意,当(eflags & EXEC_FLAG_EXPLAIN_ONLY)为 true 时,此函数不应执行任何外部可见的操作;它仅应执行使节点状态对ExplainForeignScanEndForeignScan有效的最低要求。

TupleTableSlot *
IterateForeignScan(ForeignScanState *node);

从外部源中获取一行,并将其返回到 Tuples 表插槽中(为此目的,应使用节点的ScanTupleSlot)。如果没有更多行可用,则返回 NULL。Tuples 表插槽基础结构允许返回物理或虚拟 Tuples。从性能的角度来看,在大多数情况下,后一种选择是可取的。请注意,这是在短暂的内存上下文中调用的,它将在调用之间重置。如果需要更长寿命的存储,请在BeginForeignScan中创建一个内存上下文,或者使用节点的EStatees_query_cxt

如果提供的话,返回的行必须与fdw_scan_tlist目标列表匹配,否则它们必须与要扫描的外部表的行类型匹配。如果选择优化不需要的获取列,则应在这些列位置插入空值,否则生成一个fdw_scan_tlist列表,其中省略了那些列。

请注意,PostgreSQL 的执行程序不在乎返回的行是否违反了外部表上定义的任何约束,但计划者确实在意,并且如果外部表中可见的行不满足声明的约束,则可能会错误地优化查询。 。如果在用户声明约束应为 true 时违反了约束,则可能会引发错误(就像在数据类型不匹配的情况下一样)。

void
ReScanForeignScan(ForeignScanState *node);

从头开始重新扫描。请注意,扫描所依赖的任何参数都可能具有更改的值,因此新扫描不一定返回完全相同的行。

void
EndForeignScan(ForeignScanState *node);

结束扫描并释放资源。释放 palloc 的内存通常并不重要,但是例如,应清除打开的文件和与远程服务器的连接。

57 .2.2. 扫描国外联接的 FDW 例程

如果 FDW 支持远程执行外部联接(而不是通过获取两个表的数据并在本地进行联接),则它应提供以下回调函数:

void
GetForeignJoinPaths(PlannerInfo *root,
                    RelOptInfo *joinrel,
                    RelOptInfo *outerrel,
                    RelOptInfo *innerrel,
                    JoinType jointype,
                    JoinPathExtraData *extra);

为两个(或更多)都属于同一外部服务器的外部表的联接创建可能的访问路径。在查询计划期间将调用此可选功能。与GetForeignPaths一样,此函数应为提供的joinrel生成ForeignPath路径,并调用add_path将这些路径添加到考虑用于联接的路径集中。但是与GetForeignPaths不同,此函数不必成功创建至少一条路径,因为涉及本地连接的路径始终是可能的。

注意,对于相同的联接关系,内部和外部关系的不同组合,将重复调用此函数; FDW 有责任尽量减少重复工作。

如果为连接选择了ForeignPath路径,它将代表整个连接过程;不会使用为组件表和辅助联接生成的路径。连接路径的后续处理与扫描单个外部表的路径的处理一样多。一个区别是,结果ForeignScan计划节点的scanrelid应该设置为零,因为它所表示的关系并不单一。而是,ForeignScan节点的fs_relids字段表示已连接的一组关系。 (后一个字段由核心计划程序代码自动设置,而不必由 FDW 填充.)另一个区别是,因为无法从系统目录中找到远程联接的列列表,所以 FDW 必须填充fdw_scan_tlist。带有适当的TargetEntry节点列表,代表它将在运行时在返回的 Tuples 中提供的列集。

有关其他信息,请参见Section 57.4

57 .2.3. FDW 例程,用于计划扫描后/合并处理

如果 FDW 支持执行远程扫描后/加入处理(例如远程聚合),则它应提供以下回调函数:

void
GetForeignUpperPaths(PlannerInfo *root,
                     UpperRelationKind stage,
                     RelOptInfo *input_rel,
                     RelOptInfo *output_rel,
                     void *extra);

为“上层关系”处理创建可能的访问路径,这是所有扫描后/联接查询处理(例如聚合,窗口函数,排序和表更新)的计划者术语。在查询计划期间将调用此可选功能。当前,仅当查询中涉及的所有基本关系都属于同一 FDW 时才调用它。对于 FDW 知道如何远程执行的任何扫描后/合并处理,此函数应生成ForeignPath路径,并调用add_path将这些路径添加到指定的上级关系中。与GetForeignJoinPaths一样,此函数不必成功创建任何路径,因为涉及本地处理的路径始终是可能的。

stage参数标识当前正在考虑哪个扫描后/加入步骤。 output_rel是应该接收表示该步骤的计算的路径的上级关系,input_rel是表示此步骤的 Importing 的关系。 extra参数提供了其他详细信息,当前仅针对UPPERREL_PARTIAL_GROUP_AGGUPPERREL_GROUP_AGG进行设置,在这种情况下,它指向GroupPathExtraData结构。 (请注意,添加到output_relForeignPath路径通常不会直接对input_rel的路径产生任何依赖关系,因为它们的处理应在外部完成.但是,检查先前为上一个处理步骤生成的路径对于避免多余的规划工作很有用.)

有关其他信息,请参见Section 57.4

57 .2.4. 用于更新外表的 FDW 例程

如果 FDW 支持可写的外部表,则它应根据 FDW 的需求和功能提供以下部分或全部回调函数:

void
AddForeignUpdateTargets(Query *parsetree,
                        RangeTblEntry *target_rte,
                        Relation target_relation);

对先前由表扫描功能获取的行执行UPDATEDELETE操作。 FDW 可能需要其他信息,例如行 ID 或主键列的值,以确保它可以标识要更新或删除的确切行。为了支持这一点,此函数可以在UPDATEDELETE期间向要从外部表检索的列的列表中添加额外的隐藏或“垃圾”目标列。

为此,请将TargetEntry项添加到parsetree->targetList中,其中包含要获取的额外值的表达式。每个此类条目都必须标记为resjunk = true,并且必须具有不同的resname,以便在执行时对其进行标识。避免使用与ctidNwholerowwholerowN匹配的名称,因为核心系统会生成这些名称的垃圾列。如果多余的表达式比简单的 Var 更为复杂,则必须将它们通过eval_const_expressions运行,然后再将它们添加到目标列表中。

尽管在计划期间会调用此函数,但是所提供的信息与其他计划例程可用的信息有所不同。 parsetreeUPDATEDELETE命令的分析树,而target_rtetarget_relation描述目标外部表。

如果AddForeignUpdateTargets指针设置为NULL,则不会添加任何额外的目标表达式。 (这将使无法实现DELETE操作,但是如果 FDW 依靠不变的主键来标识行,则UPDATE仍然可行)。

List *
PlanForeignModify(PlannerInfo *root,
                  ModifyTable *plan,
                  Index resultRelation,
                  int subplan_index);

执行在外部表上插入,更新或删除所需的任何其他计划操作。此函数生成 FDW 专用信息,该信息将附加到执行更新操作的ModifyTable计划节点。此私人信息的格式必须为List,并将在执行阶段传递给BeginForeignModify

root是计划者有关查询的全局信息。 planModifyTable计划节点,除fdwPrivLists字段外,该节点已完成。 resultRelation通过其范围表索引标识目标外部表。 subplan_index从零开始标识这是ModifyTable计划节点的哪个目标;如果要索引plan->plansplan节点的其他子结构,请使用此选项。

有关其他信息,请参见Section 57.4

如果PlanForeignModify指针设置为NULL,则不会执行其他计划时间操作,并且传递给BeginForeignModifyfdw_private列表将为 NIL。

void
BeginForeignModify(ModifyTableState *mtstate,
                   ResultRelInfo *rinfo,
                   List *fdw_private,
                   int subplan_index,
                   int eflags);

开始执行外部表修改操作。在执行程序启动期间调用此例程。它应该执行实际表修改之前所需的任何初始化。随后,将为要插入,更新或删除的每个 Tuples 调用ExecForeignInsertExecForeignUpdateExecForeignDelete

mtstate是正在执行的ModifyTable计划节点的整体状态;通过该结构可以获得有关计划和执行状态的全局数据。 rinfo是描述目标外部表的ResultRelInfo结构。 (ResultRelInfori_FdwState字段可用于 FDW 存储此操作所需的任何私有状态.)fdw_private包含PlanForeignModify生成的私有数据(如果有)。 subplan_index标识这是ModifyTable计划节点的目标。 eflags包含标志位,这些标志位描述此计划节点的执行者的操作模式。

请注意,当(eflags & EXEC_FLAG_EXPLAIN_ONLY)为 true 时,此函数不应执行任何外部可见的操作;它仅应执行使节点状态对ExplainForeignModifyEndForeignModify有效的最低要求。

如果BeginForeignModify指针设置为NULL,则在执行程序启动期间不执行任何操作。

TupleTableSlot *
ExecForeignInsert(EState *estate,
                  ResultRelInfo *rinfo,
                  TupleTableSlot *slot,
                  TupleTableSlot *planSlot);

将一个 Tuples 插入到外部表中。 estate是查询的全局执行状态。 rinfo是描述目标外部表的ResultRelInfo结构。 slot包含要插入的 Tuples;它将匹配外部表的行类型定义。 planSlot包含由ModifyTable计划节点的子计划生成的 Tuples;它与slot的不同之处在于可能包含其他“垃圾”列。 (对于INSERT个案例,通常对planSlot兴趣不大,但出于完整性考虑而提供)。

返回值是一个包含实际插入数据的插槽(例如,由于触发操作,它可能与所提供的数据有所不同);如果未实际插入任何行(同样,通常是由于触发的结果),则返回 NULL )。传入的slot可以重新用于此目的。

仅当INSERT查询具有RETURNING子句或外部表具有AFTER ROW触发器时,才使用返回的插槽中的数据。触发器需要所有列,但是 FDW 可以根据RETURNING子句的内容来选择优化返回部分或全部列。无论如何,必须返回一些插槽以指示成功,否则查询的报告行数将是错误的。

如果ExecForeignInsert指针设置为NULL,则尝试插入外部表的操作将失败,并显示一条错误消息。

请注意,在将路由的 Tuples 插入外部表分区或在外部表上执行COPY FROM时,也会调用此函数,在这种情况下,该函数的调用方式与在INSERT情况下的调用方式不同。请参阅下面描述的允许 FDW 支持的回调函数。

TupleTableSlot *
ExecForeignUpdate(EState *estate,
                  ResultRelInfo *rinfo,
                  TupleTableSlot *slot,
                  TupleTableSlot *planSlot);

更新外部表中的一个 Tuples。 estate是查询的全局执行状态。 rinfo是描述目标外部表的ResultRelInfo结构。 slot包含 Tuples 的新数据;它将匹配外部表的行类型定义。 planSlot包含由ModifyTable计划节点的子计划生成的 Tuples;它与slot的不同之处在于可能包含其他“垃圾”列。特别是,AddForeignUpdateTargets请求的所有垃圾列都可以从该插槽中使用。

返回值是一个包含实际更新的行的插槽(例如,由于触发操作,它可能与所提供的数据有所不同);如果没有实际更新的行(同样,通常是由于触发器)。传入的slot可以重新用于此目的。

仅当UPDATE查询具有RETURNING子句或外部表具有AFTER ROW触发器时,才使用返回的插槽中的数据。触发器需要所有列,但是 FDW 可以根据RETURNING子句的内容来选择优化返回部分或全部列。无论如何,必须返回一些插槽以指示成功,否则查询的报告行数将是错误的。

如果ExecForeignUpdate指针设置为NULL,则尝试更新外部表的操作将失败,并显示一条错误消息。

TupleTableSlot *
ExecForeignDelete(EState *estate,
                  ResultRelInfo *rinfo,
                  TupleTableSlot *slot,
                  TupleTableSlot *planSlot);

从外部表中删除一个 Tuples。 estate是查询的全局执行状态。 rinfo是描述目标外部表的ResultRelInfo结构。 slot在调用时不包含任何有用的内容,但可用于保存返回的 Tuples。 planSlot包含由ModifyTable计划节点的子计划生成的 Tuples;特别是它将包含AddForeignUpdateTargets请求的所有垃圾列。垃圾列必须用于标识要删除的 Tuples。

返回值是一个包含已删除行的插槽,如果没有行被删除(通常是由于触发器的结果),则返回 NULL。传入的slot可用于保存要返回的 Tuples。

仅当DELETE查询具有RETURNING子句或外部表具有AFTER ROW触发器时,才使用返回的插槽中的数据。触发器需要所有列,但是 FDW 可以根据RETURNING子句的内容来选择优化返回部分或全部列。无论如何,必须返回一些插槽以指示成功,否则查询的报告行数将是错误的。

如果ExecForeignDelete指针设置为NULL,则尝试从外部表中删除将失败,并显示一条错误消息。

void
EndForeignModify(EState *estate,
                 ResultRelInfo *rinfo);

结束表更新和释放资源。释放 palloc 的内存通常并不重要,但是例如,应清除打开的文件和与远程服务器的连接。

如果EndForeignModify指针设置为NULL,则在执行程序关闭期间不执行任何操作。

通过INSERTCOPY FROM插入分区表中的 Tuples 被路由到分区。如果 FDW 支持可路由的外部表分区,则它还应提供以下回调函数。在外部表上执行COPY FROM时,也会调用这些函数。

void
BeginForeignInsert(ModifyTableState *mtstate,
                   ResultRelInfo *rinfo);

开始在外部表上执行插入操作。在这两种情况下,当它都是为 Tuples 路由选择的分区以及在COPY FROM命令中指定的目标时,都会在将第一个 Tuples 插入外部表之前立即调用此例程。它应在实际插入之前执行所需的任何初始化。随后,将为要插入到外部表中的每个 Tuples 调用ExecForeignInsert

mtstate是正在执行的ModifyTable计划节点的整体状态;通过该结构可以获得有关计划和执行状态的全局数据。 rinfo是描述目标外部表的ResultRelInfo结构。 (ResultRelInfori_FdwState字段可用于 FDW 存储此操作所需的任何私有状态.)

当通过COPY FROM命令调用时,不提供mtstate中与计划相关的全局数据,并且随后为每个插入的 Tuples 调用的ExecForeignInsertplanSlot参数为NULL,无论外部表是为 Tuples 路由选择的分区还是指定的目标在命令中。

如果BeginForeignInsert指针设置为NULL,则不执行任何初始化操作。

请注意,如果 FDW 不支持可路由的外部表分区和/或在外部表上执行COPY FROM,则此函数或随后调用的ExecForeignInsert必须根据需要抛出错误。

void
EndForeignInsert(EState *estate,
                 ResultRelInfo *rinfo);

结束插入操作并释放资源。释放 palloc 的内存通常并不重要,但是例如,应清除打开的文件和与远程服务器的连接。

如果EndForeignInsert指针设置为NULL,则不执行任何终止操作。

int
IsForeignRelUpdatable(Relation rel);

报告指定的外部表支持哪些更新操作。返回值应该是规则事件编号的位掩码,使用CmdType枚举指示外部表支持哪些操作;也就是说,_3 表示(1 << CMD_UPDATE) = 4INSERT表示(1 << CMD_INSERT) = 8DELETE表示(1 << CMD_DELETE) = 16

如果IsForeignRelUpdatable指针设置为NULL,则如果 FDW 分别提供ExecForeignInsertExecForeignUpdateExecForeignDelete,则假定外部表是可插入的,可更新的或可删除的。仅当 FDW 支持某些可更新的表和某些不可更新的表时,才需要此功能。 (即使那样,也可以在执行例程中引发错误而不是检入此函数.但是,此函数用于确定可更新性,以便在information_schema视图中显示.)

可以通过实现一组备用接口来优化对外部表的某些插入,更新和删除。用于插入,更新和删除的普通接口从远程服务器获取行,然后一次修改这些行。在某些情况下,此逐行方法是必需的,但效率可能较低。如果外部服务器可以在不实际检索它们的情况下确定应修改哪些行,并且如果不存在会影响操作的本地结构(行级本地触发器或父视图的WITH CHECK OPTION约束),则可能安排事情,以便整个操作在远程服务器上执行。下述接口使之成为可能。

bool
PlanDirectModify(PlannerInfo *root,
                 ModifyTable *plan,
                 Index resultRelation,
                 int subplan_index);

确定在远程服务器上执行直接修改是否安全。如果是这样,请在执行所需的计划操作后返回true。否则,返回false。在查询计划期间将调用此可选功能。如果此函数成功执行,则将在执行阶段调用BeginDirectModifyIterateDirectModifyEndDirectModify。否则,将使用上述表更新功能执行表修改。参数与PlanForeignModify相同。

要在远程服务器上执行直接修改,此功能必须使用在远程服务器上执行直接修改的ForeignScan计划节点重写目标子计划。 ForeignScanoperation字段必须设置为CmdType枚举;即CMD_UPDATE表示UPDATECMD_INSERT表示INSERTCMD_DELETE表示DELETE

有关其他信息,请参见Section 57.4

如果PlanDirectModify指针设置为NULL,则不会尝试在远程服务器上执行直接修改。

void
BeginDirectModify(ForeignScanState *node,
                  int eflags);

准备在远程服务器上执行直接修改。在执行程序启动期间调用。它应执行直接修改之前所需的任何初始化(应在首次调用IterateDirectModify时完成)。 ForeignScanState节点已经创建,但是其fdw_state字段仍然为 NULL。可通过ForeignScanState节点访问有关要修改的表的信息(尤其是从基础ForeignScan计划节点,其中包含PlanDirectModify提供的任何 FDW 专用信息)。 eflags包含标志位,这些标志位描述此计划节点的执行者的操作模式。

请注意,当(eflags & EXEC_FLAG_EXPLAIN_ONLY)为 true 时,此函数不应执行任何外部可见的操作;它仅应执行使节点状态对ExplainDirectModifyEndDirectModify有效的最低要求。

如果BeginDirectModify指针设置为NULL,则不会尝试在远程服务器上执行直接修改。

TupleTableSlot *
IterateDirectModify(ForeignScanState *node);

INSERTUPDATEDELETE查询没有RETURNING子句时,在远程服务器上直接进行修改后,只需返回 NULL。当查询具有该子句时,获取一个包含RETURNING计算所需数据的结果,并将其返回到 Tuples 表插槽中(为此目的,应使用节点的ScanTupleSlot)。实际插入,更新或删除的数据必须存储在节点EStatees_result_relation_info->ri_projectReturning->pi_exprContext->ecxt_scantuple中。如果没有更多行可用,则返回 NULL。请注意,这是在短暂的内存上下文中调用的,它将在调用之间重置。如果需要更长寿命的存储,请在BeginDirectModify中创建一个内存上下文,或者使用节点EStatees_query_cxt

如果提供的话,返回的行必须匹配fdw_scan_tlist目标列表,否则它们必须匹配要更新的外部表的行类型。如果您选择优化RETURNING计算不需要的获取列,则应在这些列位置插入空值,否则将生成一个fdw_scan_tlist列表,其中省略了这些列。

无论查询是否具有该子句,查询的报告行数都必须由 FDW 自身增加。当查询中没有该子句时,FDW 还必须在EXPLAIN ANALYZE情况下增加ForeignScanState节点的行数。

如果IterateDirectModify指针设置为NULL,则不会尝试在远程服务器上执行直接修改。

void
EndDirectModify(ForeignScanState *node);

在远程服务器上直接修改后进行清理。释放 palloc 的内存通常并不重要,但是例如,应清除打开的文件和与远程服务器的连接。

如果EndDirectModify指针设置为NULL,则不会尝试在远程服务器上执行直接修改。

57 .2.5. 行锁定的 FDW 例程

如果 FDW 希望支持晚期行锁定(如Section 57.5中所述),则它必须提供以下回调函数:

RowMarkType
GetForeignRowMarkType(RangeTblEntry *rte,
                      LockClauseStrength strength);

报告用于外部表的行标记选项。 rte是表的RangeTblEntry节点,而strength描述相关FOR UPDATE/SHARE子句(如果有)所请求的锁定强度。结果必须是RowMarkType枚举类型的成员。

在查询计划期间,将针对出现在UPDATEDELETESELECT FOR UPDATE/SHARE查询中且不是UPDATEDELETE的目标的每个外部表调用此函数。

如果GetForeignRowMarkType指针设置为NULL,则始终使用ROW_MARK_COPY选项。 (这意味着RefetchForeignRow将永远不会被调用,因此也不必提供它.)

有关更多信息,请参见Section 57.5

HeapTuple
RefetchForeignRow(EState *estate,
                  ExecRowMark *erm,
                  Datum rowid,
                  bool *updated);

如果需要,在锁定后从外部表中重新获取一个 Tuples。 estate是查询的全局执行状态。 ermExecRowMark结构,用于描述目标外部表和要获取的行锁类型(如果有)。 rowid标识要获取的 Tuples。 updated是输出参数。

此函数应返回获取的 Tuples 的 palloc 副本,如果无法获得行锁,则返回NULL。要获取的行锁类型由erm->markType定义,这是GetForeignRowMarkType先前返回的值。 (ROW_MARK_REFERENCE表示只重新获取 Tuples 而不获取任何锁,并且ROW_MARK_COPY永远不会被该例程看到.)

另外,如果获取的是 Tuples 的更新版本而不是先前获得的相同版本,则应将*updated设置为true。 (如果 FDW 无法确定,建议始终返回true.)

请注意,默认情况下,无法获取行锁会导致引发错误;仅当erm->waitPolicy指定SKIP LOCKED选项时,才返回NULL

rowid是先前为要重新获取的行读取的ctid值。尽管rowid值作为Datum传递,但当前只能是tid。选择功能 API 是希望将来有可能允许其他数据类型用作行 ID。

如果RefetchForeignRow指针设置为NULL,则尝试重新获取行的尝试将失败,并显示一条错误消息。

有关更多信息,请参见Section 57.5

bool
RecheckForeignScan(ForeignScanState *node,
                   TupleTableSlot *slot);

重新检查先前返回的 Tuples 是否仍与相关的扫描和联接限定符匹配,并可能提供该 Tuples 的修改版本。对于不执行联接下推的外部数据包装器,通常将其设置为NULL而不是适当地设置fdw_recheck_quals更为方便。但是,当按下外部联接时,即使存在所有必需的属性,也没有足够的将与所有基表相关的检查重新应用到结果 Tuples 的原因,因为与某些限定符不匹配可能会导致某些属性变为 NULL,而不是不返回任何 Tuples。 RecheckForeignScan可以重新检查限定符,如果仍然满足限定符,则返回 true,否则返回 false,但是它也可以将替换 Tuples 存储到提供的插槽中。

为了实现连接下推,外部数据包装器通常会构造一个替代的本地连接计划,该计划仅用于重新检查。这将成为ForeignScan的外部子计划。当需要重新检查时,可以执行此子计划,并且可以将生成的 Tuples 存储在插槽中。该计划不一定有效,因为没有基表返回多于一行。例如,它可以将所有联接实现为嵌套循环。函数GetExistingLocalJoinPath可用于在现有路径中搜索合适的本地连接路径,该路径可用作备用本地连接计划。 GetExistingLocalJoinPath在指定的联接关系的路径列表中搜索未参数化的路径。 (如果找不到这样的路径,则返回 NULL,在这种情况下,外部数据包装器可能会自行构建本地路径,或者可能选择不为该联接创建访问路径.)

57 .2.6. FDW 例程的解释

void
ExplainForeignScan(ForeignScanState *node,
                   ExplainState *es);

打印其他EXPLAIN输出以进行外部表扫描。该函数可以调用ExplainPropertyText和相关函数以将字段添加到EXPLAIN输出。 es中的标志字段可用于确定要打印的内容,并且在EXPLAIN ANALYZE情况下可以检查ForeignScanState节点的状态以提供运行时统计信息。

如果ExplainForeignScan指针设置为NULL,则在EXPLAIN期间不会打印其他信息。

void
ExplainForeignModify(ModifyTableState *mtstate,
                     ResultRelInfo *rinfo,
                     List *fdw_private,
                     int subplan_index,
                     struct ExplainState *es);

打印其他EXPLAIN输出以更新外部表。该函数可以调用ExplainPropertyText和相关函数以将字段添加到EXPLAIN输出。 es中的标志字段可用于确定要打印的内容,并且在EXPLAIN ANALYZE情况下可以检查ModifyTableState节点的状态以提供运行时统计信息。前四个参数与BeginForeignModify相同。

如果ExplainForeignModify指针设置为NULL,则在EXPLAIN期间不会打印其他信息。

void
ExplainDirectModify(ForeignScanState *node,
                    ExplainState *es);

打印其他EXPLAIN输出以直接在远程服务器上进行修改。该函数可以调用ExplainPropertyText和相关函数以将字段添加到EXPLAIN输出。 es中的标志字段可用于确定要打印的内容,并且在EXPLAIN ANALYZE情况下可以检查ForeignScanState节点的状态以提供运行时统计信息。

如果ExplainDirectModify指针设置为NULL,则在EXPLAIN期间不会打印其他信息。

57 .2.7. FDW 例程进行分析

bool
AnalyzeForeignTable(Relation relation,
                    AcquireSampleRowsFunc *func,
                    BlockNumber *totalpages);

在外部表上执行ANALYZE时,将调用此函数。如果 FDW 可以收集此外部表的统计信息,则它应返回true,并提供指向一个函数的指针,该函数将从* func 中的表中收集示例行,并在 totalpages *中的页中估计表的大小。否则,返回false

如果 FDW 不支持收集任何表的统计信息,则可以将AnalyzeForeignTable指针设置为NULL

如果提供,sample 采集功能必须具有签名

int
AcquireSampleRowsFunc(Relation relation,
                      int elevel,
                      HeapTuple *rows,
                      int targrows,
                      double *totalrows,
                      double *totaldeadrows);

应该从表中收集最多* targrows 行的随机 samples,并将其存储到调用方提供的 rows 数组中。必须返回收集的实际行数。另外,将表中有效和无效行总数的估计值存储到输出参数 totalrows totaldeadrows 中。 (如果 FDW 没有任何死行概念,请将 totaldeadrows *设置为零.)

57 .2.8. 导入外来方案的 FDW 例程

List *
ImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid);

获取外部表创建命令的列表。执行导入外国模式时会调用此函数,并传递该语句的解析树以及要使用的外部服务器的 OID。它应该返回一个 C 字符串列表,每个字符串必须包含创建外表命令。这些字符串将由核心服务器解析并执行。

ImportForeignSchemaStmt结构中,remote_schema是要从中导入表的远程模式的名称。 list_type标识如何过滤表名称:FDW_IMPORT_SCHEMA_ALL表示应导入远程模式中的所有表(在这种情况下table_list为空),FDW_IMPORT_SCHEMA_LIMIT_TO表示仅包括table_list中列出的表,而FDW_IMPORT_SCHEMA_EXCEPT表示排除table_list中列出的表。 options是用于导入过程的选项列表。选项的含义取决于 FDW。例如,FDW 可以使用一个选项来定义是否应导入列的NOT NULL属性。这些选项与 FDW 作为数据库对象选项支持的选项没有任何关系。

FDW 可能会忽略ImportForeignSchemaStmtlocal_schema字段,因为核心服务器将自动将该名称插入已解析的CREATE FOREIGN TABLE命令中。

FDW 也不必考虑实现list_typetable_list指定的过滤,因为核心服务器将自动跳过根据这些选项排除的表的所有返回命令。但是,首先避免为排除表创建命令的工作通常很有用。函数IsImportableForeignTable()可能对测试给定的外部表名称是否将通过过滤器很有用。

如果 FDW 不支持导入表定义,则可以将ImportForeignSchema指针设置为NULL

57 .2.9. 并行执行的 FDW 例程

ForeignScan节点可以选择支持并行执行。并行ForeignScan将在多个进程中执行,并且必须在所有协作进程中恰好返回每一行一次。为此,进程可以通过动态共享内存的固定大小的块进行协调。不能保证此共享内存在每个进程中都 Map 到相同的地址,因此它不能包含指针。以下功能都是可选的,但是如果要支持并行执行,则大多数功能是必需的。

bool
IsForeignScanParallelSafe(PlannerInfo *root, RelOptInfo *rel,
                          RangeTblEntry *rte);

测试是否可以在并行工作程序中执行扫描。仅当计划者认为可能有并行计划时,才调用此函数;如果在并行工作程序中安全进行该扫描,则应返回 true。如果远程数据源具有事务语义,通常不会这样,除非可以以某种方式使工作人员与数据的连接共享与领导者相同的事务上下文。

如果未定义此功能,则假定扫描必须在并行引导程序中进行。请注意,返回 true 并不意味着扫描本身可以并行完成,而仅意味着扫描可以在并行工作程序中执行。因此,即使不支持并行执行,定义此方法也很有用。

Size
EstimateDSMForeignScan(ForeignScanState *node, ParallelContext *pcxt);

估计并行操作所需的动态共享内存量。该数量可能高于实际使用的数量,但一定不能低于实际数量。返回值以字节为单位。该功能是可选的,如果不需要,可以省略;但是如果省略它,则接下来的三个功能也必须省略,因为不会为 FDW 使用分配任何共享内存。

void
InitializeDSMForeignScan(ForeignScanState *node, ParallelContext *pcxt,
                         void *coordinate);

初始化并行操作所需的动态共享内存。 coordinate指向大小等于EstimateDSMForeignScan返回值的共享内存区域。此功能是可选的,如果不需要,可以省略。

void
ReInitializeDSMForeignScan(ForeignScanState *node, ParallelContext *pcxt,
                           void *coordinate);

当外部扫描计划节点将要重新扫描时,重新初始化并行操作所需的动态共享内存。此功能是可选的,如果不需要,可以省略。建议的做法是,此函数仅重置共享状态,而ReScanForeignScan函数仅重置本地状态。当前,此函数将在ReScanForeignScan之前调用,但最好不要依赖该 Sequences。

void
InitializeWorkerForeignScan(ForeignScanState *node, shm_toc *toc,
                            void *coordinate);

根据领导者在InitializeDSMForeignScan期间设置的共享状态,初始化并行工作者的本地状态。此功能是可选的,如果不需要,可以省略。

void
ShutdownForeignScan(ForeignScanState *node);

当预计节点将不会执行完成时,请释放资源。并非在所有情况下都调用此方法。有时,EndForeignScan可能在没有先调用此函数的情况下被调用。由于并行查询所用的 DSM 段在调用此回调后即被销毁,因此希望在 DSM 段消失之前采取某些措施的外部数据包装程序应实现此方法。

57 .2.10. FDW 例程,用于路径的重新参数化

List *
ReparameterizeForeignPathByChild(PlannerInfo *root, List *fdw_private,
                                 RelOptInfo *child_rel);

在将由给定子关系child_rel的最高父级参数化的路径转换为要由子关系参数化的路径时,调用此函数。该函数用于重新参数化任何路径或转换保存在ForeignPath的给定fdw_private成员中的任何表达式节点。回调可以根据需要使用reparameterize_path_by_childadjust_appendrel_attrsadjust_appendrel_attrs_multilevel