Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * nodeForeignscan.c
4 : : * Routines to support scans of foreign tables
5 : : *
6 : : * Portions Copyright (c) 1996-2024, 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/rel.h"
29 : :
30 : : static TupleTableSlot *ForeignNext(ForeignScanState *node);
31 : : static bool ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot);
32 : :
33 : :
34 : : /* ----------------------------------------------------------------
35 : : * ForeignNext
36 : : *
37 : : * This is a workhorse for ExecForeignScan
38 : : * ----------------------------------------------------------------
39 : : */
40 : : static TupleTableSlot *
4802 tgl@sss.pgh.pa.us 41 :CBC 69334 : ForeignNext(ForeignScanState *node)
42 : : {
43 : : TupleTableSlot *slot;
4753 bruce@momjian.us 44 : 69334 : ForeignScan *plan = (ForeignScan *) node->ss.ps.plan;
4802 tgl@sss.pgh.pa.us 45 : 69334 : ExprContext *econtext = node->ss.ps.ps_ExprContext;
46 : : MemoryContext oldcontext;
47 : :
48 : : /* Call the Iterate function in short-lived context */
49 : 69334 : oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
2949 rhaas@postgresql.org 50 [ + + ]: 69334 : if (plan->operation != CMD_SELECT)
51 : : {
52 : : /*
53 : : * direct modifications cannot be re-evaluated, so shouldn't get here
54 : : * during EvalPlanQual processing
55 : : */
801 andres@anarazel.de 56 [ - + ]: 418 : Assert(node->ss.ps.state->es_epq_active == NULL);
57 : :
2949 rhaas@postgresql.org 58 : 418 : slot = node->fdwroutine->IterateDirectModify(node);
59 : : }
60 : : else
61 : 68916 : slot = node->fdwroutine->IterateForeignScan(node);
4802 tgl@sss.pgh.pa.us 62 : 69321 : MemoryContextSwitchTo(oldcontext);
63 : :
64 : : /*
65 : : * Insert valid value into tableoid, the only actually-useful system
66 : : * column.
67 : : */
68 [ + + + - : 69321 : if (plan->fsSystemCol && !TupIsNull(slot))
+ + ]
1874 andres@anarazel.de 69 : 2519 : slot->tts_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
70 : :
4802 tgl@sss.pgh.pa.us 71 : 69321 : return slot;
72 : : }
73 : :
74 : : /*
75 : : * ForeignRecheck -- access method routine to recheck a tuple in EvalPlanQual
76 : : */
77 : : static bool
4802 tgl@sss.pgh.pa.us 78 :UBC 0 : ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
79 : : {
3050 rhaas@postgresql.org 80 : 0 : FdwRoutine *fdwroutine = node->fdwroutine;
81 : : ExprContext *econtext;
82 : :
83 : : /*
84 : : * extract necessary information from foreign scan node
85 : : */
3104 86 : 0 : econtext = node->ss.ps.ps_ExprContext;
87 : :
88 : : /* Does the tuple meet the remote qual condition? */
89 : 0 : econtext->ecxt_scantuple = slot;
90 : :
91 : 0 : ResetExprContext(econtext);
92 : :
93 : : /*
94 : : * If an outer join is pushed down, RecheckForeignScan may need to store a
95 : : * different tuple in the slot, because a different set of columns may go
96 : : * to NULL upon recheck. Otherwise, it shouldn't need to change the slot
97 : : * contents, just return true or false to indicate whether the quals still
98 : : * pass. For simple cases, setting fdw_recheck_quals may be easier than
99 : : * providing this callback.
100 : : */
3050 101 [ # # ]: 0 : if (fdwroutine->RecheckForeignScan &&
102 [ # # ]: 0 : !fdwroutine->RecheckForeignScan(node, slot))
103 : 0 : return false;
104 : :
2588 andres@anarazel.de 105 : 0 : return ExecQual(node->fdw_recheck_quals, econtext);
106 : : }
107 : :
108 : : /* ----------------------------------------------------------------
109 : : * ExecForeignScan(node)
110 : : *
111 : : * Fetches the next tuple from the FDW, checks local quals, and
112 : : * returns it.
113 : : * We call the ExecScan() routine and pass it the appropriate
114 : : * access method functions.
115 : : * ----------------------------------------------------------------
116 : : */
117 : : static TupleTableSlot *
2463 andres@anarazel.de 118 :CBC 60248 : ExecForeignScan(PlanState *pstate)
119 : : {
120 : 60248 : ForeignScanState *node = castNode(ForeignScanState, pstate);
801 efujita@postgresql.o 121 : 60248 : ForeignScan *plan = (ForeignScan *) node->ss.ps.plan;
122 : 60248 : EState *estate = node->ss.ps.state;
123 : :
124 : : /*
125 : : * Ignore direct modifications when EvalPlanQual is active --- they are
126 : : * irrelevant for EvalPlanQual rechecking
127 : : */
128 [ - + - - ]: 60248 : if (estate->es_epq_active != NULL && plan->operation != CMD_SELECT)
801 efujita@postgresql.o 129 :UBC 0 : return NULL;
130 : :
2463 andres@anarazel.de 131 :CBC 60248 : return ExecScan(&node->ss,
132 : : (ExecScanAccessMtd) ForeignNext,
133 : : (ExecScanRecheckMtd) ForeignRecheck);
134 : : }
135 : :
136 : :
137 : : /* ----------------------------------------------------------------
138 : : * ExecInitForeignScan
139 : : * ----------------------------------------------------------------
140 : : */
141 : : ForeignScanState *
4802 tgl@sss.pgh.pa.us 142 : 978 : ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
143 : : {
144 : : ForeignScanState *scanstate;
3262 145 : 978 : Relation currentRelation = NULL;
3271 rhaas@postgresql.org 146 : 978 : Index scanrelid = node->scan.scanrelid;
147 : : int tlistvarno;
148 : : FdwRoutine *fdwroutine;
149 : :
150 : : /* check for unsupported flags */
4802 tgl@sss.pgh.pa.us 151 [ - + ]: 978 : Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
152 : :
153 : : /*
154 : : * create state structure
155 : : */
156 : 978 : scanstate = makeNode(ForeignScanState);
157 : 978 : scanstate->ss.ps.plan = (Plan *) node;
158 : 978 : scanstate->ss.ps.state = estate;
2463 andres@anarazel.de 159 : 978 : scanstate->ss.ps.ExecProcNode = ExecForeignScan;
160 : :
161 : : /*
162 : : * Miscellaneous initialization
163 : : *
164 : : * create expression context for node
165 : : */
4802 tgl@sss.pgh.pa.us 166 : 978 : ExecAssignExprContext(estate, &scanstate->ss.ps);
167 : :
168 : : /*
169 : : * open the scan relation, if any; also acquire function pointers from the
170 : : * FDW's handler
171 : : */
3271 rhaas@postgresql.org 172 [ + + ]: 978 : if (scanrelid > 0)
173 : : {
174 : 702 : currentRelation = ExecOpenScanRelation(estate, scanrelid, eflags);
175 : 702 : scanstate->ss.ss_currentRelation = currentRelation;
3262 tgl@sss.pgh.pa.us 176 : 702 : fdwroutine = GetFdwRoutineForRelation(currentRelation, true);
177 : : }
178 : : else
179 : : {
180 : : /* We can't use the relcache, so get fdwroutine the hard way */
181 : 276 : fdwroutine = GetFdwRoutineByServerId(node->fs_server);
182 : : }
183 : :
184 : : /*
185 : : * Determine the scan tuple type. If the FDW provided a targetlist
186 : : * describing the scan tuples, use that; else use base relation's rowtype.
187 : : */
188 [ + + + + ]: 978 : if (node->fdw_scan_tlist != NIL || currentRelation == NULL)
189 : 276 : {
190 : : TupleDesc scan_tupdesc;
191 : :
1972 andres@anarazel.de 192 : 276 : scan_tupdesc = ExecTypeFromTL(node->fdw_scan_tlist);
1977 193 : 276 : ExecInitScanTupleSlot(estate, &scanstate->ss, scan_tupdesc,
194 : : &TTSOpsHeapTuple);
195 : : /* Node's targetlist will contain Vars with varno = INDEX_VAR */
3262 tgl@sss.pgh.pa.us 196 : 276 : tlistvarno = INDEX_VAR;
197 : : }
198 : : else
199 : : {
200 : : TupleDesc scan_tupdesc;
201 : :
202 : : /* don't trust FDWs to return tuples fulfilling NOT NULL constraints */
2211 andres@anarazel.de 203 : 702 : scan_tupdesc = CreateTupleDescCopy(RelationGetDescr(currentRelation));
1977 204 : 702 : ExecInitScanTupleSlot(estate, &scanstate->ss, scan_tupdesc,
205 : : &TTSOpsHeapTuple);
206 : : /* Node's targetlist will contain Vars with varno = scanrelid */
3262 tgl@sss.pgh.pa.us 207 : 702 : tlistvarno = scanrelid;
208 : : }
209 : :
210 : : /* Don't know what an FDW might return */
1977 andres@anarazel.de 211 : 978 : scanstate->ss.ps.scanopsfixed = false;
212 : 978 : scanstate->ss.ps.scanopsset = true;
213 : :
214 : : /*
215 : : * Initialize result slot, type and projection.
216 : : */
1983 217 : 978 : ExecInitResultTypeTL(&scanstate->ss.ps);
3262 tgl@sss.pgh.pa.us 218 : 978 : ExecAssignScanProjectionInfoWithVarno(&scanstate->ss, tlistvarno);
219 : :
220 : : /*
221 : : * initialize child expressions
222 : : */
2249 andres@anarazel.de 223 : 978 : scanstate->ss.ps.qual =
224 : 978 : ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
225 : 978 : scanstate->fdw_recheck_quals =
226 : 978 : ExecInitQual(node->fdw_recheck_quals, (PlanState *) scanstate);
227 : :
228 : : /*
229 : : * Determine whether to scan the foreign relation asynchronously or not;
230 : : * this has to be kept in sync with the code in ExecInitAppend().
231 : : */
1068 efujita@postgresql.o 232 [ + + ]: 1071 : scanstate->ss.ps.async_capable = (((Plan *) node)->async_capable &&
233 [ + - ]: 93 : estate->es_epq_active == NULL);
234 : :
235 : : /*
236 : : * Initialize FDW-related state.
237 : : */
4802 tgl@sss.pgh.pa.us 238 : 978 : scanstate->fdwroutine = fdwroutine;
239 : 978 : scanstate->fdw_state = NULL;
240 : :
241 : : /*
242 : : * For the FDW's convenience, look up the modification target relation's
243 : : * ResultRelInfo. The ModifyTable node should have initialized it for us,
244 : : * see ExecInitModifyTable.
245 : : *
246 : : * Don't try to look up the ResultRelInfo when EvalPlanQual is active,
247 : : * though. Direct modifications cannot be re-evaluated as part of
248 : : * EvalPlanQual. The lookup wouldn't work anyway because during
249 : : * EvalPlanQual processing, EvalPlanQual only initializes the subtree
250 : : * under the ModifyTable, and doesn't run ExecInitModifyTable.
251 : : */
976 heikki.linnakangas@i 252 [ + + + - ]: 978 : if (node->resultRelation > 0 && estate->es_epq_active == NULL)
253 : : {
254 [ + - ]: 104 : if (estate->es_result_relations == NULL ||
255 [ - + ]: 104 : estate->es_result_relations[node->resultRelation - 1] == NULL)
256 : : {
976 heikki.linnakangas@i 257 [ # # ]:UBC 0 : elog(ERROR, "result relation not initialized");
258 : : }
1278 heikki.linnakangas@i 259 :CBC 104 : scanstate->resultRelInfo = estate->es_result_relations[node->resultRelation - 1];
260 : : }
261 : :
262 : : /* Initialize any outer plan. */
3050 rhaas@postgresql.org 263 [ + + ]: 978 : if (outerPlan(node))
264 : 12 : outerPlanState(scanstate) =
265 : 12 : ExecInitNode(outerPlan(node), estate, eflags);
266 : :
267 : : /*
268 : : * Tell the FDW to initialize the scan.
269 : : */
2949 270 [ + + ]: 978 : if (node->operation != CMD_SELECT)
271 : : {
272 : : /*
273 : : * Direct modifications cannot be re-evaluated by EvalPlanQual, so
274 : : * don't bother preparing the FDW.
275 : : *
276 : : * In case of an inherited UPDATE/DELETE with foreign targets there
277 : : * can be direct-modify ForeignScan nodes in the EvalPlanQual subtree,
278 : : * so we need to ignore such ForeignScan nodes during EvalPlanQual
279 : : * processing. See also ExecForeignScan/ExecReScanForeignScan.
280 : : */
976 heikki.linnakangas@i 281 [ + - ]: 104 : if (estate->es_epq_active == NULL)
282 : 104 : fdwroutine->BeginDirectModify(scanstate, eflags);
283 : : }
284 : : else
2949 rhaas@postgresql.org 285 : 874 : fdwroutine->BeginForeignScan(scanstate, eflags);
286 : :
4802 tgl@sss.pgh.pa.us 287 : 967 : return scanstate;
288 : : }
289 : :
290 : : /* ----------------------------------------------------------------
291 : : * ExecEndForeignScan
292 : : *
293 : : * frees any storage allocated through C routines.
294 : : * ----------------------------------------------------------------
295 : : */
296 : : void
297 : 941 : ExecEndForeignScan(ForeignScanState *node)
298 : : {
2949 rhaas@postgresql.org 299 : 941 : ForeignScan *plan = (ForeignScan *) node->ss.ps.plan;
976 heikki.linnakangas@i 300 : 941 : EState *estate = node->ss.ps.state;
301 : :
302 : : /* Let the FDW shut down */
2949 rhaas@postgresql.org 303 [ + + ]: 941 : if (plan->operation != CMD_SELECT)
304 : : {
976 heikki.linnakangas@i 305 [ + - ]: 96 : if (estate->es_epq_active == NULL)
306 : 96 : node->fdwroutine->EndDirectModify(node);
307 : : }
308 : : else
2949 rhaas@postgresql.org 309 : 845 : node->fdwroutine->EndForeignScan(node);
310 : :
311 : : /* Shut down any outer plan. */
3050 312 [ + + ]: 941 : if (outerPlanState(node))
313 : 12 : ExecEndNode(outerPlanState(node));
4802 tgl@sss.pgh.pa.us 314 : 941 : }
315 : :
316 : : /* ----------------------------------------------------------------
317 : : * ExecReScanForeignScan
318 : : *
319 : : * Rescans the relation.
320 : : * ----------------------------------------------------------------
321 : : */
322 : : void
323 : 402 : ExecReScanForeignScan(ForeignScanState *node)
324 : : {
801 efujita@postgresql.o 325 : 402 : ForeignScan *plan = (ForeignScan *) node->ss.ps.plan;
326 : 402 : EState *estate = node->ss.ps.state;
3050 rhaas@postgresql.org 327 : 402 : PlanState *outerPlan = outerPlanState(node);
328 : :
329 : : /*
330 : : * Ignore direct modifications when EvalPlanQual is active --- they are
331 : : * irrelevant for EvalPlanQual rechecking
332 : : */
801 efujita@postgresql.o 333 [ - + - - ]: 402 : if (estate->es_epq_active != NULL && plan->operation != CMD_SELECT)
801 efujita@postgresql.o 334 :UBC 0 : return;
335 : :
4802 tgl@sss.pgh.pa.us 336 :CBC 402 : node->fdwroutine->ReScanForeignScan(node);
337 : :
338 : : /*
339 : : * If chgParam of subnode is not null then plan will be re-scanned by
340 : : * first ExecProcNode. outerPlan may also be NULL, in which case there is
341 : : * nothing to rescan at all.
342 : : */
3050 rhaas@postgresql.org 343 [ + + + - ]: 402 : if (outerPlan != NULL && outerPlan->chgParam == NULL)
344 : 10 : ExecReScan(outerPlan);
345 : :
4802 tgl@sss.pgh.pa.us 346 : 402 : ExecScanReScan(&node->ss);
347 : : }
348 : :
349 : : /* ----------------------------------------------------------------
350 : : * ExecForeignScanEstimate
351 : : *
352 : : * Informs size of the parallel coordination information, if any
353 : : * ----------------------------------------------------------------
354 : : */
355 : : void
2993 rhaas@postgresql.org 356 :UBC 0 : ExecForeignScanEstimate(ForeignScanState *node, ParallelContext *pcxt)
357 : : {
358 : 0 : FdwRoutine *fdwroutine = node->fdwroutine;
359 : :
360 [ # # ]: 0 : if (fdwroutine->EstimateDSMForeignScan)
361 : : {
362 : 0 : node->pscan_len = fdwroutine->EstimateDSMForeignScan(node, pcxt);
363 : 0 : shm_toc_estimate_chunk(&pcxt->estimator, node->pscan_len);
364 : 0 : shm_toc_estimate_keys(&pcxt->estimator, 1);
365 : : }
366 : 0 : }
367 : :
368 : : /* ----------------------------------------------------------------
369 : : * ExecForeignScanInitializeDSM
370 : : *
371 : : * Initialize the parallel coordination information
372 : : * ----------------------------------------------------------------
373 : : */
374 : : void
375 : 0 : ExecForeignScanInitializeDSM(ForeignScanState *node, ParallelContext *pcxt)
376 : : {
377 : 0 : FdwRoutine *fdwroutine = node->fdwroutine;
378 : :
379 [ # # ]: 0 : if (fdwroutine->InitializeDSMForeignScan)
380 : : {
381 : 0 : int plan_node_id = node->ss.ps.plan->plan_node_id;
382 : : void *coordinate;
383 : :
384 : 0 : coordinate = shm_toc_allocate(pcxt->toc, node->pscan_len);
385 : 0 : fdwroutine->InitializeDSMForeignScan(node, pcxt, coordinate);
386 : 0 : shm_toc_insert(pcxt->toc, plan_node_id, coordinate);
387 : : }
388 : 0 : }
389 : :
390 : : /* ----------------------------------------------------------------
391 : : * ExecForeignScanReInitializeDSM
392 : : *
393 : : * Reset shared state before beginning a fresh scan.
394 : : * ----------------------------------------------------------------
395 : : */
396 : : void
2419 tgl@sss.pgh.pa.us 397 : 0 : ExecForeignScanReInitializeDSM(ForeignScanState *node, ParallelContext *pcxt)
398 : : {
399 : 0 : FdwRoutine *fdwroutine = node->fdwroutine;
400 : :
401 [ # # ]: 0 : if (fdwroutine->ReInitializeDSMForeignScan)
402 : : {
403 : 0 : int plan_node_id = node->ss.ps.plan->plan_node_id;
404 : : void *coordinate;
405 : :
406 : 0 : coordinate = shm_toc_lookup(pcxt->toc, plan_node_id, false);
407 : 0 : fdwroutine->ReInitializeDSMForeignScan(node, pcxt, coordinate);
408 : : }
409 : 0 : }
410 : :
411 : : /* ----------------------------------------------------------------
412 : : * ExecForeignScanInitializeWorker
413 : : *
414 : : * Initialization according to the parallel coordination information
415 : : * ----------------------------------------------------------------
416 : : */
417 : : void
2341 andres@anarazel.de 418 : 0 : ExecForeignScanInitializeWorker(ForeignScanState *node,
419 : : ParallelWorkerContext *pwcxt)
420 : : {
2993 rhaas@postgresql.org 421 : 0 : FdwRoutine *fdwroutine = node->fdwroutine;
422 : :
423 [ # # ]: 0 : if (fdwroutine->InitializeWorkerForeignScan)
424 : : {
425 : 0 : int plan_node_id = node->ss.ps.plan->plan_node_id;
426 : : void *coordinate;
427 : :
2341 andres@anarazel.de 428 : 0 : coordinate = shm_toc_lookup(pwcxt->toc, plan_node_id, false);
429 : 0 : fdwroutine->InitializeWorkerForeignScan(node, pwcxt->toc, coordinate);
430 : : }
2993 rhaas@postgresql.org 431 : 0 : }
432 : :
433 : : /* ----------------------------------------------------------------
434 : : * ExecShutdownForeignScan
435 : : *
436 : : * Gives FDW chance to stop asynchronous resource consumption
437 : : * and release any resources still held.
438 : : * ----------------------------------------------------------------
439 : : */
440 : : void
2604 rhaas@postgresql.org 441 :CBC 547 : ExecShutdownForeignScan(ForeignScanState *node)
442 : : {
443 : 547 : FdwRoutine *fdwroutine = node->fdwroutine;
444 : :
445 [ - + ]: 547 : if (fdwroutine->ShutdownForeignScan)
2604 rhaas@postgresql.org 446 :UBC 0 : fdwroutine->ShutdownForeignScan(node);
2604 rhaas@postgresql.org 447 :CBC 547 : }
448 : :
449 : : /* ----------------------------------------------------------------
450 : : * ExecAsyncForeignScanRequest
451 : : *
452 : : * Asynchronously request a tuple from a designed async-capable node
453 : : * ----------------------------------------------------------------
454 : : */
455 : : void
1110 efujita@postgresql.o 456 : 6175 : ExecAsyncForeignScanRequest(AsyncRequest *areq)
457 : : {
458 : 6175 : ForeignScanState *node = (ForeignScanState *) areq->requestee;
459 : 6175 : FdwRoutine *fdwroutine = node->fdwroutine;
460 : :
461 [ - + ]: 6175 : Assert(fdwroutine->ForeignAsyncRequest != NULL);
462 : 6175 : fdwroutine->ForeignAsyncRequest(areq);
463 : 6175 : }
464 : :
465 : : /* ----------------------------------------------------------------
466 : : * ExecAsyncForeignScanConfigureWait
467 : : *
468 : : * In async mode, configure for a wait
469 : : * ----------------------------------------------------------------
470 : : */
471 : : void
472 : 283 : ExecAsyncForeignScanConfigureWait(AsyncRequest *areq)
473 : : {
474 : 283 : ForeignScanState *node = (ForeignScanState *) areq->requestee;
475 : 283 : FdwRoutine *fdwroutine = node->fdwroutine;
476 : :
477 [ - + ]: 283 : Assert(fdwroutine->ForeignAsyncConfigureWait != NULL);
478 : 283 : fdwroutine->ForeignAsyncConfigureWait(areq);
479 : 282 : }
480 : :
481 : : /* ----------------------------------------------------------------
482 : : * ExecAsyncForeignScanNotify
483 : : *
484 : : * Callback invoked when a relevant event has occurred
485 : : * ----------------------------------------------------------------
486 : : */
487 : : void
488 : 148 : ExecAsyncForeignScanNotify(AsyncRequest *areq)
489 : : {
490 : 148 : ForeignScanState *node = (ForeignScanState *) areq->requestee;
491 : 148 : FdwRoutine *fdwroutine = node->fdwroutine;
492 : :
493 [ - + ]: 148 : Assert(fdwroutine->ForeignAsyncNotify != NULL);
494 : 148 : fdwroutine->ForeignAsyncNotify(areq);
495 : 148 : }
|