Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit325b357

Browse files
committed
Allow foreign and custom joins to handle EvalPlanQual rechecks.
Commite7cb7ee provided basicinfrastructure for allowing a foreign data wrapper or custom scanprovider to replace a join of one or more tables with a scan.However, this infrastructure failed to take into account the needfor possible EvalPlanQual rechecks, and ExecScanFetch would failan assertion (or just overwrite memory) if such a check was attemptedfor a plan containing a pushed-down join. To fix, adjust the EPQmachinery to skip some processing steps when scanrelid == 0, makingthose the responsibility of scan's recheck method, which also hasthe responsibility in this case of correctly populating the relevantslot.To allow foreign scans to gain control in the right place to makeuse of this new facility, add a new, optional RecheckForeignScanmethod. Also, allow a foreign scan to have a child plan, which canbe used to correctly populate the slot (or perhaps for somethingelse, but this is the only use currently envisioned).KaiGai Kohei, reviewed by Robert Haas, Etsuro Fujita, and KyotaroHoriguchi.
1 parent25517ee commit325b357

File tree

12 files changed

+149
-18
lines changed

12 files changed

+149
-18
lines changed

‎contrib/file_fdw/file_fdw.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ static ForeignScan *fileGetForeignPlan(PlannerInfo *root,
121121
Oidforeigntableid,
122122
ForeignPath*best_path,
123123
List*tlist,
124-
List*scan_clauses);
124+
List*scan_clauses,
125+
Plan*outer_plan);
125126
staticvoidfileExplainForeignScan(ForeignScanState*node,ExplainState*es);
126127
staticvoidfileBeginForeignScan(ForeignScanState*node,inteflags);
127128
staticTupleTableSlot*fileIterateForeignScan(ForeignScanState*node);
@@ -525,6 +526,7 @@ fileGetForeignPaths(PlannerInfo *root,
525526
total_cost,
526527
NIL,/* no pathkeys */
527528
NULL,/* no outer rel either */
529+
NULL,/* no extra plan */
528530
coptions));
529531

530532
/*
@@ -544,7 +546,8 @@ fileGetForeignPlan(PlannerInfo *root,
544546
Oidforeigntableid,
545547
ForeignPath*best_path,
546548
List*tlist,
547-
List*scan_clauses)
549+
List*scan_clauses,
550+
Plan*outer_plan)
548551
{
549552
Indexscan_relid=baserel->relid;
550553

@@ -564,7 +567,8 @@ fileGetForeignPlan(PlannerInfo *root,
564567
NIL,/* no expressions to evaluate */
565568
best_path->fdw_private,
566569
NIL,/* no custom tlist */
567-
NIL/* no remote quals */ );
570+
NIL,/* no remote quals */
571+
outer_plan);
568572
}
569573

570574
/*

‎contrib/postgres_fdw/postgres_fdw.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,8 @@ static ForeignScan *postgresGetForeignPlan(PlannerInfo *root,
245245
Oidforeigntableid,
246246
ForeignPath*best_path,
247247
List*tlist,
248-
List*scan_clauses);
248+
List*scan_clauses,
249+
Plan*outer_plan);
249250
staticvoidpostgresBeginForeignScan(ForeignScanState*node,inteflags);
250251
staticTupleTableSlot*postgresIterateForeignScan(ForeignScanState*node);
251252
staticvoidpostgresReScanForeignScan(ForeignScanState*node);
@@ -560,6 +561,7 @@ postgresGetForeignPaths(PlannerInfo *root,
560561
fpinfo->total_cost,
561562
NIL,/* no pathkeys */
562563
NULL,/* no outer rel either */
564+
NULL,/* no extra plan */
563565
NIL);/* no fdw_private list */
564566
add_path(baserel, (Path*)path);
565567

