53.4. 外数据封装查询规划

FDW回调函数GetForeignRelSize, GetForeignPaths, GetForeignPlanPlanForeignModify必须适合PostgreSQL规划器的工作。 这有一些他们必须做的说明。

rootbaserel中的信息可以用于减少从外表中(因此降低成本)读取的信息量。 baserel->baserestrictinfo特别有趣,正如它包含应该用于过滤读取的行的限制资格测试(WHERE clauses)。 (FDW本身不需要执行这些测试,正如核心执行者反而可以检查它们。) baserel->reltargetlist可以用于决定需要抓取哪个列;但是注意它仅仅列出通过 ForeignScan规划节点发出的列,不是在质量评估中使用的列也不是查询输出列。

各种私有字段可用于FDW规划函数保持信息。一般来说,无论在FDW私有字段存储什么 应该是palloc,所以它将在规划结束后被回收。

baserel->fdw_private指针,可用于FDW规划函数存储与特定外表相关的 信息。当创建baserel结点时,核心规划器并不接触它除了初始化为NULL外。 从GetForeignRelSizeGetForeignPaths和/或者 GetForeignPathsGetForeignPlan传递信息是非常有用的, 从而避免重复计算。

GetForeignPaths可以通过在ForeignPath节点的 fdw_private字段存储私有信息标识不同访问路径的含义。 作为List指针声明fdw_private,但是实际上可以包含任何由于 核心规划器没有接触的东西。然而,最好办法是通过nodeToString可倾式表示形式, 用于调试后端可用支持。

GetForeignPlan可以检查所选的ForeignPath节点的 fdw_private字段,并且可以产生放在ForeignScan 规划节点中的fdw_exprsfdw_private列表, 在执行时间他们可用。这些列表必须在copyObject知道如何拷贝的形式中被表示。 fdw_private列表没有其他限制,并且不能通过任何方式的核心后端进行解释。 fdw_exprs列表如果不是零,那么希望包含在运行时执行的表达式树。 这些树将通过规划器使得它们完全可执行进行后处理。

GetForeignPlan中,往往目标列表可以拷贝到规划节点中。 已通过的scan_clauses列表包含和baserel->baserestrictinfo相同分句, 但是为了更好的执行效率可能重新排序。 在简单情况下FDW可以从scan_clauses列表(使用extract_actual_clauses)中删除RestrictInfo节点, 并且将所有分句放入规划节点的资格列表中,这意味着在运行期间通过执行器将检查所有分句。 更多复杂的FDW可能检查一些内部分句,在这种情况下那些分句可以从规划节点的列表中删除,以便执行器 不会浪费复查它们的时间。

作为一个例子,FDW可能标识一些来自foreign_variable = sub_expression的限制分句,它决定了在给定sub_expression的本地评估值的 远程服务器上被执行。在GetForeignPaths期间产生了该分句的实际标识, 因为它会影响路径的成本估算。路径的fdw_private字段可能包含一个指向 已标识分句的RestrictInfo节点的指针。然后GetForeignPlanscan_clauses中 删除该分句,但是增加sub_expressionfdw_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中, 然后运行时该情况与普通限制分句一样运行。

当规划一个UPDATE或者DELETE的时候, PlanForeignModify可以查找外表的RelOptInfo结构, 并且充分使用通过扫描规划函数预先创建的baserel->fdw_private数据。 然而,在INSERT中不扫描目标表,所以没有RelOptInfo。 通过PlanForeignModify返回的ListForeignScan规划节点 的fdw_private列表有相同的限制, 即它必须包含copyObject知道如何拷贝的结构。

UPDATE或者DELETE反对外部数据源支持并发更新,推荐 ForeignScan操作锁定抓取的行,也许通过SELECT FOR UPDATE的等价物。 当在SELECT FOR UPDATE/SHARE引用外表时,FDW可能也会在抓取时选择锁定行。 如果没有做,就有关外表来说, 那么FOR UPDATE或者FOR SHARE选项本质上是无操作的。 这种操作可能会产生本地表操作上轻微的语义差异,行锁定通常尽可能的延迟: 远程行可能获得锁定即使他们随后没有本地应用限制或者连接条件。 然而,匹配局部语义确实需要每行的额外远程访问,并且不可能依赖于 外部数据源提供的锁定语义内容。