Redrock Postgres 搜索 英文
版本: 9.3 / 9.4 / 9.5 / 9.6 / 10 / 11 / 12 / 13 / 14 / 15 / 16

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

FDW回调函数GetForeignRelSizeGetForeignPathsGetForeignPlanPlanForeignModify以及GetForeignJoinPaths必须适合PostgreSQL规划器的工作。 这里有一些关于它们必须做什么的注记。

rootbaserel中的信息可以被用来减少必须从外部表获得的信息量(并且因此降低代价)。baserel->baserestrictinfo是特别有趣的,因为它包含限制条件(WHERE)子句,它应该被用来过滤要被获取的行(FDW本身并不要求强制这些条件,因为核心执行器可以检查它们)。baserel->reltargetlist可以被用来决定哪些类需要被获取;但是注意它仅列出了ForeignScan计划节点所发出的列,不包含在条件计算中使用但并不被查询输出的列。

有多个私有域可以给FDW规划函数来保存信息。通常,不管你存储什么在FDW私有域中,它们都应该被palloc,这样它会在规划结束时被回收。

baserel->fdw_private是一个void指针,它可以被FDW规划函数用来存储与特定外部表相关的信息。核心规划器不会碰它除非当RelOptInfo节点被创建时把它初始化为NULL。它对从GetForeignRelSize传递信息给GetForeignPaths和/或从GetForeignPaths传递信息给GetForeignPlan非常有用,这样避免了重新计算。

GetForeignPaths可以通过在ForeignPath节点的fdw_private域中存储私有信息来标识不同的访问路径。fdw_private被声明为一个List指针,但是可能实际上包含任何东西,因为规划器不会触碰它。但是,最好是使用一种nodeToString可导出的形式,这样在后端可以用于调试支持。

GetForeignPlan可以检查选中的ForeignPath节点的fdw_private域,并且可以生成被放置于ForeignPath计划节点中的fdw_exprsfdw_private列表。这两个列表必须被表示为一种copyObject可复制的形式。fdw_private列表没有任何其他限制并且不会被核心后端以任何形式解释。非 NIL 的fdw_exprs应该包含表达式树,该树会在运行时被执行。这些树将由规划器在后期处理,以便让它们变成完全可执行的。

GetForeignPlan中,通常被传入的目标列表可以被照样复制到计划节点中。被传入的scan_clauses 列表包含和baserel->baserestrictinfo相同的子句,但是可能为了更好的执行效率会被重新排序。在简单情况下,FDW可以只把RestrictInfo节点从scan_clauses 列表剥离(使用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_recheck_quals中, 为了确保读取已提交隔离级别中正确的操作RecheckForeignScan重新检查。 在查询中涉及的一些其它表中发生并行更新的时候, 执行器可能需要验证所有最初的质量还是满足元组的, 可能是针对一组不同的参数值。 使用fdw_recheck_quals通常比在RecheckForeignScan中实现检查更容易, 当外部链接下推的时候,这种方法不充分。因为链接元组在这种情况下可能有一些字段为空而不会完全拒绝元组。

另一个通过FDW填充的ForeignScan字段是fdw_scan_tlist, 它描述了为计划节点通过FDW返回的元组。 对于简单的外表扫描,可以设置为NIL, 意味着返回的元组有用于外表的行类型声明。 非NIL值必须是包含变量的目标列表 (TargetEntry列)和/或表示返回列的表达式。 可能会使用,比如,显示了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 为潜在的远程连接生成ForeignPath与作为基表的GetForeignPaths一样。 有关预期连接的信息以上述相同的方式传递给GetForeignPlan。 然而,baserestrictinfo是不相关的连接关系; 相反,一个特定连接的相关连接子句作为单独的参数(extra->restrictlist) 传递给函数GetForeignJoinPaths

在规划一个UPDATEDELETE时,PlanForeignModify能为外部表查找RelOptInfo结构,并利用之前由扫描规划函数创建的baserel->fdw_private数据。但是,在INSERT中目标表不会被扫描,因此不会有它的RelOptInfo。由PlanForeignModify返回的List具有和ForeignScan计划节点的fdw_private列表相同的限制,即它必须只包含copyObject知道怎么拷贝的结构。

带有ON CONFLICTINSERT子句不支持声明冲突目标, 作为唯一的约束或远程表上的排除约束不是本地已知的。 这反过来意味着不支持ON CONFLICT DO UPDATE, 因为该规范是强制性的。