@@ -727,6 +729,7 @@ postgresGetForeignPaths(PlannerInfo *root,
727729
total_cost,
728730
NIL,/* no pathkeys */
729731
param_info->ppi_req_outer,
732+
NULL,
730733
NIL);/* no fdw_private list */
731734
add_path(baserel, (Path*)path);
732735
}
@@ -742,7 +745,8 @@ postgresGetForeignPlan(PlannerInfo *root,
742745
Oidforeigntableid,
743746
ForeignPath*best_path,
744747
List*tlist,
745-
List*scan_clauses)
748+
List*scan_clauses,
749+
Plan*outer_plan)
746750
{
747751
PgFdwRelationInfo*fpinfo= (PgFdwRelationInfo*)baserel->fdw_private;
748752
Indexscan_relid=baserel->relid;
@@ -882,7 +886,8 @@ postgresGetForeignPlan(PlannerInfo *root,
882886
params_list,
883887
fdw_private,
884888
NIL,/* no custom tlist */
885-
remote_exprs);
889+
remote_exprs,
890+
outer_plan);
886891
}
887892

888893
/*

‎doc/src/sgml/fdwhandler.sgml

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
that 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>

‎src/backend/executor/execScan.c

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,21 @@ ExecScanFetch(ScanState *node,
4949
*/
5050
Indexscanrelid= ((Scan*)node->ps.plan)->scanrelid;
5151

52-
Assert(scanrelid>0);
53-
if (estate->es_epqTupleSet[scanrelid-1])
52+
if (scanrelid==0)
53+
{
54+
TupleTableSlot*slot=node->ss_ScanTupleSlot;
55+
56+
/*
57+
* This is a ForeignScan or CustomScan which has pushed down a
58+
* join to the remote side. The recheck method is responsible not
59+
* only for rechecking the scan/join quals but also for storing
60+
* the correct tuple in the slot.
61+
*/
62+
if (!(*recheckMtd) (node,slot))
63+
ExecClearTuple(slot);/* would not be returned by scan */
64+
returnslot;
65+
}
66+
elseif (estate->es_epqTupleSet[scanrelid-1])
5467
{
5568
TupleTableSlot*slot=node->ss_ScanTupleSlot;
5669

@@ -347,8 +360,31 @@ ExecScanReScan(ScanState *node)
347360
{
348361
Indexscanrelid= ((Scan*)node->ps.plan)->scanrelid;
349362

350-
Assert(scanrelid>0);
363+
if (scanrelid>0)
364+
estate->es_epqScanDone[scanrelid-1]= false;
365+
else
366+
{
367+
Bitmapset*relids;
368+
intrtindex=-1;
351369

352-
estate->es_epqScanDone[scanrelid-1]= false;
370+
/*
371+
* If an FDW or custom scan provider has replaced the join with a
372+
* scan, there are multiple RTIs; reset the epqScanDone flag for
373+
* all of them.
374+
*/
375+
if (IsA(node->ps.plan,ForeignScan))
376+
relids= ((ForeignScan*)node->ps.plan)->fs_relids;
377+
elseif (IsA(node->ps.plan,CustomScan))
378+
relids= ((CustomScan*)node->ps.plan)->custom_relids;
379+
else
380+
elog(ERROR,"unexpected scan node: %d",
381+
(int)nodeTag(node->ps.plan));
382+
383+
while ((rtindex=bms_next_member(relids,rtindex)) >=0)
384+
{
385+
Assert(rtindex>0);
386+
estate->es_epqScanDone[rtindex-1]= false;
387+
}
388+
}
353389
}
354390
}

‎src/backend/executor/nodeForeignscan.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ ForeignNext(ForeignScanState *node)
7373
staticbool
7474
ForeignRecheck(ForeignScanState*node,TupleTableSlot*slot)
7575
{
76+
FdwRoutine*fdwroutine=node->fdwroutine;
7677
ExprContext*econtext;
7778

7879
/*
@@ -85,6 +86,18 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
8586

8687
ResetExprContext(econtext);
8788

89+
/*
90+
* If an outer join is pushed down, RecheckForeignScan may need to store a
91+
* different tuple in the slot, because a different set of columns may go
92+
* to NULL upon recheck. Otherwise, it shouldn't need to change the slot
93+
* contents, just return true or false to indicate whether the quals still
94+
* pass. For simple cases, setting fdw_recheck_quals may be easier than
95+
* providing this callback.
96+
*/
97+
if (fdwroutine->RecheckForeignScan&&
98+
!fdwroutine->RecheckForeignScan(node,slot))
99+
return false;
100+
88101
returnExecQual(node->fdw_recheck_quals,econtext, false);
89102
}
90103

