Postgresql 中文操作指南

59.2. Foreign Data Wrapper Callback Routines #

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

The FDW handler function returns a palloc’d FdwRoutine struct containing pointers to the callback functions described below. The scan-related functions are required, the rest are optional.

FdwRoutine 类型的结构在 src/include/foreign/fdwapi.h 中声明,有关更多详细信息,请参阅该部分。

The FdwRoutine struct type is declared in src/include/foreign/fdwapi.h, which see for additional details.

59.2.1. FDW Routines for Scanning Foreign Tables #

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

从外键表中获取关系数量估计。这是在外键表的扫描查询规划开始时调用的。root 是计划者的查询的全局信息;baserel 是计划者有关此表的信息;foreigntableid 是外键表的 pg_class OID。(foreigntableid 可以从规划器数据结构中获取,但它被显式传递以节省精力。)

Obtain relation size estimates for a foreign table. This is called at the beginning of planning for a query that scans a foreign table. root is the planner’s global information about the query; baserel is the planner’s information about this table; and foreigntableid is the pg_class OID of the foreign table. (foreigntableid could be obtained from the planner data structures, but it’s passed explicitly to save effort.)

此函数应更新 baserel→rows,使其成为表扫描后返回的行数的预期数量,并计入了限制条件完成的过滤效果。baserel→rows 的初始值只是恒定默认估计,如果可能的话应予以替换。如果该函数可以计算平均结果行宽的更好估计,则它也可以选择更新 baserel→width。(初始值基于列数据类型和上一 ANALYZE 测量的列平均宽度值。)此外,如果该函数可以计算出外键表总行数的更好估计,则此函数可能会更新 baserel→tuples。(初始值来自 pg_classreltuples 表示上一次 ANALYZE 看到的总行数;如果没有在此外键表上进行 ANALYZE,则为 -1。)

This function should update baserel→rows to be the expected number of rows returned by the table scan, after accounting for the filtering done by the restriction quals. The initial value of baserel→rows is just a constant default estimate, which should be replaced if at all possible. The function may also choose to update baserel→width if it can compute a better estimate of the average result row width. (The initial value is based on column data types and on column average-width values measured by the last ANALYZE.) Also, this function may update baserel→tuples if it can compute a better estimate of the foreign table’s total row count. (The initial value is from pg_class.reltuples which represents the total row count seen by the last ANALYZE; it will be -1 if no ANALYZE has been done on this foreign table.)

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

See Section 59.4 for additional information.

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

在扫描外键表时创建可能访问路径。这在查询计划过程中调用。参数与已经调用的 GetForeignRelSize 相同。

Create possible access paths for a scan on a foreign table. This is called during query planning. The parameters are the same as for GetForeignRelSize, which has already been called.

该函数必须为扫描外键表生成至少一个访问路径(ForeignPath 节点),并且必须调用 add_path 将每个此类路径添加到 baserel→pathlist 中。建议使用 create_foreignscan_path 构建 ForeignPath 节点。该函数可以生成多个访问路径,例如,具有有效 pathkeys 的路径,以表示预排序结果。每个访问路径都必须包含成本估计,并且可以包含识别预期特定扫描方法所需的任何 FDW 私有信息。

This function must generate at least one access path (ForeignPath node) for a scan on the foreign table and must call add_path to add each such path to baserel→pathlist. It’s recommended to use create_foreignscan_path to build the ForeignPath nodes. The function can generate multiple access paths, e.g., a path which has valid pathkeys to represent a pre-sorted result. Each access path must contain cost estimates, and can contain any FDW-private information that is needed to identify the specific scan method intended.

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

See Section 59.4 for additional information.

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

根据选定的外部访问路径创建 ForeignScan 计划节点。这在查询规划的末尾调用。参数就像 GetForeignRelSize 一样,加上选定的 ForeignPath(先前由 GetForeignPathsGetForeignJoinPathsGetForeignUpperPaths 生成)、计划节点要发出的目标列表、计划节点要执行的限制条件以及由 RecheckForeignScan 执行的重新检查所用的 ForeignScan 的外部子计划。(如果路径用于连接而不是基础关系,则 foreigntableidInvalidOid。)

Create a ForeignScan plan node from the selected foreign access path. This is called at the end of query planning. The parameters are as for GetForeignRelSize, plus the selected ForeignPath (previously produced by GetForeignPaths, GetForeignJoinPaths, or GetForeignUpperPaths), the target list to be emitted by the plan node, the restriction clauses to be enforced by the plan node, and the outer subplan of the ForeignScan, which is used for rechecks performed by RecheckForeignScan. (If the path is for a join rather than a base relation, foreigntableid is InvalidOid.)

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

This function must create and return a ForeignScan plan node; it’s recommended to use make_foreignscan to build the ForeignScan node.

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

See Section 59.4 for additional information.

void
BeginForeignScan(ForeignScanState *node,
                 int eflags);

开始执行外键扫描。这是在执行器的启动期间调用的。它应执行扫描开始前所需的任何初始化,但不要开始执行实际扫描(应该在第一次调用 IterateForeignScan 时完成)。ForeignScanState 节点已经创建,但是它的 fdw_state 字段仍然为空。关于要扫描的表的信息可以通过 ForeignScanState 节点(特别是包含由 GetForeignPlan 提供的任何 FDW 私有信息的底层 ForeignScan 计划节点)进行访问。eflags 包含描述执行器此计划节点的操作模式的标志位。

Begin executing a foreign scan. This is called during executor startup. It should perform any initialization needed before the scan can start, but not start executing the actual scan (that should be done upon the first call to IterateForeignScan). The ForeignScanState node has already been created, but its fdw_state field is still NULL. Information about the table to scan is accessible through the ForeignScanState node (in particular, from the underlying ForeignScan plan node, which contains any FDW-private information provided by GetForeignPlan). eflags contains flag bits describing the executor’s operating mode for this plan node.

请注意,当 (eflags & EXEC_FLAG_EXPLAIN_ONLY) 为真时,该函数不应执行任何外部可见操作;它应该只做使节点状态对 ExplainForeignScanEndForeignScan 有效所需的最低限度工作。

Note that when (eflags & EXEC_FLAG_EXPLAIN_ONLY) is true, this function should not perform any externally-visible actions; it should only do the minimum required to make the node state valid for ExplainForeignScan and EndForeignScan.

TupleTableSlot *
IterateForeignScan(ForeignScanState *node);

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

Fetch one row from the foreign source, returning it in a tuple table slot (the node’s ScanTupleSlot should be used for this purpose). Return NULL if no more rows are available. The tuple table slot infrastructure allows either a physical or virtual tuple to be returned; in most cases the latter choice is preferable from a performance standpoint. Note that this is called in a short-lived memory context that will be reset between invocations. Create a memory context in BeginForeignScan if you need longer-lived storage, or use the es_query_cxt of the node’s EState.

返回的行必须匹配提供的 fdw_scan_tlist 目标列表,否则它们必须匹配正在扫描的外键表的行类型。如果您选择优化检索不需要的列,则应在那些列位置中插入空值,或生成一个省略那些列的 fdw_scan_tlist 列表。

The rows returned must match the fdw_scan_tlist target list if one was supplied, otherwise they must match the row type of the foreign table being scanned. If you choose to optimize away fetching columns that are not needed, you should insert nulls in those column positions, or else generate a fdw_scan_tlist list with those columns omitted.

请注意,PostgreSQL 的执行器不关心返回的行是否违反在外键表上定义的任何约束——但是规划器确实关心,并且如果外键表中有不满足已声明约束的行时,可能会错误地优化查询。如果当用户宣布约束应为真时违反了约束,则可以引发错误(就像在数据类型不匹配的情况下需要执行的操作一样)。

