选择 打开 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
测试与开发版本: 19 / devel
不受支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2
历史版本PostgreSQL 9.3 已于 2018 年 11 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本

52.4. 外部数据包装器查询规划 #

FDW 回调函数GetForeignRelSizeGetForeignPathsGetForeignPlanPlanForeignModify必须适应PostgreSQL规划器的工作方式。下面给出一些关于它们必须做什么的说明。

rootbaserel 中的信息可用于减少必须从外部表获取的信息量(从而降低代价)。baserel->baserestrictinfo 特别重要,因为它包含限制条件(WHERE 子句),这些条件应当用来过滤待获取的行。(FDW 本身并不一定非要强制这些条件,因为核心执行器也可以检查它们。)baserel->reltargetlist 可用于确定需要取回哪些列;但要注意,它只列出必须由 ForeignScan 计划节点输出的列,而不包括在条件求值中使用但不在查询结果中输出的列。

FDW 规划函数可以利用多个私有字段来保存信息。通常,存储在 FDW 私有字段中的任何内容都应通过 palloc 分配,这样它们会在规划结束时被回收。

baserel->fdw_private 是一个 void 指针,FDW 规划函数可以用它来存储与特定外部表相关的信息。核心规划器除了在创建 baserel 节点时把它初始化为 NULL 之外,不会碰它。它非常适合把信息从GetForeignRelSize传给GetForeignPaths,以及/或者从GetForeignPaths传给GetForeignPlan,从而避免重复计算。

GetForeignPaths 可以通过把私有信息存储在 ForeignPath 节点的 fdw_private 字段中,来标识不同访问路径的含义。fdw_private 被声明为 List 指针,但实际上可以包含任何内容,因为核心规划器不会碰它。不过,最佳实践是使用一种能够被 nodeToString 导出的表示形式,以便利用后端提供的调试支持。

GetForeignPlan 可以检查所选中 ForeignPath 节点的 fdw_private 字段,并生成 fdw_exprsfdw_private 列表,放入 ForeignScan 计划节点中,以供执行期使用。这两个列表都必须采用 copyObject 能够复制的表示形式。fdw_private 列表没有其他限制,核心后端也不会以任何方式解释它。如果 fdw_exprs 列表不是 NIL,则它应包含打算在运行时执行的表达式树。这些树会经过规划器的后处理,变成完全可执行的形式。

GetForeignPlan中,通常可以把传入的目标列表原样复制到计划节点中。传入的 scan_clauses 列表与 baserel->baserestrictinfo 包含相同的子句,但可能会为了更高的执行效率而重新排序。在简单情况下,FDW 可以仅从 scan_clauses 列表中剥离 RestrictInfo 节点(使用extract_actual_clauses),并把所有子句都放到计划节点的条件列表中,这意味着所有子句都将在运行时由执行器检查。更复杂的 FDW 可能能够在内部检查某些子句,这种情况下可将这些子句从计划节点的条件列表中移除,从而避免执行器重复检查。

举例来说,FDW 可能会识别出某些形如 foreign_variable = sub_expression 的限制子句,并判断它们可以利用本地计算出的 sub_expression 值在远程服务器上执行。这类子句的识别应当在GetForeignPaths期间完成,因为它会影响该路径的代价估计。路径的 fdw_private 字段很可能会包含指向已识别子句的 RestrictInfo 节点的指针。然后,GetForeignPlan 会把该子句从 scan_clauses 中移除,但会把 sub_expression 加入 fdw_exprs,以确保它被整理成可执行形式。它还很可能会把控制信息放入计划节点的 fdw_private 字段中,用以告诉执行函数在运行时该做什么。传给远程服务器的查询将会包含类似 WHERE foreign_variable = $1 这样的条件,其中参数值在运行时通过求值 fdw_exprs 表达式树获得。

FDW 应当始终至少构造一条仅依赖于表限制子句的路径。在连接查询中,它还可能选择构造依赖连接子句的路径,例如 foreign_variable = local_variable。这类子句不会出现在 baserel->baserestrictinfo 中,而必须在关系的连接列表中查找。使用这类子句的路径被称为参数化路径。它必须使用适当的 param_info 值来标识被选中连接子句中使用到的其他关系;可使用get_baserel_parampathinfo 来计算该值。在GetForeignPlan中,连接子句中的 local_variable 部分会被加入 fdw_exprs,之后在运行时的处理方式就与普通限制子句相同。

在规划 UPDATEDELETE 时,PlanForeignModify 可以查找外部表的 RelOptInfo 结构体,并利用先前由扫描规划函数创建的 baserel->fdw_private 数据。不过在 INSERT 中,目标表不会被扫描,因此不存在对应的 RelOptInfoPlanForeignModify 返回的 ListForeignScan 计划节点的 fdw_private 列表有相同的限制,也就是说,它只能包含 copyObject 知道如何复制的结构体。

对于支持并发更新的外部数据源上的 UPDATEDELETE,建议 ForeignScan 操作对它取得的所有行加锁,例如通过相当于 SELECT FOR UPDATE 的方式。当外部表出现在 SELECT FOR UPDATE/SHARE 中时,FDW 也可以选择在取行时加锁;如果不这样做,就外部表而言,FOR UPDATEFOR SHARE 选项实际上不起作用。这样得到的语义可能与本地表上的操作略有不同,本地表上的行锁定通常尽量延迟:某些远程行可能虽然随后被本地施加的限制条件或连接条件排除,却仍被加了锁。不过,要与本地语义完全一致,就需要为每一行额外访问一次远程数据源,而且根据外部数据源提供的锁定语义,这甚至可能根本无法做到。

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。