@@ -205,6 +218,11 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
205218
scanstate->fdwroutine=fdwroutine;
206219
scanstate->fdw_state=NULL;
207220

221+
/* Initialize any outer plan. */
222+
if (outerPlan(node))
223+
outerPlanState(scanstate)=
224+
ExecInitNode(outerPlan(node),estate,eflags);
225+
208226
/*
209227
* Tell the FDW to initialize the scan.
210228
*/
@@ -225,6 +243,10 @@ ExecEndForeignScan(ForeignScanState *node)
225243
/* Let the FDW shut down */
226244
node->fdwroutine->EndForeignScan(node);
227245

246+
/* Shut down any outer plan. */
247+
if (outerPlanState(node))
248+
ExecEndNode(outerPlanState(node));
249+
228250
/* Free the exprcontext */
229251
ExecFreeExprContext(&node->ss.ps);
230252

@@ -246,7 +268,17 @@ ExecEndForeignScan(ForeignScanState *node)
246268
void
247269
ExecReScanForeignScan(ForeignScanState*node)
248270
{
271+
PlanState*outerPlan=outerPlanState(node);
272+
249273
node->fdwroutine->ReScanForeignScan(node);
250274

275+
/*
276+
* If chgParam of subnode is not null then plan will be re-scanned by
277+
* first ExecProcNode. outerPlan may also be NULL, in which case there
278+
* is nothing to rescan at all.
279+
*/
280+
if (outerPlan!=NULL&&outerPlan->chgParam==NULL)
281+
ExecReScan(outerPlan);
282+
251283
ExecScanReScan(&node->ss);
252284
}

‎src/backend/nodes/outfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1665,6 +1665,7 @@ _outForeignPath(StringInfo str, const ForeignPath *node)
16651665

16661666
_outPathInfo(str, (constPath*)node);
16671667

1668+
WRITE_NODE_FIELD(fdw_outerpath);
16681669
WRITE_NODE_FIELD(fdw_private);
16691670
}
16701671

‎src/backend/optimizer/plan/createplan.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2059,11 +2059,16 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
20592059
Indexscan_relid=rel->relid;
20602060
Oidrel_oid=InvalidOid;
20612061
Bitmapset*attrs_used=NULL;
2062+
Plan*outer_plan=NULL;
20622063
ListCell*lc;
20632064
inti;
20642065

20652066
Assert(rel->fdwroutine!=NULL);
20662067

2068+
/* transform the child path if any */
2069+
if (best_path->fdw_outerpath)
2070+
outer_plan=create_plan_recurse(root,best_path->fdw_outerpath);
2071+
20672072
/*
20682073
* If we're scanning a base relation, fetch its OID. (Irrelevant if
20692074
* scanning a join relation.)
@@ -2093,7 +2098,8 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
20932098
*/
20942099
scan_plan=rel->fdwroutine->GetForeignPlan(root,rel,rel_oid,
20952100
best_path,
2096-
tlist,scan_clauses);
2101+
tlist,scan_clauses,
2102+
outer_plan);
20972103

