LCOV - code coverage report
Current view: top level - src/backend/executor - nodeForeignscan.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 76.8 % 142 109
Test Date: 2026-02-28 23:15:01 Functions: 71.4 % 14 10
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * nodeForeignscan.c
       4              :  *    Routines to support scans of foreign tables
       5              :  *
       6              :  * Portions Copyright (c) 1996-2026, 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 *
      41        71336 : ForeignNext(ForeignScanState *node)
      42              : {
      43              :     TupleTableSlot *slot;
      44        71336 :     ForeignScan *plan = (ForeignScan *) node->ss.ps.plan;
      45        71336 :     ExprContext *econtext = node->ss.ps.ps_ExprContext;
      46              :     MemoryContext oldcontext;
      47              : 
      48              :     /* Call the Iterate function in short-lived context */
      49        71336 :     oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
      50        71336 :     if (plan->operation != CMD_SELECT)
      51              :     {
      52              :         /*
      53              :          * direct modifications cannot be re-evaluated, so shouldn't get here
      54              :          * during EvalPlanQual processing
      55              :          */
      56              :         Assert(node->ss.ps.state->es_epq_active == NULL);
      57              : 
      58          418 :         slot = node->fdwroutine->IterateDirectModify(node);
      59              :     }
      60              :     else
      61        70918 :         slot = node->fdwroutine->IterateForeignScan(node);
      62        71321 :     MemoryContextSwitchTo(oldcontext);
      63              : 
      64              :     /*
      65              :      * Insert valid value into tableoid, the only actually-useful system
      66              :      * column.
      67              :      */
      68        71321 :     if (plan->fsSystemCol && !TupIsNull(slot))
      69         4535 :         slot->tts_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
      70              : 
      71        71321 :     return slot;
      72              : }
      73              : 
      74              : /*
      75              :  * ForeignRecheck -- access method routine to recheck a tuple in EvalPlanQual
      76              :  */
      77              : static bool
      78            5 : ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
      79              : {
      80            5 :     FdwRoutine *fdwroutine = node->fdwroutine;
      81              :     ExprContext *econtext;
      82              : 
      83              :     /*
      84              :      * extract necessary information from foreign scan node
      85              :      */
      86            5 :     econtext = node->ss.ps.ps_ExprContext;
      87              : 
      88              :     /* Does the tuple meet the remote qual condition? */
      89            5 :     econtext->ecxt_scantuple = slot;
      90              : 
      91            5 :     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              :      */
     101            5 :     if (fdwroutine->RecheckForeignScan &&
     102            5 :         !fdwroutine->RecheckForeignScan(node, slot))
     103            1 :         return false;
     104              : 
     105            4 :     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 *
     118        62257 : ExecForeignScan(PlanState *pstate)
     119              : {
     120        62257 :     ForeignScanState *node = castNode(ForeignScanState, pstate);
     121        62257 :     ForeignScan *plan = (ForeignScan *) node->ss.ps.plan;
     122        62257 :     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        62257 :     if (estate->es_epq_active != NULL && plan->operation != CMD_SELECT)
     129            0 :         return NULL;
     130              : 
     131        62257 :     return ExecScan(&node->ss,
     132              :                     (ExecScanAccessMtd) ForeignNext,
     133              :                     (ExecScanRecheckMtd) ForeignRecheck);
     134              : }
     135              : 
     136              : 
     137              : /* ----------------------------------------------------------------
     138              :  *      ExecInitForeignScan
     139              :  * ----------------------------------------------------------------
     140              :  */
     141              : ForeignScanState *
     142         1039 : ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
     143              : {
     144              :     ForeignScanState *scanstate;
     145         1039 :     Relation    currentRelation = NULL;
     146         1039 :     Index       scanrelid = node->scan.scanrelid;
     147              :     int         tlistvarno;
     148              :     FdwRoutine *fdwroutine;
     149              : 
     150              :     /* check for unsupported flags */
     151              :     Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
     152              : 
     153              :     /*
     154              :      * create state structure
     155              :      */
     156         1039 :     scanstate = makeNode(ForeignScanState);
     157         1039 :     scanstate->ss.ps.plan = (Plan *) node;
     158         1039 :     scanstate->ss.ps.state = estate;
     159         1039 :     scanstate->ss.ps.ExecProcNode = ExecForeignScan;
     160              : 
     161              :     /*
     162              :      * Miscellaneous initialization
     163              :      *
     164              :      * create expression context for node
     165              :      */
     166         1039 :     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              :      */
     172         1039 :     if (scanrelid > 0)
     173              :     {
     174          744 :         currentRelation = ExecOpenScanRelation(estate, scanrelid, eflags);
     175          744 :         scanstate->ss.ss_currentRelation = currentRelation;
     176          744 :         fdwroutine = GetFdwRoutineForRelation(currentRelation, true);
     177              :     }
     178              :     else
     179              :     {
     180              :         /* We can't use the relcache, so get fdwroutine the hard way */
     181          295 :         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         1039 :     if (node->fdw_scan_tlist != NIL || currentRelation == NULL)
     189          295 :     {
     190              :         TupleDesc   scan_tupdesc;
     191              : 
     192          295 :         scan_tupdesc = ExecTypeFromTL(node->fdw_scan_tlist);
     193          295 :         ExecInitScanTupleSlot(estate, &scanstate->ss, scan_tupdesc,
     194              :                               &TTSOpsHeapTuple);
     195              :         /* Node's targetlist will contain Vars with varno = INDEX_VAR */
     196          295 :         tlistvarno = INDEX_VAR;
     197              :     }
     198              :     else
     199              :     {
     200              :         TupleDesc   scan_tupdesc;
     201              : 
     202              :         /* don't trust FDWs to return tuples fulfilling NOT NULL constraints */
     203          744 :         scan_tupdesc = CreateTupleDescCopy(RelationGetDescr(currentRelation));
     204          744 :         ExecInitScanTupleSlot(estate, &scanstate->ss, scan_tupdesc,
     205              :                               &TTSOpsHeapTuple);
     206              :         /* Node's targetlist will contain Vars with varno = scanrelid */
     207          744 :         tlistvarno = scanrelid;
     208              :     }
     209              : 
     210              :     /* Don't know what an FDW might return */
     211         1039 :     scanstate->ss.ps.scanopsfixed = false;
     212         1039 :     scanstate->ss.ps.scanopsset = true;
     213              : 
     214              :     /*
     215              :      * Initialize result slot, type and projection.
     216              :      */
     217         1039 :     ExecInitResultTypeTL(&scanstate->ss.ps);
     218         1039 :     ExecAssignScanProjectionInfoWithVarno(&scanstate->ss, tlistvarno);
     219              : 
     220              :     /*
     221              :      * initialize child expressions
     222              :      */
     223         1039 :     scanstate->ss.ps.qual =
     224         1039 :         ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
     225         1039 :     scanstate->fdw_recheck_quals =
     226         1039 :         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              :      */
     232         1132 :     scanstate->ss.ps.async_capable = (((Plan *) node)->async_capable &&
     233           93 :                                       estate->es_epq_active == NULL);
     234              : 
     235              :     /*
     236              :      * Initialize FDW-related state.
     237              :      */
     238         1039 :     scanstate->fdwroutine = fdwroutine;
     239         1039 :     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              :      */
     252         1039 :     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              :         {
     257            0 :             elog(ERROR, "result relation not initialized");
     258              :         }
     259          104 :         scanstate->resultRelInfo = estate->es_result_relations[node->resultRelation - 1];
     260              :     }
     261              : 
     262              :     /* Initialize any outer plan. */
     263         1039 :     if (outerPlan(node))
     264           19 :         outerPlanState(scanstate) =
     265           19 :             ExecInitNode(outerPlan(node), estate, eflags);
     266              : 
     267              :     /*
     268              :      * Tell the FDW to initialize the scan.
     269              :      */
     270         1039 :     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              :          */
     281          104 :         if (estate->es_epq_active == NULL)
     282          104 :             fdwroutine->BeginDirectModify(scanstate, eflags);
     283              :     }
     284              :     else
     285          935 :         fdwroutine->BeginForeignScan(scanstate, eflags);
     286              : 
     287         1030 :     return scanstate;
     288              : }
     289              : 
     290              : /* ----------------------------------------------------------------
     291              :  *      ExecEndForeignScan
     292              :  *
     293              :  *      frees any storage allocated through C routines.
     294              :  * ----------------------------------------------------------------
     295              :  */
     296              : void
     297          998 : ExecEndForeignScan(ForeignScanState *node)
     298              : {
     299          998 :     ForeignScan *plan = (ForeignScan *) node->ss.ps.plan;
     300          998 :     EState     *estate = node->ss.ps.state;
     301              : 
     302              :     /* Let the FDW shut down */
     303          998 :     if (plan->operation != CMD_SELECT)
     304              :     {
     305           96 :         if (estate->es_epq_active == NULL)
     306           96 :             node->fdwroutine->EndDirectModify(node);
     307              :     }
     308              :     else
     309          902 :         node->fdwroutine->EndForeignScan(node);
     310              : 
     311              :     /* Shut down any outer plan. */
     312          998 :     if (outerPlanState(node))
     313           19 :         ExecEndNode(outerPlanState(node));
     314          998 : }
     315              : 
     316              : /* ----------------------------------------------------------------
     317              :  *      ExecReScanForeignScan
     318              :  *
     319              :  *      Rescans the relation.
     320              :  * ----------------------------------------------------------------
     321              :  */
     322              : void
     323          410 : ExecReScanForeignScan(ForeignScanState *node)
     324              : {
     325          410 :     ForeignScan *plan = (ForeignScan *) node->ss.ps.plan;
     326          410 :     EState     *estate = node->ss.ps.state;
     327          410 :     PlanState  *outerPlan = outerPlanState(node);
     328              : 
     329              :     /*
     330              :      * Ignore direct modifications when EvalPlanQual is active --- they are
     331              :      * irrelevant for EvalPlanQual rechecking
     332              :      */
     333          410 :     if (estate->es_epq_active != NULL && plan->operation != CMD_SELECT)
     334            0 :         return;
     335              : 
     336          410 :     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              :      */
     343          410 :     if (outerPlan != NULL && outerPlan->chgParam == NULL)
     344           10 :         ExecReScan(outerPlan);
     345              : 
     346          410 :     ExecScanReScan(&node->ss);
     347              : }
     348              : 
     349              : /* ----------------------------------------------------------------
     350              :  *      ExecForeignScanEstimate
     351              :  *
     352              :  *      Informs size of the parallel coordination information, if any
     353              :  * ----------------------------------------------------------------
     354              :  */
     355              : void
     356            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
     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
     418            0 : ExecForeignScanInitializeWorker(ForeignScanState *node,
     419              :                                 ParallelWorkerContext *pwcxt)
     420              : {
     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              : 
     428            0 :         coordinate = shm_toc_lookup(pwcxt->toc, plan_node_id, false);
     429            0 :         fdwroutine->InitializeWorkerForeignScan(node, pwcxt->toc, coordinate);
     430              :     }
     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
     441          574 : ExecShutdownForeignScan(ForeignScanState *node)
     442              : {
     443          574 :     FdwRoutine *fdwroutine = node->fdwroutine;
     444              : 
     445          574 :     if (fdwroutine->ShutdownForeignScan)
     446            0 :         fdwroutine->ShutdownForeignScan(node);
     447          574 : }
     448              : 
     449              : /* ----------------------------------------------------------------
     450              :  *      ExecAsyncForeignScanRequest
     451              :  *
     452              :  *      Asynchronously request a tuple from a designed async-capable node
     453              :  * ----------------------------------------------------------------
     454              :  */
     455              : void
     456         6075 : ExecAsyncForeignScanRequest(AsyncRequest *areq)
     457              : {
     458         6075 :     ForeignScanState *node = (ForeignScanState *) areq->requestee;
     459         6075 :     FdwRoutine *fdwroutine = node->fdwroutine;
     460              : 
     461              :     Assert(fdwroutine->ForeignAsyncRequest != NULL);
     462         6075 :     fdwroutine->ForeignAsyncRequest(areq);
     463         6075 : }
     464              : 
     465              : /* ----------------------------------------------------------------
     466              :  *      ExecAsyncForeignScanConfigureWait
     467              :  *
     468              :  *      In async mode, configure for a wait
     469              :  * ----------------------------------------------------------------
     470              :  */
     471              : void
     472          228 : ExecAsyncForeignScanConfigureWait(AsyncRequest *areq)
     473              : {
     474          228 :     ForeignScanState *node = (ForeignScanState *) areq->requestee;
     475          228 :     FdwRoutine *fdwroutine = node->fdwroutine;
     476              : 
     477              :     Assert(fdwroutine->ForeignAsyncConfigureWait != NULL);
     478          228 :     fdwroutine->ForeignAsyncConfigureWait(areq);
     479          227 : }
     480              : 
     481              : /* ----------------------------------------------------------------
     482              :  *      ExecAsyncForeignScanNotify
     483              :  *
     484              :  *      Callback invoked when a relevant event has occurred
     485              :  * ----------------------------------------------------------------
     486              :  */
     487              : void
     488          147 : ExecAsyncForeignScanNotify(AsyncRequest *areq)
     489              : {
     490          147 :     ForeignScanState *node = (ForeignScanState *) areq->requestee;
     491          147 :     FdwRoutine *fdwroutine = node->fdwroutine;
     492              : 
     493              :     Assert(fdwroutine->ForeignAsyncNotify != NULL);
     494          147 :     fdwroutine->ForeignAsyncNotify(areq);
     495          147 : }
        

Generated by: LCOV version 2.0-1