@@ -168,7 +168,8 @@ GetForeignPlan (PlannerInfo *root,
168168 Oid foreigntableid,
169169 ForeignPath *best_path,
170170 List *tlist,
171- List *scan_clauses);
171+ List *scan_clauses,
172+ Plan *outer_plan);
172173</programlisting>
173174
174175 Create a <structname>ForeignScan</> plan node from the selected foreign
@@ -765,6 +766,35 @@ RefetchForeignRow (EState *estate,
765766 See <xref linkend="fdw-row-locking"> for more information.
766767 </para>
767768
769+ <para>
770+ <programlisting>
771+ bool
772+ RecheckForeignScan (ForeignScanState *node, TupleTableSlot *slot);
773+ </programlisting>
774+ Recheck that a previously-returned tuple still matches the relevant
775+ scan and join qualifiers, and possibly provide a modified version of
776+ the tuple. For foreign data wrappers which do not perform join pushdown,
777+ it will typically be more convenient to set this to <literal>NULL</> and
778+ instead set <structfield>fdw_recheck_quals</structfield> appropriately.
779+ When outer joins are pushed down, however, it isn't sufficient to
780+ reapply the checks relevant to all the base tables to the result tuple,
781+ even if all needed attributes are present, because failure to match some
782+ qualifier might result in some attributes going to NULL, rather than in
783+ no tuple being returned. <literal>RecheckForeignScan</> can recheck
784+ qualifiers and return true if they are still satisfied and false
785+ otherwise, but it can also store a replacement tuple into the supplied
786+ slot.
787+ </para>
788+
789+ <para>
790+ To implement join pushdown, a foreign data wrapper will typically
791+ construct an alternative local join plan which is used only for
792+ rechecks; this will become the outer subplan of the
793+ <literal>ForeignScan</>. When a recheck is required, this subplan
794+ can be executed and the resulting tuple can be stored in the slot.
795+ This plan need not be efficient since no base table will return more
796+ than one row; for example, it may implement all joins as nested loops.
797+ </para>
768798 </sect2>
769799
770800 <sect2 id="fdw-callbacks-explain">
@@ -1137,11 +1167,17 @@ GetForeignServerByName(const char *name, bool missing_ok);
11371167
11381168 <para>
11391169 Any clauses removed from the plan node's qual list must instead be added
1140- to <literal>fdw_recheck_quals</> in order to ensure correct behavior
1170+ to <literal>fdw_recheck_quals</> or rechecked by
1171+ <literal>RecheckForeignScan</> in order to ensure correct behavior
11411172 at the <literal>READ COMMITTED</> isolation level. When a concurrent
11421173 update occurs for some other table involved in the query, the executor
11431174 may need to verify that all of the original quals are still satisfied for
1144- the tuple, possibly against a different set of parameter values.
1175+ the tuple, possibly against a different set of parameter values. Using
1176+ <literal>fdw_recheck_quals</> is typically easier than implementing checks
1177+ inside <literal>RecheckForeignScan</>, but this method will be
1178+ insufficient when outer joins have been pushed down, since the join tuples
1179+ in that case might have some fields go to NULL without rejecting the
1180+ tuple entirely.
11451181 </para>
11461182
11471183 <para>
0 commit comments