20982104
/* Copy cost data from Path to Plan; no need to make FDW do this */
20992105
copy_path_costsize(&scan_plan->scan.plan,&best_path->path);
@@ -3706,15 +3712,16 @@ make_foreignscan(List *qptlist,
37063712
List*fdw_exprs,
37073713
List*fdw_private,
37083714
List*fdw_scan_tlist,
3709-
List*fdw_recheck_quals)
3715+
List*fdw_recheck_quals,
3716+
Plan*outer_plan)
37103717
{
37113718
ForeignScan*node=makeNode(ForeignScan);
37123719
Plan*plan=&node->scan.plan;
37133720

37143721
/* cost will be filled in by create_foreignscan_plan */
37153722
plan->targetlist=qptlist;
37163723
plan->qual=qpqual;
3717-
plan->lefttree=NULL;
3724+
plan->lefttree=outer_plan;
37183725
plan->righttree=NULL;
37193726
node->scan.scanrelid=scanrelid;
37203727
/* fs_server will be filled in by create_foreignscan_plan */

‎src/backend/optimizer/util/pathnode.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,6 +1462,7 @@ create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
14621462
doublerows,Coststartup_cost,Costtotal_cost,
14631463
List*pathkeys,
14641464
Relidsrequired_outer,
1465+
Path*fdw_outerpath,
14651466
List*fdw_private)
14661467
{
14671468
ForeignPath*pathnode=makeNode(ForeignPath);
@@ -1475,6 +1476,7 @@ create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
14751476
pathnode->path.total_cost=total_cost;
14761477
pathnode->path.pathkeys=pathkeys;
14771478

1479+
pathnode->fdw_outerpath=fdw_outerpath;
14781480
pathnode->fdw_private=fdw_private;
14791481

14801482
returnpathnode;

‎src/include/foreign/fdwapi.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,17 @@ typedef ForeignScan *(*GetForeignPlan_function) (PlannerInfo *root,
3636
Oidforeigntableid,
3737
ForeignPath*best_path,
3838
List*tlist,
39-
List*scan_clauses);
39+
List*scan_clauses,
40+
Plan*outer_plan);
4041

4142
typedefvoid (*BeginForeignScan_function) (ForeignScanState*node,
4243
inteflags);
4344

4445
typedefTupleTableSlot*(*IterateForeignScan_function) (ForeignScanState*node);
4546

47+
typedefbool (*RecheckForeignScan_function) (ForeignScanState*node,
48+
TupleTableSlot*slot);
49+
4650
typedefvoid (*ReScanForeignScan_function) (ForeignScanState*node);
4751

4852
typedefvoid (*EndForeignScan_function) (ForeignScanState*node);
@@ -162,6 +166,7 @@ typedef struct FdwRoutine
162166
/* Functions for SELECT FOR UPDATE/SHARE row locking */
163167
GetForeignRowMarkType_functionGetForeignRowMarkType;
164168
RefetchForeignRow_functionRefetchForeignRow;
169+
RecheckForeignScan_functionRecheckForeignScan;
165170

166171
/* Support functions for EXPLAIN */
167172
ExplainForeignScan_functionExplainForeignScan;

‎src/include/nodes/relation.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,7 @@ typedef struct TidPath
894894
typedefstructForeignPath
895895
{
896896
Pathpath;
897+
Path*fdw_outerpath;
897898
List*fdw_private;
898899
}ForeignPath;
899900

‎src/include/optimizer/pathnode.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ extern ForeignPath *create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
8383
doublerows,Coststartup_cost,Costtotal_cost,
8484
List*pathkeys,
8585
Relidsrequired_outer,
86+
Path*fdw_outerpath,
8687
List*fdw_private);
8788

8889
externRelidscalc_nestloop_required_outer(Path*outer_path,Path*inner_path);

‎src/include/optimizer/planmain.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
4545
Indexscanrelid,Plan*subplan);
4646
externForeignScan*make_foreignscan(List*qptlist,List*qpqual,
4747
Indexscanrelid,List*fdw_exprs,List*fdw_private,
48-
List*fdw_scan_tlist,List*fdw_recheck_quals);
48+
List*fdw_scan_tlist,List*fdw_recheck_quals,
49+
Plan*outer_plan);
4950
externAppend*make_append(List*appendplans,List*tlist);
5051
externRecursiveUnion*make_recursive_union(List*tlist,
5152
Plan*lefttree,Plan*righttree,intwtParam,

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp