FDW 回调函数 GetForeignRelSize
、GetForeignPaths
、GetForeignPlan
、PlanForeignModify
、GetForeignJoinPaths
、GetForeignUpperPaths
和 PlanDirectModify
必须能够融入 PostgreSQL 规划器的运作中。以下是一些关于它们必须做什么的说明。
可以使用 root
和 baserel
中的信息来减少必须从外部表中获取的信息量(从而降低成本)。baserel->baserestrictinfo
尤其有趣,因为它包含应该用于过滤要获取的行的限制条件(WHERE
子句)。(FDW 本身不需要强制执行这些条件,因为核心执行器可以改为检查它们。)baserel->reltarget->exprs
可用于确定需要获取哪些列;但请注意,它仅列出必须由 ForeignScan
计划节点发出的列,而不包括在条件评估中使用但未由查询输出的列。
各种私有字段可供 FDW 规划函数用于保留信息。通常,您在 FDW 私有字段中存储的任何内容都应该被 palloc'd,以便在规划结束时被回收。
baserel->fdw_private
是一个 void
指针,可供 FDW 规划函数存储与特定外部表相关的信息。核心规划器除了在创建 RelOptInfo
节点时将其初始化为 NULL 外,不会触及它。它对于将信息从 GetForeignRelSize
传递到 GetForeignPaths
和/或从 GetForeignPaths
传递到 GetForeignPlan
非常有用,从而避免重新计算。
GetForeignPaths
可以通过在 ForeignPath
节点的 fdw_private
字段中存储私有信息来识别不同访问路径的含义。fdw_private
被声明为一个 List
指针,但实际上可以包含任何内容,因为核心规划器不会触及它。但是,最佳实践是使用一个可由 nodeToString
转储的表示形式,以便与后端中可用的调试支持一起使用。
GetForeignPlan
可以检查选定的 ForeignPath
节点的 fdw_private
字段,并可以生成 fdw_exprs
和 fdw_private
列表,将它们放入 ForeignScan
计划节点,在那里它们将在执行时可用。这两个列表都必须以 copyObject
知道如何复制的形式表示。fdw_private
列表没有其他限制,并且不会被核心后端以任何方式解释。fdw_exprs
列表(如果不是 NIL)应包含打算在运行时执行的表达式树。这些树将经过规划器的后处理,使其完全可执行。
在 GetForeignPlan
中,通常可以将传入的目标列表原样复制到计划节点中。传入的 scan_clauses
列表包含与 baserel->baserestrictinfo
相同的子句,但可能会重新排序以提高执行效率。在简单的情况下,FDW 可以直接从 scan_clauses
列表(使用 extract_actual_clauses
)中剥离 RestrictInfo
节点,并将所有子句放入计划节点的 qual 列表中,这意味着所有子句将在运行时由执行器检查。更复杂的 FDW 可能能够内部检查其中一些子句,在这种情况下,可以从计划节点的 qual 列表中删除这些子句,以避免执行器浪费时间重新检查它们。
例如,FDW 可能会识别一些形式为 foreign_variable
=
sub_expression
的限制子句,它确定在给定本地评估的 sub_expression
值的情况下,可以在远程服务器上执行这些子句。对此类子句的实际识别应该在 GetForeignPaths
期间发生,因为它会影响路径的成本估计。路径的 fdw_private
字段可能包括指向已识别子句的 RestrictInfo
节点的指针。然后 GetForeignPlan
将该子句从 scan_clauses
中删除,但将 sub_expression
添加到 fdw_exprs
中,以确保它被处理成可执行的形式。它可能还会将控制信息放入计划节点的 fdw_private
字段,以告知执行函数在运行时做什么。发送到远程服务器的查询将涉及类似 WHERE
的内容,其中参数值将在运行时从 foreign_variable
= $1fdw_exprs
表达式树的评估中获得。
从计划节点 qual 列表中删除的任何子句必须改而添加到 fdw_recheck_quals
中,或由 RecheckForeignScan
重新检查,以确保在 READ COMMITTED
隔离级别下的正确行为。当查询涉及的另一个表发生并发更新时,执行器可能需要验证原始子句是否仍然满足该元组,可能针对一组不同的参数值。使用 fdw_recheck_quals
通常比在 RecheckForeignScan
中实现检查更容易,但当外部连接被下推时,此方法将不足够,因为在这种情况下,连接元组的某些字段可能变为 NULL 而不完全拒绝该元组。
FDW 可以填充的另一个 ForeignScan
字段是 fdw_scan_tlist
,它描述了 FDW 为该计划节点返回的元组。对于简单的外部表扫描,这可以设置为 NIL
,这意味着返回的元组具有为外部表声明的行类型。非 NIL
值必须是一个目标列表(TargetEntry
s 的列表),包含代表返回列的 Vars 和/或表达式。这可以用于,例如,表明 FDW 已省略了它注意到查询不需要的某些列。另外,如果 FDW 可以比本地更便宜地计算查询使用的表达式,它可以将这些表达式添加到 fdw_scan_tlist
。请注意,连接计划(由 GetForeignJoinPaths
创建的路径生成)必须始终提供 fdw_scan_tlist
来描述它们将返回的列集。
FDW 应始终构造至少一个仅依赖于表限制子句的路径。在连接查询中,它还可以选择构造依赖于连接子句的路径,例如 foreign_variable
=
local_variable
。此类子句不会出现在 baserel->baserestrictinfo
中,但必须在关系的连接列表中查找。使用此类子句的路径称为“参数化路径”。它必须使用 param_info
的适当值来识别选定连接子句中使用的其他关系;使用 get_baserel_parampathinfo
来计算该值。在 GetForeignPlan
中,连接子句的 local_variable
部分将被添加到 fdw_exprs
中,然后在运行时,该情况与普通限制子句的情况相同。
如果 FDW 支持远程连接,GetForeignJoinPaths
应该以与 GetForeignPaths
对基本表的操作方式类似的方式为潜在的远程连接生成 ForeignPath
。有关预期连接的信息可以像上面描述的那样传递给 GetForeignPlan
。但是,baserestrictinfo
对于连接关系无关紧要;相反,特定连接的相关连接子句作为单独的参数(extra->restrictlist
)传递给 GetForeignJoinPaths
。
FDW 可能还支持对扫描和连接级别之上的某些计划操作的直接执行,例如分组或聚合。为了提供此类选项,FDW 应生成路径并将其插入到适当的上层关系中。例如,代表远程聚合的路径应使用 add_path
插入到 UPPERREL_GROUP_AGG
关系中。这条路径将在成本基础上与通过读取外部关系(请注意,必须也提供这样的路径,否则计划时会出错)的简单扫描路径进行的本地聚合进行比较。如果远程聚合路径获胜(通常情况如此),它将通过调用 GetForeignPlan
以通常的方式转换为计划。建议的生成此类路径的位置是在 GetForeignUpperPaths
回调函数中,该函数在每个上层关系(即每个扫描/连接后处理步骤)被调用,前提是查询的所有基本关系都来自同一个 FDW。
PlanForeignModify
和 58.2.4 节中描述的其他回调函数的设计假设外部关系将以常规方式进行扫描,然后将单个行更新由本地 ModifyTable
计划节点驱动。这种方法对于需要读取本地表和外部表的一般情况是必要的。但是,如果操作可以完全由外部服务器执行,FDW 可以生成代表该操作的路径并将其插入到 UPPERREL_FINAL
上层关系中,在那里它将与 ModifyTable
方法竞争。这种方法也可以用于实现远程 SELECT FOR UPDATE
,而不是使用 58.2.6 节中描述的行锁定回调。请记住,插入到 UPPERREL_FINAL
的路径负责实现查询的所有行为。
在规划 UPDATE
或 DELETE
时,PlanForeignModify
和 PlanDirectModify
可以查找外部表的 RelOptInfo
结构,并利用扫描规划函数之前创建的 baserel->fdw_private
数据。但是,在 INSERT
中,目标表不会被扫描,因此没有它的 RelOptInfo
。PlanForeignModify
返回的 List
与 ForeignScan
计划节点的 fdw_private
列表具有相同的限制,即它只能包含 copyObject
知道如何复制的结构。
INSERT
语句加上 ON CONFLICT
子句不支持指定冲突目标,因为远程表上的唯一约束或排除约束在本地是未知的。这反过来也意味着 ON CONFLICT DO UPDATE
不被支持,因为在那里必须指定冲突目标。
如果您在文档中发现任何不正确之处、与您使用该特定功能时的经验不符之处或需要进一步澄清之处,请使用 此表单 来报告文档问题。