Note that PostgreSQL’s executor doesn’t care whether the rows returned violate any constraints that were defined on the foreign table — but the planner does care, and may optimize queries incorrectly if there are rows visible in the foreign table that do not satisfy a declared constraint. If a constraint is violated when the user has declared that the constraint should hold true, it may be appropriate to raise an error (just as you would need to do in the case of a data type mismatch).

void
ReScanForeignScan(ForeignScanState *node);

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

Restart the scan from the beginning. Note that any parameters the scan depends on may have changed value, so the new scan does not necessarily return exactly the same rows.

void
EndForeignScan(ForeignScanState *node);

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

End the scan and release resources. It is normally not important to release palloc’d memory, but for example open files and connections to remote servers should be cleaned up.

59.2.2. FDW Routines for Scanning Foreign Joins #

如果 FDW 支持远程执行外键连接(而不是通过抓取两个表的数据并在本地执行连接),则应提供此回调函数:

If an FDW supports performing foreign joins remotely (rather than by fetching both tables' data and doing the join locally), it should provide this callback function:

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

针对全部属于同一外键服务器的两个(或更多)外键表的连接创建可能访问路径。此可选函数在查询计划期间调用。与 GetForeignPaths 一样,此函数应针对提供 joinrel 生成 ForeignPath 路径(使用 create_foreign_join_path 构建它们),并调用 add_path 将这些路径添加到连接考虑路径集。但与 GetForeignPaths 不同,此函数不必成功创建至少一个路径,因为总是可能包含本地连接的路径。

Create possible access paths for a join of two (or more) foreign tables that all belong to the same foreign server. This optional function is called during query planning. As with GetForeignPaths, this function should generate ForeignPath path(s) for the supplied joinrel (use create_foreign_join_path to build them), and call add_path to add these paths to the set of paths considered for the join. But unlike GetForeignPaths, it is not necessary that this function succeed in creating at least one path, since paths involving local joining are always possible.

请注意,此函数将对相同的连接关系重复调用,具有内部和外部关系的不同组合;FDW 有责任最大限度地减少重复工作。

Note that this function will be invoked repeatedly for the same join relation, with different combinations of inner and outer relations; it is the responsibility of the FDW to minimize duplicated work.

如果为连接选择 ForeignPath 路径,它将代表整个连接过程;不会使用为组件表和辅助连接生成的路经。连接路径的后继处理过程与扫描单个外键表的路径十分相似。一个区别是结果 ForeignScan 计划节点的 scanrelid 应设置为零,因为它不表示单个关系;相反,ForeignScan 节点的 fs_relids 字段表示被连接的关系集。(后一个字段由核心规划器代码自动设置,无需由 FDW 填充。)另一个区别在于,由于无法从系统目录中找到远程连接的列列表,FDW 必须使用合适的 TargetEntry 节点列表来填充 fdw_scan_tlist,表示它将在返回的元组中在运行时提供的列集。

If a ForeignPath path is chosen for the join, it will represent the entire join process; paths generated for the component tables and subsidiary joins will not be used. Subsequent processing of the join path proceeds much as it does for a path scanning a single foreign table. One difference is that the scanrelid of the resulting ForeignScan plan node should be set to zero, since there is no single relation that it represents; instead, the fs_relids field of the ForeignScan node represents the set of relations that were joined. (The latter field is set up automatically by the core planner code, and need not be filled by the FDW.) Another difference is that, because the column list for a remote join cannot be found from the system catalogs, the FDW must fill fdw_scan_tlist with an appropriate list of TargetEntry nodes, representing the set of columns it will supply at run time in the tuples it returns.

Note

从 PostgreSQL 16 开始,fs_relids 包含外部连接的 rangetable 索引(如果此类连接中包含任何索引)。新字段 fs_base_relids 仅包含基本关系索引,因此模拟了 fs_relids 的旧语义。

Beginning with PostgreSQL 16, fs_relids includes the rangetable indexes of outer joins, if any were involved in this join. The new field fs_base_relids includes only base relation indexes, and thus mimics fs_relids's old semantics.

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

See Section 59.4 for additional information.

59.2.3. FDW Routines for Planning Post-Scan/Join Processing #

如果 FDW 支持执行远程扫描后/连接后处理,例如远程聚合,则应提供此回调函数:

If an FDW supports performing remote post-scan/join processing, such as remote aggregation, it should provide this callback function:

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

创建_upper relation_处理(这是计划中所有扫描后/连接查询处理的术语,例如聚合、窗口函数、排序和表更新)的可能访问路径。此可选函数在查询计划期间调用。目前,仅当查询中涉及的所有基本关系属于同一个 FDW 时才调用它。此函数应为 FDW 知道如何远程执行的任何扫描后/连接处理生成_ForeignPath_路径(使用_create_foreign_upper_path_构建它们),并调用_add_path_将这些路径添加到指示的上层关系。与_GetForeignJoinPaths_一样,此函数不必成功创建任何路径,因为总是可能涉及局部处理的路径。

Create possible access paths for upper relation processing, which is the planner’s term for all post-scan/join query processing, such as aggregation, window functions, sorting, and table updates. This optional function is called during query planning. Currently, it is called only if all base relation(s) involved in the query belong to the same FDW. This function should generate ForeignPath path(s) for any post-scan/join processing that the FDW knows how to perform remotely (use create_foreign_upper_path to build them), and call add_path to add these paths to the indicated upper relation. As with GetForeignJoinPaths, it is not necessary that this function succeed in creating any paths, since paths involving local processing are always possible.

stage_参数可识别当前正在考虑的扫描后/连接步骤。_output_rel_是应接收表示此步骤计算的路径的上层关系,而_input_rel_是表示此步骤输入的关系。_extra_参数提供其他详细信息,目前仅为_UPPERREL_PARTIAL_GROUP_AGG_或_UPPERREL_GROUP_AGG_设置,在这种情况下,它指向_GroupPathExtraData_结构;或者对于_UPPERREL_FINAL,在这种情况下,它指向_FinalPathExtraData_结构。(请注意,添加到_output_rel_的_ForeignPath_路径通常不会直接依赖于_input_rel_的路径,因为它们的处理期望在外部完成。但是,检查先前为之前的处理步骤生成的路径可以帮助避免冗余计划工作。)

The stage parameter identifies which post-scan/join step is currently being considered. output_rel is the upper relation that should receive paths representing computation of this step, and input_rel is the relation representing the input to this step. The extra parameter provides additional details, currently, it is set only for UPPERREL_PARTIAL_GROUP_AGG or UPPERREL_GROUP_AGG, in which case it points to a GroupPathExtraData structure; or for UPPERREL_FINAL, in which case it points to a FinalPathExtraData structure. (Note that ForeignPath paths added to output_rel would typically not have any direct dependency on paths of the input_rel, since their processing is expected to be done externally. However, examining paths previously generated for the previous processing step can be useful to avoid redundant planning work.)

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

See Section 59.4 for additional information.

59.2.4. FDW Routines for Updating Foreign Tables #

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

If an FDW supports writable foreign tables, it should provide some or all of the following callback functions depending on the needs and capabilities of the FDW:

void
AddForeignUpdateTargets(PlannerInfo *root,
                        Index rtindex,
                        RangeTblEntry *target_rte,
                        Relation target_relation);

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

UPDATE and DELETE operations are performed against rows previously fetched by the table-scanning functions. The FDW may need extra information, such as a row ID or the values of primary-key columns, to ensure that it can identify the exact row to update or delete. To support that, this function can add extra hidden, or “junk”, target columns to the list of columns that are to be retrieved from the foreign table during an UPDATE or DELETE.

为此,创建一个 Var ,表示您需要的额外值,并将其传递给 add_row_identity_var ,同时提供一个垃圾列名称。(如果需要多列,可以这样做多次。)您必须为您需要的每个不同的 Var 选择一个不同的垃圾列名称,但 Var_s that are identical except for the _varno 字段可以且应该共享一个列名称。核心系统使用垃圾列名称 tableoid 来表示表中的 tableoid 列, ctidctid_N for ctid, wholerow for a whole-row Var marked with vartype = RECORD, and wholerow_N for a whole-row Varvartype 等于表的声明行类型。只要可能,就重新使用这些名称(规划程序将合并对相同垃圾列的重复请求)。如果您需要的垃圾列类型与此不同,最好选择一个以您的扩展名作为前缀的名称,以避免与其他 FDW 冲突。

To do that, construct a Var representing an extra value you need, and pass it to add_row_identity_var, along with a name for the junk column. (You can do this more than once if several columns are needed.) You must choose a distinct junk column name for each different Var you need, except that Var_s that are identical except for the _varno field can and should share a column name. The core system uses the junk column names tableoid for a table’s tableoid column, ctid or ctid_N for _ctid, wholerow for a whole-row Var marked with vartype = RECORD, and wholerow_N for a whole-row _Var with vartype equal to the table’s declared row type. Re-use these names when you can (the planner will combine duplicate requests for identical junk columns). If you need another kind of junk column besides these, it might be wise to choose a name prefixed with your extension name, to avoid conflicts against other FDWs.

如果_AddForeignUpdateTargets_指针设置为_NULL_,则不会添加额外的目标表达式。(这将使得不可能实现_DELETE_操作,但如果 FDW 依赖于不变的主键来识别行,则_UPDATE_仍然可行。)

If the AddForeignUpdateTargets pointer is set to NULL, no extra target expressions are added. (This will make it impossible to implement DELETE operations, though UPDATE may still be feasible if the FDW relies on an unchanging primary key to identify rows.)

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

对外部表执行插入、更新或删除所需的任何其他计划操作。此函数生成 FDW 私有信息,该信息将附加到执行更新操作的_ModifyTable_计划节点。此私有信息必须采用_List_的形式,并将在执行阶段传递给_BeginForeignModify_。

Perform any additional planning actions needed for an insert, update, or delete on a foreign table. This function generates the FDW-private information that will be attached to the ModifyTable plan node that performs the update action. This private information must have the form of a List, and will be delivered to BeginForeignModify during the execution stage.

_root_是计划者有关查询的全局信息。_plan_是_ModifyTable_计划节点,除了_fdwPrivLists_字段外,它已经完成。_resultRelation_通过范围表索引识别目标外部表。_subplan_index_识别这是_ModifyTable_计划节点的哪个目标,从零计数;如果您要索引到_plan_节点的目标关系子结构中,请使用此方法。

root is the planner’s global information about the query. plan is the ModifyTable plan node, which is complete except for the fdwPrivLists field. resultRelation identifies the target foreign table by its range table index. subplan_index identifies which target of the ModifyTable plan node this is, counting from zero; use this if you want to index into per-target-relation substructures of the plan node.

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

See Section 59.4 for additional information.

如果_PlanForeignModify_指针设置为_NULL_,则不会执行其他计划时间操作,并且传递给_BeginForeignModify_的_fdw_private_列表将为 NIL。

If the PlanForeignModify pointer is set to NULL, no additional plan-time actions are taken, and the fdw_private list delivered to BeginForeignModify will be NIL.

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

开始执行外部表修改操作。此例程在执行器启动期间调用。它应执行实际表修改之前所需的任何初始化。随后,ExecForeignInsert/ExecForeignBatchInsert、_ExecForeignUpdate_或_ExecForeignDelete_将针对要插入、更新或删除的元组(们)调用。

Begin executing a foreign table modification operation. This routine is called during executor startup. It should perform any initialization needed prior to the actual table modifications. Subsequently, ExecForeignInsert/ExecForeignBatchInsert, ExecForeignUpdate or ExecForeignDelete will be called for tuple(s) to be inserted, updated, or deleted.

mtstate_是正在执行的_ModifyTable_计划节点的总体状态;可通过此结构获取关于整个计划和执行状态的全局信息。_rinfo_是描述目标外部表的_ResultRelInfo_结构。(_ResultRelInfori_FdwState 字段可用于 FDW 存储此操作所需的任何私有状态。) fdw_private 包含由 PlanForeignModify 生成的私有数据(如果有)。subplan_index 识别这是 ModifyTable 计划节点的哪个目标。eflags 包含描述此计划节点的执行器操作模式的标志位。

mtstate is the overall state of the ModifyTable plan node being executed; global data about the plan and execution state is available via this structure. rinfo is the ResultRelInfo struct describing the target foreign table. (The ri_FdwState field of ResultRelInfo is available for the FDW to store any private state it needs for this operation.) fdw_private contains the private data generated by PlanForeignModify, if any. subplan_index identifies which target of the ModifyTable plan node this is. eflags contains flag bits describing the executor’s operating mode for this plan node.

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

Note that when (eflags & EXEC_FLAG_EXPLAIN_ONLY) is true, this function should not perform any externally-visible actions; it should only do the minimum required to make the node state valid for ExplainForeignModify and EndForeignModify.

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

If the BeginForeignModify pointer is set to NULL, no action is taken during executor startup.

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

向外部表中插入一个元组。estate_是查询的全局执行状态。_rinfo_是描述目标外部表的_ResultRelInfo_结构。_slot_包含要插入的元组;它将与外部表的行类型定义相匹配。_planSlot_包含由_ModifyTable_计划节点的子计划生成的元组;与_slot_不同,它可能包含附加的“垃圾”列。(_planSlot 通常对 INSERT 案例兴趣不大,但提供它是为了完整性。)

Insert one tuple into the foreign table. estate is global execution state for the query. rinfo is the ResultRelInfo struct describing the target foreign table. slot contains the tuple to be inserted; it will match the row-type definition of the foreign table. planSlot contains the tuple that was generated by the ModifyTable plan node’s subplan; it differs from slot in possibly containing additional “junk” columns. (The planSlot is typically of little interest for INSERT cases, but is provided for completeness.)

返回的值要么是一个包含实际插入数据的槽(它可能不同于提供的数据,例如作为触发器操作的结果),要么是 NULL,如果实际上没有插入任何行(同样,通常是触发器导致的结果)。可以为此目的重复使用传入的_slot_。

The return value is either a slot containing the data that was actually inserted (this might differ from the data supplied, for example as a result of trigger actions), or NULL if no row was actually inserted (again, typically as a result of triggers). The passed-in slot can be re-used for this purpose.

仅当_INSERT_语句具有_RETURNING_子句或涉及到视图_WITH CHECK OPTION_时;或者外部表具有_AFTER ROW_触发器时,才会使用返回槽中的数据。触发器需要所有列,但 FDW 可以根据_RETURNING_子句或_WITH CHECK OPTION_约束的内容选择优化掉返回部分或所有列。无论如何,都必须返回一些槽来表示成功,否则查询的报告行数将错误。

The data in the returned slot is used only if the INSERT statement has a RETURNING clause or involves a view WITH CHECK OPTION; or if the foreign table has an AFTER ROW trigger. Triggers require all columns, but the FDW could choose to optimize away returning some or all columns depending on the contents of the RETURNING clause or WITH CHECK OPTION constraints. Regardless, some slot must be returned to indicate success, or the query’s reported row count will be wrong.

如果_ExecForeignInsert_指针设置为_NULL_,则尝试插入到外部表将失败并显示错误消息。

If the ExecForeignInsert pointer is set to NULL, attempts to insert into the foreign table will fail with an error message.

请注意,当将路由的元组插入到外部表分区中或在外部表上执行_COPY FROM_时,也会调用此函数,在这种情况下,调用它的方式与在_INSERT_案例中的调用方式不同。请参阅下面描述的回调函数,允许 FDW 支持它。

Note that this function is also called when inserting routed tuples into a foreign-table partition or executing COPY FROM on a foreign table, in which case it is called in a different way than it is in the INSERT case. See the callback functions described below that allow the FDW to support that.

TupleTableSlot **
ExecForeignBatchInsert(EState *estate,
                       ResultRelInfo *rinfo,
                       TupleTableSlot **slots,
                       TupleTableSlot **planSlots,
                       int *numSlots);

将多个元组批量插入到外部表中。ExecForeignInsert_的参数相同,但_slots_和_planSlots_包含多个元组,*numSlots_指定这些数组中的元组数量。

Insert multiple tuples in bulk into the foreign table. The parameters are the same for ExecForeignInsert except slots and planSlots contain multiple tuples and *numSlots specifies the number of tuples in those arrays.

返回值是包含实际插入的数据的槽阵列(这可能与提供的数据不同,例如由于触发器操作。)已传递的 slots 可重新用于此目的。成功插入元组的数量将返回在 *numSlots 中。

The return value is an array of slots containing the data that was actually inserted (this might differ from the data supplied, for example as a result of trigger actions.) The passed-in slots can be re-used for this purpose. The number of successfully inserted tuples is returned in *numSlots.

返回槽中的数据仅当 INSERT 语句涉及视图 WITH CHECK OPTION 时使用;或如果外键表有一个 AFTER ROW 触发器。触发器需要所有列,但 FDW 可以选择根据 WITH CHECK OPTION 约束的内容优化返回部分或所有列。

The data in the returned slot is used only if the INSERT statement involves a view WITH CHECK OPTION; or if the foreign table has an AFTER ROW trigger. Triggers require all columns, but the FDW could choose to optimize away returning some or all columns depending on the contents of the WITH CHECK OPTION constraints.

如果 ExecForeignBatchInsertGetForeignModifyBatchSize 指针设置为 NULL,则尝试插入到外键表的尝试将使用 ExecForeignInsert。如果 INSERT 具有 RETURNING 子句,则不使用此函数。

If the ExecForeignBatchInsert or GetForeignModifyBatchSize pointer is set to NULL, attempts to insert into the foreign table will use ExecForeignInsert. This function is not used if the INSERT has the RETURNING clause.

请注意,当将路由的元组插入到外部表分区中或在外部表上执行_COPY FROM_时,也会调用此函数,在这种情况下,调用它的方式与在_INSERT_案例中的调用方式不同。请参阅下面描述的回调函数,允许 FDW 支持它。

Note that this function is also called when inserting routed tuples into a foreign-table partition or executing COPY FROM on a foreign table, in which case it is called in a different way than it is in the INSERT case. See the callback functions described below that allow the FDW to support that.

int
GetForeignModifyBatchSize(ResultRelInfo *rinfo);

报告单个 ExecForeignBatchInsert 调用可处理的指定外键表的最大元组数。执行器最多将给定数量的元组传递给 ExecForeignBatchInsertrinfo 是描述目标外键表的 ResultRelInfo 结构。期望 FDW 为用户提供外键服务器和/或外键表选项以设置此值,或一些硬编码值。

Report the maximum number of tuples that a single ExecForeignBatchInsert call can handle for the specified foreign table. The executor passes at most the given number of tuples to ExecForeignBatchInsert. rinfo is the ResultRelInfo struct describing the target foreign table. The FDW is expected to provide a foreign server and/or foreign table option for the user to set this value, or some hard-coded value.

如果 ExecForeignBatchInsertGetForeignModifyBatchSize 指针设置为 NULL,则尝试插入到外键表的尝试将使用 ExecForeignInsert

If the ExecForeignBatchInsert or GetForeignModifyBatchSize pointer is set to NULL, attempts to insert into the foreign table will use ExecForeignInsert.

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

更新外键表中的一个元组。estate 是查询的全局执行状态。rinfo 是描述目标外键表的 ResultRelInfo 结构。slot 包含元组的新数据;它将匹配外键表的行类型定义。planSlot 包含由 ModifyTable 计划节点的子计划生成的元组。与 slot 不同,此元组仅包含由查询更改的列的新值,因此不要依赖外键表的属性编号来索引 planSlot。此外,planSlot 通常包含其他“垃圾”列。特别是,可以从这个槽获得 AddForeignUpdateTargets 请求的任何垃圾列。

Update one tuple in the foreign table. estate is global execution state for the query. rinfo is the ResultRelInfo struct describing the target foreign table. slot contains the new data for the tuple; it will match the row-type definition of the foreign table. planSlot contains the tuple that was generated by the ModifyTable plan node’s subplan. Unlike slot, this tuple contains only the new values for columns changed by the query, so do not rely on attribute numbers of the foreign table to index into planSlot. Also, planSlot typically contains additional “junk” columns. In particular, any junk columns that were requested by AddForeignUpdateTargets will be available from this slot.

返回值是要么包含按实际更新的行槽(这可能与提供的数据不同,例如由于触发器操作),要么是空值,如果没有实际更新任何行(同样通常是由于触发器)。已传递的 slot 可重新用于此目的。

The return value is either a slot containing the row as it was actually updated (this might differ from the data supplied, for example as a result of trigger actions), or NULL if no row was actually updated (again, typically as a result of triggers). The passed-in slot can be re-used for this purpose.

返回槽中的数据仅当 UPDATE 语句有 RETURNING 子句或涉及视图 WITH CHECK OPTION 时使用;或如果外键表有一个 AFTER ROW 触发器。触发器需要所有列,但 FDW 可以选择根据 RETURNING 子句或 WITH CHECK OPTION 约束的内容优化返回部分或所有列。无论如何,都必须返回一些槽以表示成功,否则查询的报告行计数会错误。

The data in the returned slot is used only if the UPDATE statement has a RETURNING clause or involves a view WITH CHECK OPTION; or if the foreign table has an AFTER ROW trigger. Triggers require all columns, but the FDW could choose to optimize away returning some or all columns depending on the contents of the RETURNING clause or WITH CHECK OPTION constraints. Regardless, some slot must be returned to indicate success, or the query’s reported row count will be wrong.

如果 ExecForeignUpdate 指针设置为 NULL,则尝试更新外键表会失败,并显示错误消息。

If the ExecForeignUpdate pointer is set to NULL, attempts to update the foreign table will fail with an error message.

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

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

Delete one tuple from the foreign table. estate is global execution state for the query. rinfo is the ResultRelInfo struct describing the target foreign table. slot contains nothing useful upon call, but can be used to hold the returned tuple. planSlot contains the tuple that was generated by the ModifyTable plan node’s subplan; in particular, it will carry any junk columns that were requested by AddForeignUpdateTargets. The junk column(s) must be used to identify the tuple to be deleted.

返回值是要么包含已被删除行的槽,要么是空值,如果没有删除任何行(通常是由于触发器)。已传递的 slot 可用于保存要返回的元组。

The return value is either a slot containing the row that was deleted, or NULL if no row was deleted (typically as a result of triggers). The passed-in slot can be used to hold the tuple to be returned.

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

The data in the returned slot is used only if the DELETE query has a RETURNING clause or the foreign table has an AFTER ROW trigger. Triggers require all columns, but the FDW could choose to optimize away returning some or all columns depending on the contents of the RETURNING clause. Regardless, some slot must be returned to indicate success, or the query’s reported row count will be wrong.

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

If the ExecForeignDelete pointer is set to NULL, attempts to delete from the foreign table will fail with an error message.

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

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

End the table update and release resources. It is normally not important to release palloc’d memory, but for example open files and connections to remote servers should be cleaned up.

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

If the EndForeignModify pointer is set to NULL, no action is taken during executor shutdown.

INSERTCOPY FROM 插入到分区表的元组将路由到分区。如果 FDW 支持可路由的外键表分区,它还应提供以下回调函数。这些函数在对外键表执行 COPY FROM 时也会被调用。

Tuples inserted into a partitioned table by INSERT or COPY FROM are routed to partitions. If an FDW supports routable foreign-table partitions, it should also provide the following callback functions. These functions are also called when COPY FROM is executed on a foreign table.

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

开始在外键表上执行插入操作。在将第一个元组插入外键表之前(无论是为元组路由选择的 partition,还是 COPY FROM 命令中指定的目标),都会调用此例程。它应该执行实际插入前所需的任何初始化。随后,将对要插入外键表的元组调用 ExecForeignInsertExecForeignBatchInsert

Begin executing an insert operation on a foreign table. This routine is called right before the first tuple is inserted into the foreign table in both cases when it is the partition chosen for tuple routing and the target specified in a COPY FROM command. It should perform any initialization needed prior to the actual insertion. Subsequently, ExecForeignInsert or ExecForeignBatchInsert will be called for tuple(s) to be inserted into the foreign table.

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

mtstate is the overall state of the ModifyTable plan node being executed; global data about the plan and execution state is available via this structure. rinfo is the ResultRelInfo struct describing the target foreign table. (The ri_FdwState field of ResultRelInfo is available for the FDW to store any private state it needs for this operation.)

当由 COPY FROM 命令调用时,不会提供 mtstate 中与计划相关的全局数据,并且 ExecForeignInsertplanSlot 参数随后每个插入的元组都会调用 NULL,无论外键表是为元组路由选择的 partition 还是命令中指定的目标。

When this is called by a COPY FROM command, the plan-related global data in mtstate is not provided and the planSlot parameter of ExecForeignInsert subsequently called for each inserted tuple is NULL, whether the foreign table is the partition chosen for tuple routing or the target specified in the command.

如果将 BeginForeignInsert 指针设置为 NULL,将不采取任何初始化操作。

If the BeginForeignInsert pointer is set to NULL, no action is taken for the initialization.

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

Note that if the FDW does not support routable foreign-table partitions and/or executing COPY FROM on foreign tables, this function or ExecForeignInsert/ExecForeignBatchInsert subsequently called must throw error as needed.

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

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

End the insert operation and release resources. It is normally not important to release palloc’d memory, but for example open files and connections to remote servers should be cleaned up.

如果将 EndForeignInsert 指针设置为 NULL,将不采取任何终止操作。

If the EndForeignInsert pointer is set to NULL, no action is taken for the termination.

int
IsForeignRelUpdatable(Relation rel);

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

Report which update operations the specified foreign table supports. The return value should be a bit mask of rule event numbers indicating which operations are supported by the foreign table, using the CmdType enumeration; that is, (1 << CMD_UPDATE) = 4 for UPDATE, (1 << CMD_INSERT) = 8 for INSERT, and (1 << CMD_DELETE) = 16 for DELETE.

如果将 IsForeignRelUpdatable 指针设置为 NULL,则假设如果 FDW 分别提供 ExecForeignInsertExecForeignUpdateExecForeignDelete,则外表可插入、可更新或可删除。此函数仅在 FDW 支持一些可更新外表和一些不可更新外表的情况下才需要。(即使在这种情况下,也可以在执行例程中引发错误,而不是在此函数中检查。但是,此函数用于确定可更新性以在 information_schema 视图中显示。)

If the IsForeignRelUpdatable pointer is set to NULL, foreign tables are assumed to be insertable, updatable, or deletable if the FDW provides ExecForeignInsert, ExecForeignUpdate, or ExecForeignDelete respectively. This function is only needed if the FDW supports some tables that are updatable and some that are not. (Even then, it’s permissible to throw an error in the execution routine instead of checking in this function. However, this function is used to determine updatability for display in the information_schema views.)

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

Some inserts, updates, and deletes to foreign tables can be optimized by implementing an alternative set of interfaces. The ordinary interfaces for inserts, updates, and deletes fetch rows from the remote server and then modify those rows one at a time. In some cases, this row-by-row approach is necessary, but it can be inefficient. If it is possible for the foreign server to determine which rows should be modified without actually retrieving them, and if there are no local structures which would affect the operation (row-level local triggers, stored generated columns, or WITH CHECK OPTION constraints from parent views), then it is possible to arrange things so that the entire operation is performed on the remote server. The interfaces described below make this possible.

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

决定是否可以安全地在某个远程服务器上执行直接修改。如果是这样,在执行为此执行所需的规划操作后,应返回 true。否则,返回 false。此可选函数在查询规划期间调用。如果此函数成功,则 BeginDirectModifyIterateDirectModifyEndDirectModify 将在执行阶段被调用。否则,将使用上面描述的表更新函数来执行表修改。参数与 PlanForeignModify 中的参数相同。

Decide whether it is safe to execute a direct modification on the remote server. If so, return true after performing planning actions needed for that. Otherwise, return false. This optional function is called during query planning. If this function succeeds, BeginDirectModify, IterateDirectModify and EndDirectModify will be called at the execution stage, instead. Otherwise, the table modification will be executed using the table-updating functions described above. The parameters are the same as for PlanForeignModify.

要对远程服务器执行直接修改,此函数必须使用 ForeignScan 计划节点重写目标子计划,该节点在远程服务器上执行直接修改。ForeignScanoperationresultRelation 字段必须设置得当。operation 必须设置为对应于语句种类的 CmdType 枚举(即对于 UPDATECMD_UPDATE,对于 INSERTCMD_INSERT,对于 DELETECMD_DELETE),并且 resultRelation 参数必须复制到 resultRelation 字段。

To execute the direct modification on the remote server, this function must rewrite the target subplan with a ForeignScan plan node that executes the direct modification on the remote server. The operation and resultRelation fields of the ForeignScan must be set appropriately. operation must be set to the CmdType enumeration corresponding to the statement kind (that is, CMD_UPDATE for UPDATE, CMD_INSERT for INSERT, and CMD_DELETE for DELETE), and the resultRelation argument must be copied to the resultRelation field.

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

See Section 59.4 for additional information.

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

If the PlanDirectModify pointer is set to NULL, no attempts to execute a direct modification on the remote server are taken.

void
BeginDirectModify(ForeignScanState *node,
                  int eflags);

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

Prepare to execute a direct modification on the remote server. This is called during executor startup. It should perform any initialization needed prior to the direct modification (that should be done upon the first call to IterateDirectModify). The ForeignScanState node has already been created, but its fdw_state field is still NULL. Information about the table to modify is accessible through the ForeignScanState node (in particular, from the underlying ForeignScan plan node, which contains any FDW-private information provided by PlanDirectModify). eflags contains flag bits describing the executor’s operating mode for this plan node.

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

Note that when (eflags & EXEC_FLAG_EXPLAIN_ONLY) is true, this function should not perform any externally-visible actions; it should only do the minimum required to make the node state valid for ExplainDirectModify and EndDirectModify.

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

If the BeginDirectModify pointer is set to NULL, no attempts to execute a direct modification on the remote server are taken.

TupleTableSlot *
IterateDirectModify(ForeignScanState *node);

INSERTUPDATEDELETE 查询没有 RETURNING 子句时,仅在远程服务器上执行直接修改后返回 NULL。当查询有该子句时,获取一个包含 RETURNING 计算所需数据的 result,并将其以元组表槽的形式返回(应使用节点的 ScanTupleSlot 来实现此目的)。实际插入、更新或删除的数据必须存储在 node→resultRelInfo→ri_projectReturning→pi_exprContext→ecxt_scantuple 中。如果没有更多可用的行,则返回 NULL。请注意,这是在调用之间将被重置的短暂的内存上下文中调用的。如果您需要更长时间的存储,请在 BeginDirectModify 中创建一个内存上下文,或使用节点的 EStatees_query_cxt

When the INSERT, UPDATE or DELETE query doesn’t have a RETURNING clause, just return NULL after a direct modification on the remote server. When the query has the clause, fetch one result containing the data needed for the RETURNING calculation, returning it in a tuple table slot (the node’s ScanTupleSlot should be used for this purpose). The data that was actually inserted, updated or deleted must be stored in node→resultRelInfo→ri_projectReturning→pi_exprContext→ecxt_scantuple. Return NULL if no more rows are available. Note that this is called in a short-lived memory context that will be reset between invocations. Create a memory context in BeginDirectModify if you need longer-lived storage, or use the es_query_cxt of the node’s EState.

返回的行必须与 fdw_scan_tlist 目标列表相匹配(如果提供了此类列表),否则它们必须与正在更新的外表的行类型相匹配。如果您选择优化删除对 RETURNING 计算不必要获取的列,您应将 null 插入到这些列位置,或使用省略这些列的 fdw_scan_tlist 列表生成。

The rows returned must match the fdw_scan_tlist target list if one was supplied, otherwise they must match the row type of the foreign table being updated. If you choose to optimize away fetching columns that are not needed for the RETURNING calculation, you should insert nulls in those column positions, or else generate a fdw_scan_tlist list with those columns omitted.

无论查询有没有该子句,查询报告的行数都必须由 FDW 本身增加。当查询没有该子句时,FDW 还必须为 EXPLAIN ANALYZE 用例中 ForeignScanState 节点增加行数。

Whether the query has the clause or not, the query’s reported row count must be incremented by the FDW itself. When the query doesn’t have the clause, the FDW must also increment the row count for the ForeignScanState node in the EXPLAIN ANALYZE case.

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

If the IterateDirectModify pointer is set to NULL, no attempts to execute a direct modification on the remote server are taken.

void
EndDirectModify(ForeignScanState *node);

清理在远程服务器上执行直接修改后的操作。通常不重要释放 palloc 分配的内存,但例如应清理远程服务器上的打开文件和连接。

Clean up following a direct modification on the remote server. It is normally not important to release palloc’d memory, but for example open files and connections to the remote server should be cleaned up.

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

If the EndDirectModify pointer is set to NULL, no attempts to execute a direct modification on the remote server are taken.

59.2.5. FDW Routines for TRUNCATE #

void
ExecForeignTruncate(List *rels,
                    DropBehavior behavior,
                    bool restart_seqs);

截断外部表。在外部表上执行 TRUNCATE 时,将调用此函数。 rels 是一个 Relation 数据结构列表,其中包含要截断的外部表。

Truncate foreign tables. This function is called when TRUNCATE is executed on a foreign table. rels is a list of Relation data structures of foreign tables to truncate.

behaviorDROP_RESTRICTDROP_CASCADE,分别表示原始 TRUNCATE 命令中请求了 RESTRICTCASCADE 选项。

behavior is either DROP_RESTRICT or DROP_CASCADE indicating that the RESTRICT or CASCADE option was requested in the original TRUNCATE command, respectively.

如果 restart_seqstrue,则原始 TRUNCATE 命令请求的是 RESTART IDENTITY 行为,否则请求的是 CONTINUE IDENTITY 行为。

If restart_seqs is true, the original TRUNCATE command requested the RESTART IDENTITY behavior, otherwise the CONTINUE IDENTITY behavior was requested.

请注意,原始 TRUNCATE 命令中指定的 ONLY 选项不会传递给 ExecForeignTruncate。此行为类似于外部表上的 SELECTUPDATEDELETE 的回调函数。

Note that the ONLY options specified in the original TRUNCATE command are not passed to ExecForeignTruncate. This behavior is similar to the callback functions of SELECT, UPDATE and DELETE on a foreign table.

对于要截断其外部表的每个外部服务器,ExecForeignTruncate 会被调用一次。这意味着 rels 中包含的所有外部表都必须属于同一台服务器。

ExecForeignTruncate is invoked once per foreign server for which foreign tables are to be truncated. This means that all foreign tables included in rels must belong to the same server.

如果 ExecForeignTruncate 指针设置为 NULL,则尝试截断外部表会失败,并出现错误消息。

If the ExecForeignTruncate pointer is set to NULL, attempts to truncate foreign tables will fail with an error message.

59.2.6. FDW Routines for Row Locking #

如果 FDW 希望支持 late row locking(如 Section 59.5中所述),则必须提供以下回调函数:

If an FDW wishes to support late row locking (as described in Section 59.5), it must provide the following callback functions:

RowMarkType
GetForeignRowMarkType(RangeTblEntry *rte,
                      LockClauseStrength strength);

报告对外部表使用哪种行标记选项。rte 是表的 RangeTblEntry 节点,如果存在,strength 将描述相关 FOR UPDATE/SHARE 子句请求的锁强度。结果必须是 RowMarkType 枚举类型的成员。

Report which row-marking option to use for a foreign table. rte is the RangeTblEntry node for the table and strength describes the lock strength requested by the relevant FOR UPDATE/SHARE clause, if any. The result must be a member of the RowMarkType enum type.

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

This function is called during query planning for each foreign table that appears in an UPDATE, DELETE, or SELECT FOR UPDATE/SHARE query and is not the target of UPDATE or DELETE.

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

If the GetForeignRowMarkType pointer is set to NULL, the ROW_MARK_COPY option is always used. (This implies that RefetchForeignRow will never be called, so it need not be provided either.)

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

See Section 59.5 for more information.

void
RefetchForeignRow(EState *estate,
                  ExecRowMark *erm,
                  Datum rowid,
                  TupleTableSlot *slot,
                  bool *updated);

在锁定需要锁定的元组槽后,重新获取该元组槽。estate 是查询的全局执行状态。erm 是描述目标外部表和要获取的行锁类型(如果存在)的 ExecRowMark 结构。rowid 标识要获取的元组。slot 在调用时不包含任何有用的内容,但可用于保存返回的元组。updated 是一个输出参数。

Re-fetch one tuple slot from the foreign table, after locking it if required. estate is global execution state for the query. erm is the ExecRowMark struct describing the target foreign table and the row lock type (if any) to acquire. rowid identifies the tuple to be fetched. slot contains nothing useful upon call, but can be used to hold the returned tuple. updated is an output parameter.

该函数应将元组存储到提供的槽中,或在无法获取行锁时将其清除。要获取的行锁类型由 erm→markType 定义,该类型是 GetForeignRowMarkType 之前返回的值。(ROW_MARK_REFERENCE 意味着仅在不获取任何锁的情况下重新获取元组,此例程永远不会看到 ROW_MARK_COPY。)

This function should store the tuple into the provided slot, or clear it if the row lock couldn’t be obtained. The row lock type to acquire is defined by erm→markType, which is the value previously returned by GetForeignRowMarkType. (ROW_MARK_REFERENCE means to just re-fetch the tuple without acquiring any lock, and ROW_MARK_COPY will never be seen by this routine.)

此外,如果获取的内容是元组的更新版本,而不是之前获取的相同版本,则应将 *updated 设置为 true。(如果 FDW 不能确定这一点,建议始终返回 true。)

In addition, *updated should be set to true if what was fetched was an updated version of the tuple rather than the same version previously obtained. (If the FDW cannot be sure about this, always returning true is recommended.)

请注意,默认情况下,未能获取行锁将导致引发错误;只有在 erm→waitPolicy 指定 SKIP LOCKED 选项时,返回空槽才合适。

Note that by default, failure to acquire a row lock should result in raising an error; returning with an empty slot is only appropriate if the SKIP LOCKED option is specified by erm→waitPolicy.

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

The rowid is the ctid value previously read for the row to be re-fetched. Although the rowid value is passed as a Datum, it can currently only be a tid. The function API is chosen in hopes that it may be possible to allow other data types for row IDs in future.

如果 RefetchForeignRow 指针设置为 NULL,则尝试重新获取行会失败,并出现错误消息。

If the RefetchForeignRow pointer is set to NULL, attempts to re-fetch rows will fail with an error message.

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

See Section 59.5 for more information.

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

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

Recheck that a previously-returned tuple still matches the relevant scan and join qualifiers, and possibly provide a modified version of the tuple. For foreign data wrappers which do not perform join pushdown, it will typically be more convenient to set this to NULL and instead set fdw_recheck_quals appropriately. When outer joins are pushed down, however, it isn’t sufficient to reapply the checks relevant to all the base tables to the result tuple, even if all needed attributes are present, because failure to match some qualifier might result in some attributes going to NULL, rather than in no tuple being returned. RecheckForeignScan can recheck qualifiers and return true if they are still satisfied and false otherwise, but it can also store a replacement tuple into the supplied slot.

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

To implement join pushdown, a foreign data wrapper will typically construct an alternative local join plan which is used only for rechecks; this will become the outer subplan of the ForeignScan. When a recheck is required, this subplan can be executed and the resulting tuple can be stored in the slot. This plan need not be efficient since no base table will return more than one row; for example, it may implement all joins as nested loops. The function GetExistingLocalJoinPath may be used to search existing paths for a suitable local join path, which can be used as the alternative local join plan. GetExistingLocalJoinPath searches for an unparameterized path in the path list of the specified join relation. (If it does not find such a path, it returns NULL, in which case a foreign data wrapper may build the local path by itself or may choose not to create access paths for that join.)

59.2.7. FDW Routines for EXPLAIN #

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

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

Print additional EXPLAIN output for a foreign table scan. This function can call ExplainPropertyText and related functions to add fields to the EXPLAIN output. The flag fields in es can be used to determine what to print, and the state of the ForeignScanState node can be inspected to provide run-time statistics in the EXPLAIN ANALYZE case.

如果 ExplainForeignScan 指针被设置为 NULL,那么在 EXPLAIN 期间不会打印额外信息。

If the ExplainForeignScan pointer is set to NULL, no additional information is printed during EXPLAIN.

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

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

Print additional EXPLAIN output for a foreign table update. This function can call ExplainPropertyText and related functions to add fields to the EXPLAIN output. The flag fields in es can be used to determine what to print, and the state of the ModifyTableState node can be inspected to provide run-time statistics in the EXPLAIN ANALYZE case. The first four arguments are the same as for BeginForeignModify.

如果 ExplainForeignModify 指针被设置为 NULL,那么在 EXPLAIN 期间不会打印额外信息。

If the ExplainForeignModify pointer is set to NULL, no additional information is printed during EXPLAIN.

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

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

Print additional EXPLAIN output for a direct modification on the remote server. This function can call ExplainPropertyText and related functions to add fields to the EXPLAIN output. The flag fields in es can be used to determine what to print, and the state of the ForeignScanState node can be inspected to provide run-time statistics in the EXPLAIN ANALYZE case.

如果 ExplainDirectModify 指针被设置为 NULL,那么在 EXPLAIN 期间不会打印额外信息。

If the ExplainDirectModify pointer is set to NULL, no additional information is printed during EXPLAIN.

59.2.8. FDW Routines for ANALYZE #

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

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

This function is called when ANALYZE is executed on a foreign table. If the FDW can collect statistics for this foreign table, it should return true, and provide a pointer to a function that will collect sample rows from the table in func, plus the estimated size of the table in pages in totalpages. Otherwise, return false.

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

If the FDW does not support collecting statistics for any tables, the AnalyzeForeignTable pointer can be set to NULL.

如果提供,则样本收集函数必须具有签名

If provided, the sample collection function must have the signature

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

应从表中收集多达 targrows 行的随机样本,并存储到调用方提供的 rows 数组中。必须返回实际收集的行数。此外,将表中活跃行和无效行的总数的估计值存储到输出参数 totalrowstotaldeadrows 中。(如果 FDW 没有任何无效行的概念,则将 totaldeadrows 设置为零。)

A random sample of up to targrows rows should be collected from the table and stored into the caller-provided rows array. The actual number of rows collected must be returned. In addition, store estimates of the total numbers of live and dead rows in the table into the output parameters totalrows and totaldeadrows. (Set totaldeadrows to zero if the FDW does not have any concept of dead rows.)

59.2.9. FDW Routines for IMPORT FOREIGN SCHEMA #

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

获取外部表创建命令的列表。在执行 IMPORT FOREIGN SCHEMA 时调用此函数,并为该语句传递解析树以及要使用的外部服务器的 OID。它应该返回一个 C 字符串列表,其中每个字符串都必须包含一个 CREATE FOREIGN TABLE 命令。核心服务器将解析并执行这些字符串。

Obtain a list of foreign table creation commands. This function is called when executing IMPORT FOREIGN SCHEMA, and is passed the parse tree for that statement, as well as the OID of the foreign server to use. It should return a list of C strings, each of which must contain a CREATE FOREIGN TABLE command. These strings will be parsed and executed by the core server.

ImportForeignSchemaStmt struct 中,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 作为数据库对象选项支持的选项有任何关系。

Within the ImportForeignSchemaStmt struct, remote_schema is the name of the remote schema from which tables are to be imported. list_type identifies how to filter table names: FDW_IMPORT_SCHEMA_ALL means that all tables in the remote schema should be imported (in this case table_list is empty), FDW_IMPORT_SCHEMA_LIMIT_TO means to include only tables listed in table_list, and FDW_IMPORT_SCHEMA_EXCEPT means to exclude the tables listed in table_list. options is a list of options used for the import process. The meanings of the options are up to the FDW. For example, an FDW could use an option to define whether the NOT NULL attributes of columns should be imported. These options need not have anything to do with those supported by the FDW as database object options.

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

The FDW may ignore the local_schema field of the ImportForeignSchemaStmt, because the core server will automatically insert that name into the parsed CREATE FOREIGN TABLE commands.

FDW 也不必关心实现 list_typetable_list 指定的过滤,因为核心服务器会自动跳过根据这些选项排除的任何已返回命令。但是,通常最好一开始就避免为排除的表创建命令。函数 IsImportableForeignTable() 可能有助于测试给定外键表名称是否会通过过滤。

The FDW does not have to concern itself with implementing the filtering specified by list_type and table_list, either, as the core server will automatically skip any returned commands for tables excluded according to those options. However, it’s often useful to avoid the work of creating commands for excluded tables in the first place. The function IsImportableForeignTable() may be useful to test whether a given foreign-table name will pass the filter.

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

If the FDW does not support importing table definitions, the ImportForeignSchema pointer can be set to NULL.

59.2.10. FDW Routines for Parallel Execution #

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

A ForeignScan node can, optionally, support parallel execution. A parallel ForeignScan will be executed in multiple processes and must return each row exactly once across all cooperating processes. To do this, processes can coordinate through fixed-size chunks of dynamic shared memory. This shared memory is not guaranteed to be mapped at the same address in every process, so it must not contain pointers. The following functions are all optional, but most are required if parallel execution is to be supported.

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

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

Test whether a scan can be performed within a parallel worker. This function will only be called when the planner believes that a parallel plan might be possible, and should return true if it is safe for that scan to run within a parallel worker. This will generally not be the case if the remote data source has transaction semantics, unless the worker’s connection to the data can somehow be made to share the same transaction context as the leader.

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

If this function is not defined, it is assumed that the scan must take place within the parallel leader. Note that returning true does not mean that the scan itself can be done in parallel, only that the scan can be performed within a parallel worker. Therefore, it can be useful to define this method even when parallel execution is not supported.

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

估计并行操作所需的动态共享内存量。这可能高于实际使用量,但不能低于使用量。返回值以字节为单位。此函数是可选函数,如果不需要,可以省略;但是,如果省略,还必须省略后三个函数,因为不会为 FDW 的使用分配共享内存。

Estimate the amount of dynamic shared memory that will be required for parallel operation. This may be higher than the amount that will actually be used, but it must not be lower. The return value is in bytes. This function is optional, and can be omitted if not needed; but if it is omitted, the next three functions must be omitted as well, because no shared memory will be allocated for the FDW’s use.

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

初始化并行操作所需的动态共享内存。coordinate 指向大小与 EstimateDSMForeignScan 的返回值相等的共享内存区域。此函数是可选的,并且如不必要,可将其省略。

Initialize the dynamic shared memory that will be required for parallel operation. coordinate points to a shared memory area of size equal to the return value of EstimateDSMForeignScan. This function is optional, and can be omitted if not needed.

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

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

Re-initialize the dynamic shared memory required for parallel operation when the foreign-scan plan node is about to be re-scanned. This function is optional, and can be omitted if not needed. Recommended practice is that this function reset only shared state, while the ReScanForeignScan function resets only local state. Currently, this function will be called before ReScanForeignScan, but it’s best not to rely on that ordering.

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

根据 InitializeDSMForeignScan 期间领导设置的共享状态初始化并行工作程序的本地状态。此函数是可选的,并且如不必要,可将其省略。

Initialize a parallel worker’s local state based on the shared state set up by the leader during InitializeDSMForeignScan. This function is optional, and can be omitted if not needed.

void
ShutdownForeignScan(ForeignScanState *node);

当预期不会执行节点至完成时,释放资源。并非在所有情况下都调用此方法;有时,可能在未首先调用此功能的情况下调用 EndForeignScan。由于并在调用此回调后销毁并行查询使用的 DSM 段,因此希望在 DSM 段消失前执行某些操作的外键数据封装程序应实现此方法。

Release resources when it is anticipated the node will not be executed to completion. This is not called in all cases; sometimes, EndForeignScan may be called without this function having been called first. Since the DSM segment used by parallel query is destroyed just after this callback is invoked, foreign data wrappers that wish to take some action before the DSM segment goes away should implement this method.

59.2.11. FDW Routines for Asynchronous Execution #

ForeignScan 节点可以选择支持异步执行,如 src/backend/executor/README 所述。以下函数均为可选的,但如果要支持异步执行,则都需要。

A ForeignScan node can, optionally, support asynchronous execution as described in src/backend/executor/README. The following functions are all optional, but are all required if asynchronous execution is to be supported.

bool
IsForeignPathAsyncCapable(ForeignPath *path);

测试给定的 ForeignPath 路径是否能够异步扫描底层外键关系。仅当给定路径是 AppendPath 路径的子级且规划器认为异步执行可提高性能时,此函数才会在查询规划结束时调用,并且如果给定的路径能够异步扫描外键关系,则应返回 true。

Test whether a given ForeignPath path can scan the underlying foreign relation asynchronously. This function will only be called at the end of query planning when the given path is a direct child of an AppendPath path and when the planner believes that asynchronous execution improves performance, and should return true if the given path is able to scan the foreign relation asynchronously.

如果未定义此函数,则假定给定的路径使用 IterateForeignScan 扫描外键关系。(这意味着将永远不会调用下面描述的回调函数,因此也无需提供它们。)

If this function is not defined, it is assumed that the given path scans the foreign relation using IterateForeignScan. (This implies that the callback functions described below will never be called, so they need not be provided either.)

void
ForeignAsyncRequest(AsyncRequest *areq);

ForeignScan 节点方式异步生成一个元组。areq 是描述 ForeignScan 节点及其父 Append 节点(向其请求元组)的 AsyncRequest 结构。此函数应将元组存储到 areq→result 指定的槽中,并将 areq→request_complete 设置为 true;或如果它需要等待核心服务器外部的事件(例如网络 I/O),并且无法立即生成任何元组,将标志设置为 false,并将 areq→callback_pending 设置为 true,以便 ForeignScan 节点从下面描述的回调函数获得回调。如果没有更多元组可用,则将槽设置为 NULL 或空槽,并将 areq→request_complete 标志设置为 true。建议在 areq 中使用 ExecAsyncRequestDoneExecAsyncRequestPending 设置输出参数。

Produce one tuple asynchronously from the ForeignScan node. areq is the AsyncRequest struct describing the ForeignScan node and the parent Append node that requested the tuple from it. This function should store the tuple into the slot specified by areq→result, and set areq→request_complete to true; or if it needs to wait on an event external to the core server such as network I/O, and cannot produce any tuple immediately, set the flag to false, and set areq→callback_pending to true for the ForeignScan node to get a callback from the callback functions described below. If no more tuples are available, set the slot to NULL or an empty slot, and the areq→request_complete flag to true. It’s recommended to use ExecAsyncRequestDone or ExecAsyncRequestPending to set the output parameters in the areq.

void
ForeignAsyncConfigureWait(AsyncRequest *areq);

配置文件描述符事件,ForeignScan 节点希望等待该事件。仅当 ForeignScan 节点设置了 areq→callback_pending 标志时才调用此函数,并且应将事件添加到 areq 描述的父 Append 节点的 as_eventset。有关更多信息,请参阅 src/backend/executor/execAsync.cExecAsyncConfigureWait 的注释。发生文件描述符事件时,将调用 ForeignAsyncNotify

Configure a file descriptor event for which the ForeignScan node wishes to wait. This function will only be called when the ForeignScan node has the areq→callback_pending flag set, and should add the event to the as_eventset of the parent Append node described by the areq. See the comments for ExecAsyncConfigureWait in src/backend/executor/execAsync.c for additional information. When the file descriptor event occurs, ForeignAsyncNotify will be called.

void
ForeignAsyncNotify(AsyncRequest *areq);

处理已发生的相关事件,然后以 ForeignScan 节点方式异步生成一个元组。此函数应以与 ForeignAsyncRequest 相同的方式在 areq 中设置输出参数。

Process a relevant event that has occurred, then produce one tuple asynchronously from the ForeignScan node. This function should set the output parameters in the areq in the same way as ForeignAsyncRequest.

59.2.12. FDW Routines for Reparameterization of Paths #

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

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

This function is called while converting a path parameterized by the top-most parent of the given child relation child_rel to be parameterized by the child relation. The function is used to reparameterize any paths or translate any expression nodes saved in the given fdw_private member of a ForeignPath. The callback may use reparameterize_path_by_child, adjust_appendrel_attrs or adjust_appendrel_attrs_multilevel as required.