1 /*-------------------------------------------------------------------------
2 *
3 * nodeForeignscan.c
4 * Routines to support scans of foreign tables
5 *
6 * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/executor/nodeForeignscan.c
12 *
13 *-------------------------------------------------------------------------
14 */
15 /*
16 * INTERFACE ROUTINES
17 *
18 * ExecForeignScan scans a foreign table.
19 * ExecInitForeignScan creates and initializes state info.
20 * ExecReScanForeignScan rescans the foreign relation.
21 * ExecEndForeignScan releases any resources allocated.
22 */
23 #include "postgres.h"
24
25 #include "executor/executor.h"
26 #include "executor/nodeForeignscan.h"
27 #include "foreign/fdwapi.h"
28 #include "utils/memutils.h"
29 #include "utils/rel.h"
30
31 static TupleTableSlot *ForeignNext(ForeignScanState *node);
32 static bool ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot);
33
34
35 /* ----------------------------------------------------------------
36 * ForeignNext
37 *
38 * This is a workhorse for ExecForeignScan
39 * ----------------------------------------------------------------
40 */
41 static TupleTableSlot *
ForeignNext(ForeignScanState * node)42 ForeignNext(ForeignScanState *node)
43 {
44 TupleTableSlot *slot;
45 ForeignScan *plan = (ForeignScan *) node->ss.ps.plan;
46 ExprContext *econtext = node->ss.ps.ps_ExprContext;
47 MemoryContext oldcontext;
48
49 /* Call the Iterate function in short-lived context */
50 oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
51 if (plan->operation != CMD_SELECT)
52 slot = node->fdwroutine->IterateDirectModify(node);
53 else
54 slot = node->fdwroutine->IterateForeignScan(node);
55 MemoryContextSwitchTo(oldcontext);
56
57 /*
58 * If any system columns are requested, we have to force the tuple into
59 * physical-tuple form to avoid "cannot extract system attribute from
60 * virtual tuple" errors later. We also insert a valid value for
61 * tableoid, which is the only actually-useful system column.
62 */
63 if (plan->fsSystemCol && !TupIsNull(slot))
64 {
65 HeapTuple tup = ExecMaterializeSlot(slot);
66
67 tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
68 }
69
70 return slot;
71 }
72
73 /*
74 * ForeignRecheck -- access method routine to recheck a tuple in EvalPlanQual
75 */
76 static bool
ForeignRecheck(ForeignScanState * node,TupleTableSlot * slot)77 ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
78 {
79 FdwRoutine *fdwroutine = node->fdwroutine;
80 ExprContext *econtext;
81
82 /*
83 * extract necessary information from foreign scan node
84 */
85 econtext = node->ss.ps.ps_ExprContext;
86
87 /* Does the tuple meet the remote qual condition? */
88 econtext->ecxt_scantuple = slot;
89
90 ResetExprContext(econtext);
91
92 /*
93 * If an outer join is pushed down, RecheckForeignScan may need to store a
94 * different tuple in the slot, because a different set of columns may go
95 * to NULL upon recheck. Otherwise, it shouldn't need to change the slot
96 * contents, just return true or false to indicate whether the quals still
97 * pass. For simple cases, setting fdw_recheck_quals may be easier than
98 * providing this callback.
99 */
100 if (fdwroutine->RecheckForeignScan &&
101 !fdwroutine->RecheckForeignScan(node, slot))
102 return false;
103
104 return ExecQual(node->fdw_recheck_quals, econtext, false);
105 }
106
107 /* ----------------------------------------------------------------
108 * ExecForeignScan(node)
109 *
110 * Fetches the next tuple from the FDW, checks local quals, and
111 * returns it.
112 * We call the ExecScan() routine and pass it the appropriate
113 * access method functions.
114 * ----------------------------------------------------------------
115 */
116 TupleTableSlot *
ExecForeignScan(ForeignScanState * node)117 ExecForeignScan(ForeignScanState *node)
118 {
119 return ExecScan((ScanState *) node,
120 (ExecScanAccessMtd) ForeignNext,
121 (ExecScanRecheckMtd) ForeignRecheck);
122 }
123
124
125 /* ----------------------------------------------------------------
126 * ExecInitForeignScan
127 * ----------------------------------------------------------------
128 */
129 ForeignScanState *
ExecInitForeignScan(ForeignScan * node,EState * estate,int eflags)130 ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
131 {
132 ForeignScanState *scanstate;
133 Relation currentRelation = NULL;
134 Index scanrelid = node->scan.scanrelid;
135 Index tlistvarno;
136 FdwRoutine *fdwroutine;
137
138 /* check for unsupported flags */
139 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
140
141 /*
142 * create state structure
143 */
144 scanstate = makeNode(ForeignScanState);
145 scanstate->ss.ps.plan = (Plan *) node;
146 scanstate->ss.ps.state = estate;
147
148 /*
149 * Miscellaneous initialization
150 *
151 * create expression context for node
152 */
153 ExecAssignExprContext(estate, &scanstate->ss.ps);
154
155 scanstate->ss.ps.ps_TupFromTlist = false;
156
157 /*
158 * initialize child expressions
159 */
160 scanstate->ss.ps.targetlist = (List *)
161 ExecInitExpr((Expr *) node->scan.plan.targetlist,
162 (PlanState *) scanstate);
163 scanstate->ss.ps.qual = (List *)
164 ExecInitExpr((Expr *) node->scan.plan.qual,
165 (PlanState *) scanstate);
166 scanstate->fdw_recheck_quals = (List *)
167 ExecInitExpr((Expr *) node->fdw_recheck_quals,
168 (PlanState *) scanstate);
169
170 /*
171 * tuple table initialization
172 */
173 ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
174 ExecInitScanTupleSlot(estate, &scanstate->ss);
175
176 /*
177 * open the base relation, if any, and acquire an appropriate lock on it;
178 * also acquire function pointers from the FDW's handler
179 */
180 if (scanrelid > 0)
181 {
182 currentRelation = ExecOpenScanRelation(estate, scanrelid, eflags);
183 scanstate->ss.ss_currentRelation = currentRelation;
184 fdwroutine = GetFdwRoutineForRelation(currentRelation, true);
185 }
186 else
187 {
188 /* We can't use the relcache, so get fdwroutine the hard way */
189 fdwroutine = GetFdwRoutineByServerId(node->fs_server);
190 }
191
192 /*
193 * Determine the scan tuple type. If the FDW provided a targetlist
194 * describing the scan tuples, use that; else use base relation's rowtype.
195 */
196 if (node->fdw_scan_tlist != NIL || currentRelation == NULL)
197 {
198 TupleDesc scan_tupdesc;
199
200 scan_tupdesc = ExecTypeFromTL(node->fdw_scan_tlist, false);
201 ExecAssignScanType(&scanstate->ss, scan_tupdesc);
202 /* Node's targetlist will contain Vars with varno = INDEX_VAR */
203 tlistvarno = INDEX_VAR;
204 }
205 else
206 {
207 ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation));
208 /* Node's targetlist will contain Vars with varno = scanrelid */
209 tlistvarno = scanrelid;
210 }
211
212 /*
213 * Initialize result tuple type and projection info.
214 */
215 ExecAssignResultTypeFromTL(&scanstate->ss.ps);
216 ExecAssignScanProjectionInfoWithVarno(&scanstate->ss, tlistvarno);
217
218 /*
219 * Initialize FDW-related state.
220 */
221 scanstate->fdwroutine = fdwroutine;
222 scanstate->fdw_state = NULL;
223
224 /* Initialize any outer plan. */
225 if (outerPlan(node))
226 outerPlanState(scanstate) =
227 ExecInitNode(outerPlan(node), estate, eflags);
228
229 /*
230 * Tell the FDW to initialize the scan.
231 */
232 if (node->operation != CMD_SELECT)
233 fdwroutine->BeginDirectModify(scanstate, eflags);
234 else
235 fdwroutine->BeginForeignScan(scanstate, eflags);
236
237 return scanstate;
238 }
239
240 /* ----------------------------------------------------------------
241 * ExecEndForeignScan
242 *
243 * frees any storage allocated through C routines.
244 * ----------------------------------------------------------------
245 */
246 void
ExecEndForeignScan(ForeignScanState * node)247 ExecEndForeignScan(ForeignScanState *node)
248 {
249 ForeignScan *plan = (ForeignScan *) node->ss.ps.plan;
250
251 /* Let the FDW shut down */
252 if (plan->operation != CMD_SELECT)
253 node->fdwroutine->EndDirectModify(node);
254 else
255 node->fdwroutine->EndForeignScan(node);
256
257 /* Shut down any outer plan. */
258 if (outerPlanState(node))
259 ExecEndNode(outerPlanState(node));
260
261 /* Free the exprcontext */
262 ExecFreeExprContext(&node->ss.ps);
263
264 /* clean out the tuple table */
265 ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
266 ExecClearTuple(node->ss.ss_ScanTupleSlot);
267
268 /* close the relation. */
269 if (node->ss.ss_currentRelation)
270 ExecCloseScanRelation(node->ss.ss_currentRelation);
271 }
272
273 /* ----------------------------------------------------------------
274 * ExecReScanForeignScan
275 *
276 * Rescans the relation.
277 * ----------------------------------------------------------------
278 */
279 void
ExecReScanForeignScan(ForeignScanState * node)280 ExecReScanForeignScan(ForeignScanState *node)
281 {
282 PlanState *outerPlan = outerPlanState(node);
283
284 node->fdwroutine->ReScanForeignScan(node);
285
286 /*
287 * If chgParam of subnode is not null then plan will be re-scanned by
288 * first ExecProcNode. outerPlan may also be NULL, in which case there is
289 * nothing to rescan at all.
290 */
291 if (outerPlan != NULL && outerPlan->chgParam == NULL)
292 ExecReScan(outerPlan);
293
294 ExecScanReScan(&node->ss);
295 }
296
297 /* ----------------------------------------------------------------
298 * ExecForeignScanEstimate
299 *
300 * Informs size of the parallel coordination information, if any
301 * ----------------------------------------------------------------
302 */
303 void
ExecForeignScanEstimate(ForeignScanState * node,ParallelContext * pcxt)304 ExecForeignScanEstimate(ForeignScanState *node, ParallelContext *pcxt)
305 {
306 FdwRoutine *fdwroutine = node->fdwroutine;
307
308 if (fdwroutine->EstimateDSMForeignScan)
309 {
310 node->pscan_len = fdwroutine->EstimateDSMForeignScan(node, pcxt);
311 shm_toc_estimate_chunk(&pcxt->estimator, node->pscan_len);
312 shm_toc_estimate_keys(&pcxt->estimator, 1);
313 }
314 }
315
316 /* ----------------------------------------------------------------
317 * ExecForeignScanInitializeDSM
318 *
319 * Initialize the parallel coordination information
320 * ----------------------------------------------------------------
321 */
322 void
ExecForeignScanInitializeDSM(ForeignScanState * node,ParallelContext * pcxt)323 ExecForeignScanInitializeDSM(ForeignScanState *node, ParallelContext *pcxt)
324 {
325 FdwRoutine *fdwroutine = node->fdwroutine;
326
327 if (fdwroutine->InitializeDSMForeignScan)
328 {
329 int plan_node_id = node->ss.ps.plan->plan_node_id;
330 void *coordinate;
331
332 coordinate = shm_toc_allocate(pcxt->toc, node->pscan_len);
333 fdwroutine->InitializeDSMForeignScan(node, pcxt, coordinate);
334 shm_toc_insert(pcxt->toc, plan_node_id, coordinate);
335 }
336 }
337
338 /* ----------------------------------------------------------------
339 * ExecForeignScanInitializeDSM
340 *
341 * Initialization according to the parallel coordination information
342 * ----------------------------------------------------------------
343 */
344 void
ExecForeignScanInitializeWorker(ForeignScanState * node,shm_toc * toc)345 ExecForeignScanInitializeWorker(ForeignScanState *node, shm_toc *toc)
346 {
347 FdwRoutine *fdwroutine = node->fdwroutine;
348
349 if (fdwroutine->InitializeWorkerForeignScan)
350 {
351 int plan_node_id = node->ss.ps.plan->plan_node_id;
352 void *coordinate;
353
354 coordinate = shm_toc_lookup(toc, plan_node_id);
355 fdwroutine->InitializeWorkerForeignScan(node, toc, coordinate);
356 }
357 }
358