LCOV - code coverage report
Current view: top level - src/backend/executor - spi.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 900 1231 73.1 %
Date: 2023-12-05 08:11:01 Functions: 70 84 83.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * spi.c
       4             :  *              Server Programming Interface
       5             :  *
       6             :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/executor/spi.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/htup_details.h"
      18             : #include "access/printtup.h"
      19             : #include "access/sysattr.h"
      20             : #include "access/xact.h"
      21             : #include "catalog/heap.h"
      22             : #include "catalog/pg_type.h"
      23             : #include "commands/trigger.h"
      24             : #include "executor/executor.h"
      25             : #include "executor/spi_priv.h"
      26             : #include "miscadmin.h"
      27             : #include "tcop/pquery.h"
      28             : #include "tcop/utility.h"
      29             : #include "utils/builtins.h"
      30             : #include "utils/datum.h"
      31             : #include "utils/lsyscache.h"
      32             : #include "utils/memutils.h"
      33             : #include "utils/rel.h"
      34             : #include "utils/snapmgr.h"
      35             : #include "utils/syscache.h"
      36             : #include "utils/typcache.h"
      37             : 
      38             : 
      39             : /*
      40             :  * These global variables are part of the API for various SPI functions
      41             :  * (a horrible API choice, but it's too late now).  To reduce the risk of
      42             :  * interference between different SPI callers, we save and restore them
      43             :  * when entering/exiting a SPI nesting level.
      44             :  */
      45             : uint64      SPI_processed = 0;
      46             : SPITupleTable *SPI_tuptable = NULL;
      47             : int         SPI_result = 0;
      48             : 
      49             : static _SPI_connection *_SPI_stack = NULL;
      50             : static _SPI_connection *_SPI_current = NULL;
      51             : static int  _SPI_stack_depth = 0;   /* allocated size of _SPI_stack */
      52             : static int  _SPI_connected = -1;    /* current stack index */
      53             : 
      54             : typedef struct SPICallbackArg
      55             : {
      56             :     const char *query;
      57             :     RawParseMode mode;
      58             : } SPICallbackArg;
      59             : 
      60             : static Portal SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
      61             :                                        ParamListInfo paramLI, bool read_only);
      62             : 
      63             : static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan);
      64             : 
      65             : static void _SPI_prepare_oneshot_plan(const char *src, SPIPlanPtr plan);
      66             : 
      67             : static int  _SPI_execute_plan(SPIPlanPtr plan, const SPIExecuteOptions *options,
      68             :                               Snapshot snapshot, Snapshot crosscheck_snapshot,
      69             :                               bool fire_triggers);
      70             : 
      71             : static ParamListInfo _SPI_convert_params(int nargs, Oid *argtypes,
      72             :                                          Datum *Values, const char *Nulls);
      73             : 
      74             : static int  _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount);
      75             : 
      76             : static void _SPI_error_callback(void *arg);
      77             : 
      78             : static void _SPI_cursor_operation(Portal portal,
      79             :                                   FetchDirection direction, long count,
      80             :                                   DestReceiver *dest);
      81             : 
      82             : static SPIPlanPtr _SPI_make_plan_non_temp(SPIPlanPtr plan);
      83             : static SPIPlanPtr _SPI_save_plan(SPIPlanPtr plan);
      84             : 
      85             : static int  _SPI_begin_call(bool use_exec);
      86             : static int  _SPI_end_call(bool use_exec);
      87             : static MemoryContext _SPI_execmem(void);
      88             : static MemoryContext _SPI_procmem(void);
      89             : static bool _SPI_checktuples(void);
      90             : 
      91             : 
      92             : /* =================== interface functions =================== */
      93             : 
      94             : int
      95       15438 : SPI_connect(void)
      96             : {
      97       15438 :     return SPI_connect_ext(0);
      98             : }
      99             : 
     100             : int
     101       98338 : SPI_connect_ext(int options)
     102             : {
     103             :     int         newdepth;
     104             : 
     105             :     /* Enlarge stack if necessary */
     106       98338 :     if (_SPI_stack == NULL)
     107             :     {
     108        1616 :         if (_SPI_connected != -1 || _SPI_stack_depth != 0)
     109           0 :             elog(ERROR, "SPI stack corrupted");
     110        1616 :         newdepth = 16;
     111        1616 :         _SPI_stack = (_SPI_connection *)
     112        1616 :             MemoryContextAlloc(TopMemoryContext,
     113             :                                newdepth * sizeof(_SPI_connection));
     114        1616 :         _SPI_stack_depth = newdepth;
     115             :     }
     116             :     else
     117             :     {
     118       96722 :         if (_SPI_stack_depth <= 0 || _SPI_stack_depth <= _SPI_connected)
     119           0 :             elog(ERROR, "SPI stack corrupted");
     120       96722 :         if (_SPI_stack_depth == _SPI_connected + 1)
     121             :         {
     122          26 :             newdepth = _SPI_stack_depth * 2;
     123          26 :             _SPI_stack = (_SPI_connection *)
     124          26 :                 repalloc(_SPI_stack,
     125             :                          newdepth * sizeof(_SPI_connection));
     126          26 :             _SPI_stack_depth = newdepth;
     127             :         }
     128             :     }
     129             : 
     130             :     /* Enter new stack level */
     131       98338 :     _SPI_connected++;
     132             :     Assert(_SPI_connected >= 0 && _SPI_connected < _SPI_stack_depth);
     133             : 
     134       98338 :     _SPI_current = &(_SPI_stack[_SPI_connected]);
     135       98338 :     _SPI_current->processed = 0;
     136       98338 :     _SPI_current->tuptable = NULL;
     137       98338 :     _SPI_current->execSubid = InvalidSubTransactionId;
     138       98338 :     slist_init(&_SPI_current->tuptables);
     139       98338 :     _SPI_current->procCxt = NULL;    /* in case we fail to create 'em */
     140       98338 :     _SPI_current->execCxt = NULL;
     141       98338 :     _SPI_current->connectSubid = GetCurrentSubTransactionId();
     142       98338 :     _SPI_current->queryEnv = NULL;
     143       98338 :     _SPI_current->atomic = (options & SPI_OPT_NONATOMIC ? false : true);
     144       98338 :     _SPI_current->internal_xact = false;
     145       98338 :     _SPI_current->outer_processed = SPI_processed;
     146       98338 :     _SPI_current->outer_tuptable = SPI_tuptable;
     147       98338 :     _SPI_current->outer_result = SPI_result;
     148             : 
     149             :     /*
     150             :      * Create memory contexts for this procedure
     151             :      *
     152             :      * In atomic contexts (the normal case), we use TopTransactionContext,
     153             :      * otherwise PortalContext, so that it lives across transaction
     154             :      * boundaries.
     155             :      *
     156             :      * XXX It could be better to use PortalContext as the parent context in
     157             :      * all cases, but we may not be inside a portal (consider deferred-trigger
     158             :      * execution).  Perhaps CurTransactionContext could be an option?  For now
     159             :      * it doesn't matter because we clean up explicitly in AtEOSubXact_SPI();
     160             :      * but see also AtEOXact_SPI().
     161             :      */
     162       98338 :     _SPI_current->procCxt = AllocSetContextCreate(_SPI_current->atomic ? TopTransactionContext : PortalContext,
     163             :                                                   "SPI Proc",
     164             :                                                   ALLOCSET_DEFAULT_SIZES);
     165       98338 :     _SPI_current->execCxt = AllocSetContextCreate(_SPI_current->atomic ? TopTransactionContext : _SPI_current->procCxt,
     166             :                                                   "SPI Exec",
     167             :                                                   ALLOCSET_DEFAULT_SIZES);
     168             :     /* ... and switch to procedure's context */
     169       98338 :     _SPI_current->savedcxt = MemoryContextSwitchTo(_SPI_current->procCxt);
     170             : 
     171             :     /*
     172             :      * Reset API global variables so that current caller cannot accidentally
     173             :      * depend on state of an outer caller.
     174             :      */
     175       98338 :     SPI_processed = 0;
     176       98338 :     SPI_tuptable = NULL;
     177       98338 :     SPI_result = 0;
     178             : 
     179       98338 :     return SPI_OK_CONNECT;
     180             : }
     181             : 
     182             : int
     183       95856 : SPI_finish(void)
     184             : {
     185             :     int         res;
     186             : 
     187       95856 :     res = _SPI_begin_call(false);   /* just check we're connected */
     188       95856 :     if (res < 0)
     189           0 :         return res;
     190             : 
     191             :     /* Restore memory context as it was before procedure call */
     192       95856 :     MemoryContextSwitchTo(_SPI_current->savedcxt);
     193             : 
     194             :     /* Release memory used in procedure call (including tuptables) */
     195       95856 :     MemoryContextDelete(_SPI_current->execCxt);
     196       95856 :     _SPI_current->execCxt = NULL;
     197       95856 :     MemoryContextDelete(_SPI_current->procCxt);
     198       95856 :     _SPI_current->procCxt = NULL;
     199             : 
     200             :     /*
     201             :      * Restore outer API variables, especially SPI_tuptable which is probably
     202             :      * pointing at a just-deleted tuptable
     203             :      */
     204       95856 :     SPI_processed = _SPI_current->outer_processed;
     205       95856 :     SPI_tuptable = _SPI_current->outer_tuptable;
     206       95856 :     SPI_result = _SPI_current->outer_result;
     207             : 
     208             :     /* Exit stack level */
     209       95856 :     _SPI_connected--;
     210       95856 :     if (_SPI_connected < 0)
     211       83720 :         _SPI_current = NULL;
     212             :     else
     213       12136 :         _SPI_current = &(_SPI_stack[_SPI_connected]);
     214             : 
     215       95856 :     return SPI_OK_FINISH;
     216             : }
     217             : 
     218             : /*
     219             :  * SPI_start_transaction is a no-op, kept for backwards compatibility.
     220             :  * SPI callers are *always* inside a transaction.
     221             :  */
     222             : void
     223           0 : SPI_start_transaction(void)
     224             : {
     225           0 : }
     226             : 
     227             : static void
     228        4288 : _SPI_commit(bool chain)
     229             : {
     230        4288 :     MemoryContext oldcontext = CurrentMemoryContext;
     231             :     SavedTransactionCharacteristics savetc;
     232             : 
     233             :     /*
     234             :      * Complain if we are in a context that doesn't permit transaction
     235             :      * termination.  (Note: here and _SPI_rollback should be the only places
     236             :      * that throw ERRCODE_INVALID_TRANSACTION_TERMINATION, so that callers can
     237             :      * test for that with security that they know what happened.)
     238             :      */
     239        4288 :     if (_SPI_current->atomic)
     240          32 :         ereport(ERROR,
     241             :                 (errcode(ERRCODE_INVALID_TRANSACTION_TERMINATION),
     242             :                  errmsg("invalid transaction termination")));
     243             : 
     244             :     /*
     245             :      * This restriction is required by PLs implemented on top of SPI.  They
     246             :      * use subtransactions to establish exception blocks that are supposed to
     247             :      * be rolled back together if there is an error.  Terminating the
     248             :      * top-level transaction in such a block violates that idea.  A future PL
     249             :      * implementation might have different ideas about this, in which case
     250             :      * this restriction would have to be refined or the check possibly be
     251             :      * moved out of SPI into the PLs.  Note however that the code below relies
     252             :      * on not being within a subtransaction.
     253             :      */
     254        4256 :     if (IsSubTransaction())
     255           6 :         ereport(ERROR,
     256             :                 (errcode(ERRCODE_INVALID_TRANSACTION_TERMINATION),
     257             :                  errmsg("cannot commit while a subtransaction is active")));
     258             : 
     259        4250 :     if (chain)
     260           4 :         SaveTransactionCharacteristics(&savetc);
     261             : 
     262             :     /* Catch any error occurring during the COMMIT */
     263        4250 :     PG_TRY();
     264             :     {
     265             :         /* Protect current SPI stack entry against deletion */
     266        4250 :         _SPI_current->internal_xact = true;
     267             : 
     268             :         /*
     269             :          * Hold any pinned portals that any PLs might be using.  We have to do
     270             :          * this before changing transaction state, since this will run
     271             :          * user-defined code that might throw an error.
     272             :          */
     273        4250 :         HoldPinnedPortals();
     274             : 
     275             :         /* Release snapshots associated with portals */
     276        4248 :         ForgetPortalSnapshots();
     277             : 
     278             :         /* Do the deed */
     279        4248 :         CommitTransactionCommand();
     280             : 
     281             :         /* Immediately start a new transaction */
     282        4234 :         StartTransactionCommand();
     283        4234 :         if (chain)
     284           4 :             RestoreTransactionCharacteristics(&savetc);
     285             : 
     286        4234 :         MemoryContextSwitchTo(oldcontext);
     287             : 
     288        4234 :         _SPI_current->internal_xact = false;
     289             :     }
     290          16 :     PG_CATCH();
     291             :     {
     292             :         ErrorData  *edata;
     293             : 
     294             :         /* Save error info in caller's context */
     295          16 :         MemoryContextSwitchTo(oldcontext);
     296          16 :         edata = CopyErrorData();
     297          16 :         FlushErrorState();
     298             : 
     299             :         /*
     300             :          * Abort the failed transaction.  If this fails too, we'll just
     301             :          * propagate the error out ... there's not that much we can do.
     302             :          */
     303          16 :         AbortCurrentTransaction();
     304             : 
     305             :         /* ... and start a new one */
     306          16 :         StartTransactionCommand();
     307          16 :         if (chain)
     308           0 :             RestoreTransactionCharacteristics(&savetc);
     309             : 
     310          16 :         MemoryContextSwitchTo(oldcontext);
     311             : 
     312          16 :         _SPI_current->internal_xact = false;
     313             : 
     314             :         /* Now that we've cleaned up the transaction, re-throw the error */
     315          16 :         ReThrowError(edata);
     316             :     }
     317        4234 :     PG_END_TRY();
     318        4234 : }
     319             : 
     320             : void
     321        4284 : SPI_commit(void)
     322             : {
     323        4284 :     _SPI_commit(false);
     324        4230 : }
     325             : 
     326             : void
     327           4 : SPI_commit_and_chain(void)
     328             : {
     329           4 :     _SPI_commit(true);
     330           4 : }
     331             : 
     332             : static void
     333         164 : _SPI_rollback(bool chain)
     334             : {
     335         164 :     MemoryContext oldcontext = CurrentMemoryContext;
     336             :     SavedTransactionCharacteristics savetc;
     337             : 
     338             :     /* see under SPI_commit() */
     339         164 :     if (_SPI_current->atomic)
     340           0 :         ereport(ERROR,
     341             :                 (errcode(ERRCODE_INVALID_TRANSACTION_TERMINATION),
     342             :                  errmsg("invalid transaction termination")));
     343             : 
     344             :     /* see under SPI_commit() */
     345         164 :     if (IsSubTransaction())
     346           4 :         ereport(ERROR,
     347             :                 (errcode(ERRCODE_INVALID_TRANSACTION_TERMINATION),
     348             :                  errmsg("cannot roll back while a subtransaction is active")));
     349             : 
     350         160 :     if (chain)
     351           4 :         SaveTransactionCharacteristics(&savetc);
     352             : 
     353             :     /* Catch any error occurring during the ROLLBACK */
     354         160 :     PG_TRY();
     355             :     {
     356             :         /* Protect current SPI stack entry against deletion */
     357         160 :         _SPI_current->internal_xact = true;
     358             : 
     359             :         /*
     360             :          * Hold any pinned portals that any PLs might be using.  We have to do
     361             :          * this before changing transaction state, since this will run
     362             :          * user-defined code that might throw an error, and in any case
     363             :          * couldn't be run in an already-aborted transaction.
     364             :          */
     365         160 :         HoldPinnedPortals();
     366             : 
     367             :         /* Release snapshots associated with portals */
     368         156 :         ForgetPortalSnapshots();
     369             : 
     370             :         /* Do the deed */
     371         156 :         AbortCurrentTransaction();
     372             : 
     373             :         /* Immediately start a new transaction */
     374         156 :         StartTransactionCommand();
     375         156 :         if (chain)
     376           4 :             RestoreTransactionCharacteristics(&savetc);
     377             : 
     378         156 :         MemoryContextSwitchTo(oldcontext);
     379             : 
     380         156 :         _SPI_current->internal_xact = false;
     381             :     }
     382           4 :     PG_CATCH();
     383             :     {
     384             :         ErrorData  *edata;
     385             : 
     386             :         /* Save error info in caller's context */
     387           4 :         MemoryContextSwitchTo(oldcontext);
     388           4 :         edata = CopyErrorData();
     389           4 :         FlushErrorState();
     390             : 
     391             :         /*
     392             :          * Try again to abort the failed transaction.  If this fails too,
     393             :          * we'll just propagate the error out ... there's not that much we can
     394             :          * do.
     395             :          */
     396           4 :         AbortCurrentTransaction();
     397             : 
     398             :         /* ... and start a new one */
     399           4 :         StartTransactionCommand();
     400           4 :         if (chain)
     401           0 :             RestoreTransactionCharacteristics(&savetc);
     402             : 
     403           4 :         MemoryContextSwitchTo(oldcontext);
     404             : 
     405           4 :         _SPI_current->internal_xact = false;
     406             : 
     407             :         /* Now that we've cleaned up the transaction, re-throw the error */
     408           4 :         ReThrowError(edata);
     409             :     }
     410         156 :     PG_END_TRY();
     411         156 : }
     412             : 
     413             : void
     414         160 : SPI_rollback(void)
     415             : {
     416         160 :     _SPI_rollback(false);
     417         152 : }
     418             : 
     419             : void
     420           4 : SPI_rollback_and_chain(void)
     421             : {
     422           4 :     _SPI_rollback(true);
     423           4 : }
     424             : 
     425             : /*
     426             :  * Clean up SPI state at transaction commit or abort.
     427             :  */
     428             : void
     429      514090 : AtEOXact_SPI(bool isCommit)
     430             : {
     431      514090 :     bool        found = false;
     432             : 
     433             :     /*
     434             :      * Pop stack entries, stopping if we find one marked internal_xact (that
     435             :      * one belongs to the caller of SPI_commit or SPI_rollback).
     436             :      */
     437      516360 :     while (_SPI_connected >= 0)
     438             :     {
     439        6680 :         _SPI_connection *connection = &(_SPI_stack[_SPI_connected]);
     440             : 
     441        6680 :         if (connection->internal_xact)
     442        4410 :             break;
     443             : 
     444        2270 :         found = true;
     445             : 
     446             :         /*
     447             :          * We need not release the procedure's memory contexts explicitly, as
     448             :          * they'll go away automatically when their parent context does; see
     449             :          * notes in SPI_connect_ext.
     450             :          */
     451             : 
     452             :         /*
     453             :          * Restore outer global variables and pop the stack entry.  Unlike
     454             :          * SPI_finish(), we don't risk switching to memory contexts that might
     455             :          * be already gone.
     456             :          */
     457        2270 :         SPI_processed = connection->outer_processed;
     458        2270 :         SPI_tuptable = connection->outer_tuptable;
     459        2270 :         SPI_result = connection->outer_result;
     460             : 
     461        2270 :         _SPI_connected--;
     462        2270 :         if (_SPI_connected < 0)
     463        2196 :             _SPI_current = NULL;
     464             :         else
     465          74 :             _SPI_current = &(_SPI_stack[_SPI_connected]);
     466             :     }
     467             : 
     468             :     /* We should only find entries to pop during an ABORT. */
     469      514090 :     if (found && isCommit)
     470           0 :         ereport(WARNING,
     471             :                 (errcode(ERRCODE_WARNING),
     472             :                  errmsg("transaction left non-empty SPI stack"),
     473             :                  errhint("Check for missing \"SPI_finish\" calls.")));
     474      514090 : }
     475             : 
     476             : /*
     477             :  * Clean up SPI state at subtransaction commit or abort.
     478             :  *
     479             :  * During commit, there shouldn't be any unclosed entries remaining from
     480             :  * the current subtransaction; we emit a warning if any are found.
     481             :  */
     482             : void
     483       17992 : AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid)
     484             : {
     485       17992 :     bool        found = false;
     486             : 
     487       18204 :     while (_SPI_connected >= 0)
     488             :     {
     489       13828 :         _SPI_connection *connection = &(_SPI_stack[_SPI_connected]);
     490             : 
     491       13828 :         if (connection->connectSubid != mySubid)
     492       13616 :             break;              /* couldn't be any underneath it either */
     493             : 
     494         212 :         if (connection->internal_xact)
     495           0 :             break;
     496             : 
     497         212 :         found = true;
     498             : 
     499             :         /*
     500             :          * Release procedure memory explicitly (see note in SPI_connect)
     501             :          */
     502         212 :         if (connection->execCxt)
     503             :         {
     504         212 :             MemoryContextDelete(connection->execCxt);
     505         212 :             connection->execCxt = NULL;
     506             :         }
     507         212 :         if (connection->procCxt)
     508             :         {
     509         212 :             MemoryContextDelete(connection->procCxt);
     510         212 :             connection->procCxt = NULL;
     511             :         }
     512             : 
     513             :         /*
     514             :          * Restore outer global variables and pop the stack entry.  Unlike
     515             :          * SPI_finish(), we don't risk switching to memory contexts that might
     516             :          * be already gone.
     517             :          */
     518         212 :         SPI_processed = connection->outer_processed;
     519         212 :         SPI_tuptable = connection->outer_tuptable;
     520         212 :         SPI_result = connection->outer_result;
     521             : 
     522         212 :         _SPI_connected--;
     523         212 :         if (_SPI_connected < 0)
     524          72 :             _SPI_current = NULL;
     525             :         else
     526         140 :             _SPI_current = &(_SPI_stack[_SPI_connected]);
     527             :     }
     528             : 
     529       17992 :     if (found && isCommit)
     530           0 :         ereport(WARNING,
     531             :                 (errcode(ERRCODE_WARNING),
     532             :                  errmsg("subtransaction left non-empty SPI stack"),
     533             :                  errhint("Check for missing \"SPI_finish\" calls.")));
     534             : 
     535             :     /*
     536             :      * If we are aborting a subtransaction and there is an open SPI context
     537             :      * surrounding the subxact, clean up to prevent memory leakage.
     538             :      */
     539       17992 :     if (_SPI_current && !isCommit)
     540             :     {
     541             :         slist_mutable_iter siter;
     542             : 
     543             :         /*
     544             :          * Throw away executor state if current executor operation was started
     545             :          * within current subxact (essentially, force a _SPI_end_call(true)).
     546             :          */
     547        6318 :         if (_SPI_current->execSubid >= mySubid)
     548             :         {
     549        5528 :             _SPI_current->execSubid = InvalidSubTransactionId;
     550        5528 :             MemoryContextReset(_SPI_current->execCxt);
     551             :         }
     552             : 
     553             :         /* throw away any tuple tables created within current subxact */
     554       15322 :         slist_foreach_modify(siter, &_SPI_current->tuptables)
     555             :         {
     556             :             SPITupleTable *tuptable;
     557             : 
     558        9004 :             tuptable = slist_container(SPITupleTable, next, siter.cur);
     559        9004 :             if (tuptable->subid >= mySubid)
     560             :             {
     561             :                 /*
     562             :                  * If we used SPI_freetuptable() here, its internal search of
     563             :                  * the tuptables list would make this operation O(N^2).
     564             :                  * Instead, just free the tuptable manually.  This should
     565             :                  * match what SPI_freetuptable() does.
     566             :                  */
     567        5296 :                 slist_delete_current(&siter);
     568        5296 :                 if (tuptable == _SPI_current->tuptable)
     569        5290 :                     _SPI_current->tuptable = NULL;
     570        5296 :                 if (tuptable == SPI_tuptable)
     571           6 :                     SPI_tuptable = NULL;
     572        5296 :                 MemoryContextDelete(tuptable->tuptabcxt);
     573             :             }
     574             :         }
     575             :     }
     576       17992 : }
     577             : 
     578             : /*
     579             :  * Are we executing inside a procedure (that is, a nonatomic SPI context)?
     580             :  */
     581             : bool
     582      506314 : SPI_inside_nonatomic_context(void)
     583             : {
     584      506314 :     if (_SPI_current == NULL)
     585      501904 :         return false;           /* not in any SPI context at all */
     586        4410 :     if (_SPI_current->atomic)
     587           0 :         return false;           /* it's atomic (ie function not procedure) */
     588        4410 :     return true;
     589             : }
     590             : 
     591             : 
     592             : /* Parse, plan, and execute a query string */
     593             : int
     594        1316 : SPI_execute(const char *src, bool read_only, long tcount)
     595             : {
     596             :     _SPI_plan   plan;
     597             :     SPIExecuteOptions options;
     598             :     int         res;
     599             : 
     600        1316 :     if (src == NULL || tcount < 0)
     601           0 :         return SPI_ERROR_ARGUMENT;
     602             : 
     603        1316 :     res = _SPI_begin_call(true);
     604        1316 :     if (res < 0)
     605           0 :         return res;
     606             : 
     607        1316 :     memset(&plan, 0, sizeof(_SPI_plan));
     608        1316 :     plan.magic = _SPI_PLAN_MAGIC;
     609        1316 :     plan.parse_mode = RAW_PARSE_DEFAULT;
     610        1316 :     plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
     611             : 
     612        1316 :     _SPI_prepare_oneshot_plan(src, &plan);
     613             : 
     614        1300 :     memset(&options, 0, sizeof(options));
     615        1300 :     options.read_only = read_only;
     616        1300 :     options.tcount = tcount;
     617             : 
     618        1300 :     res = _SPI_execute_plan(&plan, &options,
     619             :                             InvalidSnapshot, InvalidSnapshot,
     620             :                             true);
     621             : 
     622        1246 :     _SPI_end_call(true);
     623        1246 :     return res;
     624             : }
     625             : 
     626             : /* Obsolete version of SPI_execute */
     627             : int
     628         392 : SPI_exec(const char *src, long tcount)
     629             : {
     630         392 :     return SPI_execute(src, false, tcount);
     631             : }
     632             : 
     633             : /* Parse, plan, and execute a query string, with extensible options */
     634             : int
     635       11020 : SPI_execute_extended(const char *src,
     636             :                      const SPIExecuteOptions *options)
     637             : {
     638             :     int         res;
     639             :     _SPI_plan   plan;
     640             : 
     641       11020 :     if (src == NULL || options == NULL)
     642           0 :         return SPI_ERROR_ARGUMENT;
     643             : 
     644       11020 :     res = _SPI_begin_call(true);
     645       11020 :     if (res < 0)
     646           0 :         return res;
     647             : 
     648       11020 :     memset(&plan, 0, sizeof(_SPI_plan));
     649       11020 :     plan.magic = _SPI_PLAN_MAGIC;
     650       11020 :     plan.parse_mode = RAW_PARSE_DEFAULT;
     651       11020 :     plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
     652       11020 :     if (options->params)
     653             :     {
     654         570 :         plan.parserSetup = options->params->parserSetup;
     655         570 :         plan.parserSetupArg = options->params->parserSetupArg;
     656             :     }
     657             : 
     658       11020 :     _SPI_prepare_oneshot_plan(src, &plan);
     659             : 
     660       11020 :     res = _SPI_execute_plan(&plan, options,
     661             :                             InvalidSnapshot, InvalidSnapshot,
     662             :                             true);
     663             : 
     664       10818 :     _SPI_end_call(true);
     665       10818 :     return res;
     666             : }
     667             : 
     668             : /* Execute a previously prepared plan */
     669             : int
     670        4012 : SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
     671             :                  bool read_only, long tcount)
     672             : {
     673             :     SPIExecuteOptions options;
     674             :     int         res;
     675             : 
     676        4012 :     if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
     677           0 :         return SPI_ERROR_ARGUMENT;
     678             : 
     679        4012 :     if (plan->nargs > 0 && Values == NULL)
     680           0 :         return SPI_ERROR_PARAM;
     681             : 
     682        4012 :     res = _SPI_begin_call(true);
     683        4012 :     if (res < 0)
     684           0 :         return res;
     685             : 
     686        4012 :     memset(&options, 0, sizeof(options));
     687        4012 :     options.params = _SPI_convert_params(plan->nargs, plan->argtypes,
     688             :                                          Values, Nulls);
     689        4012 :     options.read_only = read_only;
     690        4012 :     options.tcount = tcount;
     691             : 
     692        4012 :     res = _SPI_execute_plan(plan, &options,
     693             :                             InvalidSnapshot, InvalidSnapshot,
     694             :                             true);
     695             : 
     696        4000 :     _SPI_end_call(true);
     697        4000 :     return res;
     698             : }
     699             : 
     700             : /* Obsolete version of SPI_execute_plan */
     701             : int
     702         186 : SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls, long tcount)
     703             : {
     704         186 :     return SPI_execute_plan(plan, Values, Nulls, false, tcount);
     705             : }
     706             : 
     707             : /* Execute a previously prepared plan */
     708             : int
     709        2512 : SPI_execute_plan_extended(SPIPlanPtr plan,
     710             :                           const SPIExecuteOptions *options)
     711             : {
     712             :     int         res;
     713             : 
     714        2512 :     if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || options == NULL)
     715           0 :         return SPI_ERROR_ARGUMENT;
     716             : 
     717        2512 :     res = _SPI_begin_call(true);
     718        2512 :     if (res < 0)
     719           0 :         return res;
     720             : 
     721        2512 :     res = _SPI_execute_plan(plan, options,
     722             :                             InvalidSnapshot, InvalidSnapshot,
     723             :                             true);
     724             : 
     725        2504 :     _SPI_end_call(true);
     726        2504 :     return res;
     727             : }
     728             : 
     729             : /* Execute a previously prepared plan */
     730             : int
     731       61622 : SPI_execute_plan_with_paramlist(SPIPlanPtr plan, ParamListInfo params,
     732             :                                 bool read_only, long tcount)
     733             : {
     734             :     SPIExecuteOptions options;
     735             :     int         res;
     736             : 
     737       61622 :     if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
     738           0 :         return SPI_ERROR_ARGUMENT;
     739             : 
     740       61622 :     res = _SPI_begin_call(true);
     741       61622 :     if (res < 0)
     742           0 :         return res;
     743             : 
     744       61622 :     memset(&options, 0, sizeof(options));
     745       61622 :     options.params = params;
     746       61622 :     options.read_only = read_only;
     747       61622 :     options.tcount = tcount;
     748             : 
     749       61622 :     res = _SPI_execute_plan(plan, &options,
     750             :                             InvalidSnapshot, InvalidSnapshot,
     751             :                             true);
     752             : 
     753       56266 :     _SPI_end_call(true);
     754       56266 :     return res;
     755             : }
     756             : 
     757             : /*
     758             :  * SPI_execute_snapshot -- identical to SPI_execute_plan, except that we allow
     759             :  * the caller to specify exactly which snapshots to use, which will be
     760             :  * registered here.  Also, the caller may specify that AFTER triggers should be
     761             :  * queued as part of the outer query rather than being fired immediately at the
     762             :  * end of the command.
     763             :  *
     764             :  * This is currently not documented in spi.sgml because it is only intended
     765             :  * for use by RI triggers.
     766             :  *
     767             :  * Passing snapshot == InvalidSnapshot will select the normal behavior of
     768             :  * fetching a new snapshot for each query.
     769             :  */
     770             : int
     771        6814 : SPI_execute_snapshot(SPIPlanPtr plan,
     772             :                      Datum *Values, const char *Nulls,
     773             :                      Snapshot snapshot, Snapshot crosscheck_snapshot,
     774             :                      bool read_only, bool fire_triggers, long tcount)
     775             : {
     776             :     SPIExecuteOptions options;
     777             :     int         res;
     778             : 
     779        6814 :     if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
     780           0 :         return SPI_ERROR_ARGUMENT;
     781             : 
     782        6814 :     if (plan->nargs > 0 && Values == NULL)
     783           0 :         return SPI_ERROR_PARAM;
     784             : 
     785        6814 :     res = _SPI_begin_call(true);
     786        6814 :     if (res < 0)
     787           0 :         return res;
     788             : 
     789        6814 :     memset(&options, 0, sizeof(options));
     790        6814 :     options.params = _SPI_convert_params(plan->nargs, plan->argtypes,
     791             :                                          Values, Nulls);
     792        6814 :     options.read_only = read_only;
     793        6814 :     options.tcount = tcount;
     794             : 
     795        6814 :     res = _SPI_execute_plan(plan, &options,
     796             :                             snapshot, crosscheck_snapshot,
     797             :                             fire_triggers);
     798             : 
     799        6800 :     _SPI_end_call(true);
     800        6800 :     return res;
     801             : }
     802             : 
     803             : /*
     804             :  * SPI_execute_with_args -- plan and execute a query with supplied arguments
     805             :  *
     806             :  * This is functionally equivalent to SPI_prepare followed by
     807             :  * SPI_execute_plan.
     808             :  */
     809             : int
     810           0 : SPI_execute_with_args(const char *src,
     811             :                       int nargs, Oid *argtypes,
     812             :                       Datum *Values, const char *Nulls,
     813             :                       bool read_only, long tcount)
     814             : {
     815             :     int         res;
     816             :     _SPI_plan   plan;
     817             :     ParamListInfo paramLI;
     818             :     SPIExecuteOptions options;
     819             : 
     820           0 :     if (src == NULL || nargs < 0 || tcount < 0)
     821           0 :         return SPI_ERROR_ARGUMENT;
     822             : 
     823           0 :     if (nargs > 0 && (argtypes == NULL || Values == NULL))
     824           0 :         return SPI_ERROR_PARAM;
     825             : 
     826           0 :     res = _SPI_begin_call(true);
     827           0 :     if (res < 0)
     828           0 :         return res;
     829             : 
     830           0 :     memset(&plan, 0, sizeof(_SPI_plan));
     831           0 :     plan.magic = _SPI_PLAN_MAGIC;
     832           0 :     plan.parse_mode = RAW_PARSE_DEFAULT;
     833           0 :     plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
     834           0 :     plan.nargs = nargs;
     835           0 :     plan.argtypes = argtypes;
     836           0 :     plan.parserSetup = NULL;
     837           0 :     plan.parserSetupArg = NULL;
     838             : 
     839           0 :     paramLI = _SPI_convert_params(nargs, argtypes,
     840             :                                   Values, Nulls);
     841             : 
     842           0 :     _SPI_prepare_oneshot_plan(src, &plan);
     843             : 
     844           0 :     memset(&options, 0, sizeof(options));
     845           0 :     options.params = paramLI;
     846           0 :     options.read_only = read_only;
     847           0 :     options.tcount = tcount;
     848             : 
     849           0 :     res = _SPI_execute_plan(&plan, &options,
     850             :                             InvalidSnapshot, InvalidSnapshot,
     851             :                             true);
     852             : 
     853           0 :     _SPI_end_call(true);
     854           0 :     return res;
     855             : }
     856             : 
     857             : SPIPlanPtr
     858        4742 : SPI_prepare(const char *src, int nargs, Oid *argtypes)
     859             : {
     860        4742 :     return SPI_prepare_cursor(src, nargs, argtypes, 0);
     861             : }
     862             : 
     863             : SPIPlanPtr
     864        4742 : SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
     865             :                    int cursorOptions)
     866             : {
     867             :     _SPI_plan   plan;
     868             :     SPIPlanPtr  result;
     869             : 
     870        4742 :     if (src == NULL || nargs < 0 || (nargs > 0 && argtypes == NULL))
     871             :     {
     872           0 :         SPI_result = SPI_ERROR_ARGUMENT;
     873           0 :         return NULL;
     874             :     }
     875             : 
     876        4742 :     SPI_result = _SPI_begin_call(true);
     877        4742 :     if (SPI_result < 0)
     878           0 :         return NULL;
     879             : 
     880        4742 :     memset(&plan, 0, sizeof(_SPI_plan));
     881        4742 :     plan.magic = _SPI_PLAN_MAGIC;
     882        4742 :     plan.parse_mode = RAW_PARSE_DEFAULT;
     883        4742 :     plan.cursor_options = cursorOptions;
     884        4742 :     plan.nargs = nargs;
     885        4742 :     plan.argtypes = argtypes;
     886        4742 :     plan.parserSetup = NULL;
     887        4742 :     plan.parserSetupArg = NULL;
     888             : 
     889        4742 :     _SPI_prepare_plan(src, &plan);
     890             : 
     891             :     /* copy plan to procedure context */
     892        4740 :     result = _SPI_make_plan_non_temp(&plan);
     893             : 
     894        4740 :     _SPI_end_call(true);
     895             : 
     896        4740 :     return result;
     897             : }
     898             : 
     899             : SPIPlanPtr
     900       24042 : SPI_prepare_extended(const char *src,
     901             :                      const SPIPrepareOptions *options)
     902             : {
     903             :     _SPI_plan   plan;
     904             :     SPIPlanPtr  result;
     905             : 
     906       24042 :     if (src == NULL || options == NULL)
     907             :     {
     908           0 :         SPI_result = SPI_ERROR_ARGUMENT;
     909           0 :         return NULL;
     910             :     }
     911             : 
     912       24042 :     SPI_result = _SPI_begin_call(true);
     913       24042 :     if (SPI_result < 0)
     914           0 :         return NULL;
     915             : 
     916       24042 :     memset(&plan, 0, sizeof(_SPI_plan));
     917       24042 :     plan.magic = _SPI_PLAN_MAGIC;
     918       24042 :     plan.parse_mode = options->parseMode;
     919       24042 :     plan.cursor_options = options->cursorOptions;
     920       24042 :     plan.nargs = 0;
     921       24042 :     plan.argtypes = NULL;
     922       24042 :     plan.parserSetup = options->parserSetup;
     923       24042 :     plan.parserSetupArg = options->parserSetupArg;
     924             : 
     925       24042 :     _SPI_prepare_plan(src, &plan);
     926             : 
     927             :     /* copy plan to procedure context */
     928       23956 :     result = _SPI_make_plan_non_temp(&plan);
     929             : 
     930       23956 :     _SPI_end_call(true);
     931             : 
     932       23956 :     return result;
     933             : }
     934             : 
     935             : SPIPlanPtr
     936           0 : SPI_prepare_params(const char *src,
     937             :                    ParserSetupHook parserSetup,
     938             :                    void *parserSetupArg,
     939             :                    int cursorOptions)
     940             : {
     941             :     _SPI_plan   plan;
     942             :     SPIPlanPtr  result;
     943             : 
     944           0 :     if (src == NULL)
     945             :     {
     946           0 :         SPI_result = SPI_ERROR_ARGUMENT;
     947           0 :         return NULL;
     948             :     }
     949             : 
     950           0 :     SPI_result = _SPI_begin_call(true);
     951           0 :     if (SPI_result < 0)
     952           0 :         return NULL;
     953             : 
     954           0 :     memset(&plan, 0, sizeof(_SPI_plan));
     955           0 :     plan.magic = _SPI_PLAN_MAGIC;
     956           0 :     plan.parse_mode = RAW_PARSE_DEFAULT;
     957           0 :     plan.cursor_options = cursorOptions;
     958           0 :     plan.nargs = 0;
     959           0 :     plan.argtypes = NULL;
     960           0 :     plan.parserSetup = parserSetup;
     961           0 :     plan.parserSetupArg = parserSetupArg;
     962             : 
     963           0 :     _SPI_prepare_plan(src, &plan);
     964             : 
     965             :     /* copy plan to procedure context */
     966           0 :     result = _SPI_make_plan_non_temp(&plan);
     967             : 
     968           0 :     _SPI_end_call(true);
     969             : 
     970           0 :     return result;
     971             : }
     972             : 
     973             : int
     974       27334 : SPI_keepplan(SPIPlanPtr plan)
     975             : {
     976             :     ListCell   *lc;
     977             : 
     978       27334 :     if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC ||
     979       27334 :         plan->saved || plan->oneshot)
     980           0 :         return SPI_ERROR_ARGUMENT;
     981             : 
     982             :     /*
     983             :      * Mark it saved, reparent it under CacheMemoryContext, and mark all the
     984             :      * component CachedPlanSources as saved.  This sequence cannot fail
     985             :      * partway through, so there's no risk of long-term memory leakage.
     986             :      */
     987       27334 :     plan->saved = true;
     988       27334 :     MemoryContextSetParent(plan->plancxt, CacheMemoryContext);
     989             : 
     990       54668 :     foreach(lc, plan->plancache_list)
     991             :     {
     992       27334 :         CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
     993             : 
     994       27334 :         SaveCachedPlan(plansource);
     995             :     }
     996             : 
     997       27334 :     return 0;
     998             : }
     999             : 
    1000             : SPIPlanPtr
    1001           0 : SPI_saveplan(SPIPlanPtr plan)
    1002             : {
    1003             :     SPIPlanPtr  newplan;
    1004             : 
    1005           0 :     if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
    1006             :     {
    1007           0 :         SPI_result = SPI_ERROR_ARGUMENT;
    1008           0 :         return NULL;
    1009             :     }
    1010             : 
    1011           0 :     SPI_result = _SPI_begin_call(false);    /* don't change context */
    1012           0 :     if (SPI_result < 0)
    1013           0 :         return NULL;
    1014             : 
    1015           0 :     newplan = _SPI_save_plan(plan);
    1016             : 
    1017           0 :     SPI_result = _SPI_end_call(false);
    1018             : 
    1019           0 :     return newplan;
    1020             : }
    1021             : 
    1022             : int
    1023        4974 : SPI_freeplan(SPIPlanPtr plan)
    1024             : {
    1025             :     ListCell   *lc;
    1026             : 
    1027        4974 :     if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
    1028           0 :         return SPI_ERROR_ARGUMENT;
    1029             : 
    1030             :     /* Release the plancache entries */
    1031        9948 :     foreach(lc, plan->plancache_list)
    1032             :     {
    1033        4974 :         CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
    1034             : 
    1035        4974 :         DropCachedPlan(plansource);
    1036             :     }
    1037             : 
    1038             :     /* Now get rid of the _SPI_plan and subsidiary data in its plancxt */
    1039        4974 :     MemoryContextDelete(plan->plancxt);
    1040             : 
    1041        4974 :     return 0;
    1042             : }
    1043             : 
    1044             : HeapTuple
    1045        2058 : SPI_copytuple(HeapTuple tuple)
    1046             : {
    1047             :     MemoryContext oldcxt;
    1048             :     HeapTuple   ctuple;
    1049             : 
    1050        2058 :     if (tuple == NULL)
    1051             :     {
    1052           0 :         SPI_result = SPI_ERROR_ARGUMENT;
    1053           0 :         return NULL;
    1054             :     }
    1055             : 
    1056        2058 :     if (_SPI_current == NULL)
    1057             :     {
    1058           0 :         SPI_result = SPI_ERROR_UNCONNECTED;
    1059           0 :         return NULL;
    1060             :     }
    1061             : 
    1062        2058 :     oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
    1063             : 
    1064        2058 :     ctuple = heap_copytuple(tuple);
    1065             : 
    1066        2058 :     MemoryContextSwitchTo(oldcxt);
    1067             : 
    1068        2058 :     return ctuple;
    1069             : }
    1070             : 
    1071             : HeapTupleHeader
    1072        6096 : SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
    1073             : {
    1074             :     MemoryContext oldcxt;
    1075             :     HeapTupleHeader dtup;
    1076             : 
    1077        6096 :     if (tuple == NULL || tupdesc == NULL)
    1078             :     {
    1079           0 :         SPI_result = SPI_ERROR_ARGUMENT;
    1080           0 :         return NULL;
    1081             :     }
    1082             : 
    1083        6096 :     if (_SPI_current == NULL)
    1084             :     {
    1085           0 :         SPI_result = SPI_ERROR_UNCONNECTED;
    1086           0 :         return NULL;
    1087             :     }
    1088             : 
    1089             :     /* For RECORD results, make sure a typmod has been assigned */
    1090        6096 :     if (tupdesc->tdtypeid == RECORDOID &&
    1091        6080 :         tupdesc->tdtypmod < 0)
    1092           0 :         assign_record_type_typmod(tupdesc);
    1093             : 
    1094        6096 :     oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
    1095             : 
    1096        6096 :     dtup = DatumGetHeapTupleHeader(heap_copy_tuple_as_datum(tuple, tupdesc));
    1097             : 
    1098        6096 :     MemoryContextSwitchTo(oldcxt);
    1099             : 
    1100        6096 :     return dtup;
    1101             : }
    1102             : 
    1103             : HeapTuple
    1104          12 : SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
    1105             :                 Datum *Values, const char *Nulls)
    1106             : {
    1107             :     MemoryContext oldcxt;
    1108             :     HeapTuple   mtuple;
    1109             :     int         numberOfAttributes;
    1110             :     Datum      *v;
    1111             :     bool       *n;
    1112             :     int         i;
    1113             : 
    1114          12 :     if (rel == NULL || tuple == NULL || natts < 0 || attnum == NULL || Values == NULL)
    1115             :     {
    1116           0 :         SPI_result = SPI_ERROR_ARGUMENT;
    1117           0 :         return NULL;
    1118             :     }
    1119             : 
    1120          12 :     if (_SPI_current == NULL)
    1121             :     {
    1122           0 :         SPI_result = SPI_ERROR_UNCONNECTED;
    1123           0 :         return NULL;
    1124             :     }
    1125             : 
    1126          12 :     oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
    1127             : 
    1128          12 :     SPI_result = 0;
    1129             : 
    1130          12 :     numberOfAttributes = rel->rd_att->natts;
    1131          12 :     v = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
    1132          12 :     n = (bool *) palloc(numberOfAttributes * sizeof(bool));
    1133             : 
    1134             :     /* fetch old values and nulls */
    1135          12 :     heap_deform_tuple(tuple, rel->rd_att, v, n);
    1136             : 
    1137             :     /* replace values and nulls */
    1138          24 :     for (i = 0; i < natts; i++)
    1139             :     {
    1140          12 :         if (attnum[i] <= 0 || attnum[i] > numberOfAttributes)
    1141             :             break;
    1142          12 :         v[attnum[i] - 1] = Values[i];
    1143          12 :         n[attnum[i] - 1] = (Nulls && Nulls[i] == 'n');
    1144             :     }
    1145             : 
    1146          12 :     if (i == natts)             /* no errors in *attnum */
    1147             :     {
    1148          12 :         mtuple = heap_form_tuple(rel->rd_att, v, n);
    1149             : 
    1150             :         /*
    1151             :          * copy the identification info of the old tuple: t_ctid, t_self, and
    1152             :          * OID (if any)
    1153             :          */
    1154          12 :         mtuple->t_data->t_ctid = tuple->t_data->t_ctid;
    1155          12 :         mtuple->t_self = tuple->t_self;
    1156          12 :         mtuple->t_tableOid = tuple->t_tableOid;
    1157             :     }
    1158             :     else
    1159             :     {
    1160           0 :         mtuple = NULL;
    1161           0 :         SPI_result = SPI_ERROR_NOATTRIBUTE;
    1162             :     }
    1163             : 
    1164          12 :     pfree(v);
    1165          12 :     pfree(n);
    1166             : 
    1167          12 :     MemoryContextSwitchTo(oldcxt);
    1168             : 
    1169          12 :     return mtuple;
    1170             : }
    1171             : 
    1172             : int
    1173       19130 : SPI_fnumber(TupleDesc tupdesc, const char *fname)
    1174             : {
    1175             :     int         res;
    1176             :     const FormData_pg_attribute *sysatt;
    1177             : 
    1178      101612 :     for (res = 0; res < tupdesc->natts; res++)
    1179             :     {
    1180      101598 :         Form_pg_attribute attr = TupleDescAttr(tupdesc, res);
    1181             : 
    1182      101598 :         if (namestrcmp(&attr->attname, fname) == 0 &&
    1183       19116 :             !attr->attisdropped)
    1184       19116 :             return res + 1;
    1185             :     }
    1186             : 
    1187          14 :     sysatt = SystemAttributeByName(fname);
    1188          14 :     if (sysatt != NULL)
    1189           0 :         return sysatt->attnum;
    1190             : 
    1191             :     /* SPI_ERROR_NOATTRIBUTE is different from all sys column numbers */
    1192          14 :     return SPI_ERROR_NOATTRIBUTE;
    1193             : }
    1194             : 
    1195             : char *
    1196         972 : SPI_fname(TupleDesc tupdesc, int fnumber)
    1197             : {
    1198             :     const FormData_pg_attribute *att;
    1199             : 
    1200         972 :     SPI_result = 0;
    1201             : 
    1202         972 :     if (fnumber > tupdesc->natts || fnumber == 0 ||
    1203             :         fnumber <= FirstLowInvalidHeapAttributeNumber)
    1204             :     {
    1205           0 :         SPI_result = SPI_ERROR_NOATTRIBUTE;
    1206           0 :         return NULL;
    1207             :     }
    1208             : 
    1209         972 :     if (fnumber > 0)
    1210         972 :         att = TupleDescAttr(tupdesc, fnumber - 1);
    1211             :     else
    1212           0 :         att = SystemAttributeDefinition(fnumber);
    1213             : 
    1214         972 :     return pstrdup(NameStr(att->attname));
    1215             : }
    1216             : 
    1217             : char *
    1218        8324 : SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
    1219             : {
    1220             :     Datum       val;
    1221             :     bool        isnull;
    1222             :     Oid         typoid,
    1223             :                 foutoid;
    1224             :     bool        typisvarlena;
    1225             : 
    1226        8324 :     SPI_result = 0;
    1227             : 
    1228        8324 :     if (fnumber > tupdesc->natts || fnumber == 0 ||
    1229             :         fnumber <= FirstLowInvalidHeapAttributeNumber)
    1230             :     {
    1231           0 :         SPI_result = SPI_ERROR_NOATTRIBUTE;
    1232           0 :         return NULL;
    1233             :     }
    1234             : 
    1235        8324 :     val = heap_getattr(tuple, fnumber, tupdesc, &isnull);
    1236        8324 :     if (isnull)
    1237         130 :         return NULL;
    1238             : 
    1239        8194 :     if (fnumber > 0)
    1240        8194 :         typoid = TupleDescAttr(tupdesc, fnumber - 1)->atttypid;
    1241             :     else
    1242           0 :         typoid = (SystemAttributeDefinition(fnumber))->atttypid;
    1243             : 
    1244        8194 :     getTypeOutputInfo(typoid, &foutoid, &typisvarlena);
    1245             : 
    1246        8194 :     return OidOutputFunctionCall(foutoid, val);
    1247             : }
    1248             : 
    1249             : Datum
    1250       56504 : SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
    1251             : {
    1252       56504 :     SPI_result = 0;
    1253             : 
    1254       56504 :     if (fnumber > tupdesc->natts || fnumber == 0 ||
    1255             :         fnumber <= FirstLowInvalidHeapAttributeNumber)
    1256             :     {
    1257           0 :         SPI_result = SPI_ERROR_NOATTRIBUTE;
    1258           0 :         *isnull = true;
    1259           0 :         return (Datum) NULL;
    1260             :     }
    1261             : 
    1262       56504 :     return heap_getattr(tuple, fnumber, tupdesc, isnull);
    1263             : }
    1264             : 
    1265             : char *
    1266           0 : SPI_gettype(TupleDesc tupdesc, int fnumber)
    1267             : {
    1268             :     Oid         typoid;
    1269             :     HeapTuple   typeTuple;
    1270             :     char       *result;
    1271             : 
    1272           0 :     SPI_result = 0;
    1273             : 
    1274           0 :     if (fnumber > tupdesc->natts || fnumber == 0 ||
    1275             :         fnumber <= FirstLowInvalidHeapAttributeNumber)
    1276             :     {
    1277           0 :         SPI_result = SPI_ERROR_NOATTRIBUTE;
    1278           0 :         return NULL;
    1279             :     }
    1280             : 
    1281           0 :     if (fnumber > 0)
    1282           0 :         typoid = TupleDescAttr(tupdesc, fnumber - 1)->atttypid;
    1283             :     else
    1284           0 :         typoid = (SystemAttributeDefinition(fnumber))->atttypid;
    1285             : 
    1286           0 :     typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typoid));
    1287             : 
    1288           0 :     if (!HeapTupleIsValid(typeTuple))
    1289             :     {
    1290           0 :         SPI_result = SPI_ERROR_TYPUNKNOWN;
    1291           0 :         return NULL;
    1292             :     }
    1293             : 
    1294           0 :     result = pstrdup(NameStr(((Form_pg_type) GETSTRUCT(typeTuple))->typname));
    1295           0 :     ReleaseSysCache(typeTuple);
    1296           0 :     return result;
    1297             : }
    1298             : 
    1299             : /*
    1300             :  * Get the data type OID for a column.
    1301             :  *
    1302             :  * There's nothing similar for typmod and typcollation.  The rare consumers
    1303             :  * thereof should inspect the TupleDesc directly.
    1304             :  */
    1305             : Oid
    1306        1362 : SPI_gettypeid(TupleDesc tupdesc, int fnumber)
    1307             : {
    1308        1362 :     SPI_result = 0;
    1309             : 
    1310        1362 :     if (fnumber > tupdesc->natts || fnumber == 0 ||
    1311             :         fnumber <= FirstLowInvalidHeapAttributeNumber)
    1312             :     {
    1313           0 :         SPI_result = SPI_ERROR_NOATTRIBUTE;
    1314           0 :         return InvalidOid;
    1315             :     }
    1316             : 
    1317        1362 :     if (fnumber > 0)
    1318        1362 :         return TupleDescAttr(tupdesc, fnumber - 1)->atttypid;
    1319             :     else
    1320           0 :         return (SystemAttributeDefinition(fnumber))->atttypid;
    1321             : }
    1322             : 
    1323             : char *
    1324         468 : SPI_getrelname(Relation rel)
    1325             : {
    1326         468 :     return pstrdup(RelationGetRelationName(rel));
    1327             : }
    1328             : 
    1329             : char *
    1330         276 : SPI_getnspname(Relation rel)
    1331             : {
    1332         276 :     return get_namespace_name(RelationGetNamespace(rel));
    1333             : }
    1334             : 
    1335             : void *
    1336          38 : SPI_palloc(Size size)
    1337             : {
    1338          38 :     if (_SPI_current == NULL)
    1339           0 :         elog(ERROR, "SPI_palloc called while not connected to SPI");
    1340             : 
    1341          38 :     return MemoryContextAlloc(_SPI_current->savedcxt, size);
    1342             : }
    1343             : 
    1344             : void *
    1345           0 : SPI_repalloc(void *pointer, Size size)
    1346             : {
    1347             :     /* No longer need to worry which context chunk was in... */
    1348           0 :     return repalloc(pointer, size);
    1349             : }
    1350             : 
    1351             : void
    1352           0 : SPI_pfree(void *pointer)
    1353             : {
    1354             :     /* No longer need to worry which context chunk was in... */
    1355           0 :     pfree(pointer);
    1356           0 : }
    1357             : 
    1358             : Datum
    1359        5818 : SPI_datumTransfer(Datum value, bool typByVal, int typLen)
    1360             : {
    1361             :     MemoryContext oldcxt;
    1362             :     Datum       result;
    1363             : 
    1364        5818 :     if (_SPI_current == NULL)
    1365           0 :         elog(ERROR, "SPI_datumTransfer called while not connected to SPI");
    1366             : 
    1367        5818 :     oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
    1368             : 
    1369        5818 :     result = datumTransfer(value, typByVal, typLen);
    1370             : 
    1371        5818 :     MemoryContextSwitchTo(oldcxt);
    1372             : 
    1373        5818 :     return result;
    1374             : }
    1375             : 
    1376             : void
    1377           0 : SPI_freetuple(HeapTuple tuple)
    1378             : {
    1379             :     /* No longer need to worry which context tuple was in... */
    1380           0 :     heap_freetuple(tuple);
    1381           0 : }
    1382             : 
    1383             : void
    1384      155202 : SPI_freetuptable(SPITupleTable *tuptable)
    1385             : {
    1386      155202 :     bool        found = false;
    1387             : 
    1388             :     /* ignore call if NULL pointer */
    1389      155202 :     if (tuptable == NULL)
    1390       82272 :         return;
    1391             : 
    1392             :     /*
    1393             :      * Search only the topmost SPI context for a matching tuple table.
    1394             :      */
    1395       72930 :     if (_SPI_current != NULL)
    1396             :     {
    1397             :         slist_mutable_iter siter;
    1398             : 
    1399             :         /* find tuptable in active list, then remove it */
    1400       72930 :         slist_foreach_modify(siter, &_SPI_current->tuptables)
    1401             :         {
    1402             :             SPITupleTable *tt;
    1403             : 
    1404       72930 :             tt = slist_container(SPITupleTable, next, siter.cur);
    1405       72930 :             if (tt == tuptable)
    1406             :             {
    1407       72930 :                 slist_delete_current(&siter);
    1408       72930 :                 found = true;
    1409       72930 :                 break;
    1410             :             }
    1411             :         }
    1412             :     }
    1413             : 
    1414             :     /*
    1415             :      * Refuse the deletion if we didn't find it in the topmost SPI context.
    1416             :      * This is primarily a guard against double deletion, but might prevent
    1417             :      * other errors as well.  Since the worst consequence of not deleting a
    1418             :      * tuptable would be a transient memory leak, this is just a WARNING.
    1419             :      */
    1420       72930 :     if (!found)
    1421             :     {
    1422           0 :         elog(WARNING, "attempt to delete invalid SPITupleTable %p", tuptable);
    1423           0 :         return;
    1424             :     }
    1425             : 
    1426             :     /* for safety, reset global variables that might point at tuptable */
    1427       72930 :     if (tuptable == _SPI_current->tuptable)
    1428           0 :         _SPI_current->tuptable = NULL;
    1429       72930 :     if (tuptable == SPI_tuptable)
    1430       65326 :         SPI_tuptable = NULL;
    1431             : 
    1432             :     /* release all memory belonging to tuptable */
    1433       72930 :     MemoryContextDelete(tuptable->tuptabcxt);
    1434             : }
    1435             : 
    1436             : 
    1437             : /*
    1438             :  * SPI_cursor_open()
    1439             :  *
    1440             :  *  Open a prepared SPI plan as a portal
    1441             :  */
    1442             : Portal
    1443         214 : SPI_cursor_open(const char *name, SPIPlanPtr plan,
    1444             :                 Datum *Values, const char *Nulls,
    1445             :                 bool read_only)
    1446             : {
    1447             :     Portal      portal;
    1448             :     ParamListInfo paramLI;
    1449             : 
    1450             :     /* build transient ParamListInfo in caller's context */
    1451         214 :     paramLI = _SPI_convert_params(plan->nargs, plan->argtypes,
    1452             :                                   Values, Nulls);
    1453             : 
    1454         214 :     portal = SPI_cursor_open_internal(name, plan, paramLI, read_only);
    1455             : 
    1456             :     /* done with the transient ParamListInfo */
    1457         214 :     if (paramLI)
    1458           8 :         pfree(paramLI);
    1459             : 
    1460         214 :     return portal;
    1461             : }
    1462             : 
    1463             : 
    1464             : /*
    1465             :  * SPI_cursor_open_with_args()
    1466             :  *
    1467             :  * Parse and plan a query and open it as a portal.
    1468             :  */
    1469             : Portal
    1470           0 : SPI_cursor_open_with_args(const char *name,
    1471             :                           const char *src,
    1472             :                           int nargs, Oid *argtypes,
    1473             :                           Datum *Values, const char *Nulls,
    1474             :                           bool read_only, int cursorOptions)
    1475             : {
    1476             :     Portal      result;
    1477             :     _SPI_plan   plan;
    1478             :     ParamListInfo paramLI;
    1479             : 
    1480           0 :     if (src == NULL || nargs < 0)
    1481           0 :         elog(ERROR, "SPI_cursor_open_with_args called with invalid arguments");
    1482             : 
    1483           0 :     if (nargs > 0 && (argtypes == NULL || Values == NULL))
    1484           0 :         elog(ERROR, "SPI_cursor_open_with_args called with missing parameters");
    1485             : 
    1486           0 :     SPI_result = _SPI_begin_call(true);
    1487           0 :     if (SPI_result < 0)
    1488           0 :         elog(ERROR, "SPI_cursor_open_with_args called while not connected");
    1489             : 
    1490           0 :     memset(&plan, 0, sizeof(_SPI_plan));
    1491           0 :     plan.magic = _SPI_PLAN_MAGIC;
    1492           0 :     plan.parse_mode = RAW_PARSE_DEFAULT;
    1493           0 :     plan.cursor_options = cursorOptions;
    1494           0 :     plan.nargs = nargs;
    1495           0 :     plan.argtypes = argtypes;
    1496           0 :     plan.parserSetup = NULL;
    1497           0 :     plan.parserSetupArg = NULL;
    1498             : 
    1499             :     /* build transient ParamListInfo in executor context */
    1500           0 :     paramLI = _SPI_convert_params(nargs, argtypes,
    1501             :                                   Values, Nulls);
    1502             : 
    1503           0 :     _SPI_prepare_plan(src, &plan);
    1504             : 
    1505             :     /* We needn't copy the plan; SPI_cursor_open_internal will do so */
    1506             : 
    1507           0 :     result = SPI_cursor_open_internal(name, &plan, paramLI, read_only);
    1508             : 
    1509             :     /* And clean up */
    1510           0 :     _SPI_end_call(true);
    1511             : 
    1512           0 :     return result;
    1513             : }
    1514             : 
    1515             : 
    1516             : /*
    1517             :  * SPI_cursor_open_with_paramlist()
    1518             :  *
    1519             :  *  Same as SPI_cursor_open except that parameters (if any) are passed
    1520             :  *  as a ParamListInfo, which supports dynamic parameter set determination
    1521             :  */
    1522             : Portal
    1523        2520 : SPI_cursor_open_with_paramlist(const char *name, SPIPlanPtr plan,
    1524             :                                ParamListInfo params, bool read_only)
    1525             : {
    1526        2520 :     return SPI_cursor_open_internal(name, plan, params, read_only);
    1527             : }
    1528             : 
    1529             : /* Parse a query and open it as a cursor */
    1530             : Portal
    1531        9158 : SPI_cursor_parse_open(const char *name,
    1532             :                       const char *src,
    1533             :                       const SPIParseOpenOptions *options)
    1534             : {
    1535             :     Portal      result;
    1536             :     _SPI_plan   plan;
    1537             : 
    1538        9158 :     if (src == NULL || options == NULL)
    1539           0 :         elog(ERROR, "SPI_cursor_parse_open called with invalid arguments");
    1540             : 
    1541        9158 :     SPI_result = _SPI_begin_call(true);
    1542        9158 :     if (SPI_result < 0)
    1543           0 :         elog(ERROR, "SPI_cursor_parse_open called while not connected");
    1544             : 
    1545        9158 :     memset(&plan, 0, sizeof(_SPI_plan));
    1546        9158 :     plan.magic = _SPI_PLAN_MAGIC;
    1547        9158 :     plan.parse_mode = RAW_PARSE_DEFAULT;
    1548        9158 :     plan.cursor_options = options->cursorOptions;
    1549        9158 :     if (options->params)
    1550             :     {
    1551          12 :         plan.parserSetup = options->params->parserSetup;
    1552          12 :         plan.parserSetupArg = options->params->parserSetupArg;
    1553             :     }
    1554             : 
    1555        9158 :     _SPI_prepare_plan(src, &plan);
    1556             : 
    1557             :     /* We needn't copy the plan; SPI_cursor_open_internal will do so */
    1558             : 
    1559        9158 :     result = SPI_cursor_open_internal(name, &plan,
    1560        9158 :                                       options->params, options->read_only);
    1561             : 
    1562             :     /* And clean up */
    1563        9158 :     _SPI_end_call(true);
    1564             : 
    1565        9158 :     return result;
    1566             : }
    1567             : 
    1568             : 
    1569             : /*
    1570             :  * SPI_cursor_open_internal()
    1571             :  *
    1572             :  *  Common code for SPI_cursor_open variants
    1573             :  */
    1574             : static Portal
    1575       11892 : SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
    1576             :                          ParamListInfo paramLI, bool read_only)
    1577             : {
    1578             :     CachedPlanSource *plansource;
    1579             :     CachedPlan *cplan;
    1580             :     List       *stmt_list;
    1581             :     char       *query_string;
    1582             :     Snapshot    snapshot;
    1583             :     MemoryContext oldcontext;
    1584             :     Portal      portal;
    1585             :     SPICallbackArg spicallbackarg;
    1586             :     ErrorContextCallback spierrcontext;
    1587             : 
    1588             :     /*
    1589             :      * Check that the plan is something the Portal code will special-case as
    1590             :      * returning one tupleset.
    1591             :      */
    1592       11892 :     if (!SPI_is_cursor_plan(plan))
    1593             :     {
    1594             :         /* try to give a good error message */
    1595             :         const char *cmdtag;
    1596             : 
    1597           0 :         if (list_length(plan->plancache_list) != 1)
    1598           0 :             ereport(ERROR,
    1599             :                     (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
    1600             :                      errmsg("cannot open multi-query plan as cursor")));
    1601           0 :         plansource = (CachedPlanSource *) linitial(plan->plancache_list);
    1602             :         /* A SELECT that fails SPI_is_cursor_plan() must be SELECT INTO */
    1603           0 :         if (plansource->commandTag == CMDTAG_SELECT)
    1604           0 :             cmdtag = "SELECT INTO";
    1605             :         else
    1606           0 :             cmdtag = GetCommandTagName(plansource->commandTag);
    1607           0 :         ereport(ERROR,
    1608             :                 (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
    1609             :         /* translator: %s is name of a SQL command, eg INSERT */
    1610             :                  errmsg("cannot open %s query as cursor", cmdtag)));
    1611             :     }
    1612             : 
    1613             :     Assert(list_length(plan->plancache_list) == 1);
    1614       11892 :     plansource = (CachedPlanSource *) linitial(plan->plancache_list);
    1615             : 
    1616             :     /* Push the SPI stack */
    1617       11892 :     if (_SPI_begin_call(true) < 0)
    1618           0 :         elog(ERROR, "SPI_cursor_open called while not connected");
    1619             : 
    1620             :     /* Reset SPI result (note we deliberately don't touch lastoid) */
    1621       11892 :     SPI_processed = 0;
    1622       11892 :     SPI_tuptable = NULL;
    1623       11892 :     _SPI_current->processed = 0;
    1624       11892 :     _SPI_current->tuptable = NULL;
    1625             : 
    1626             :     /* Create the portal */
    1627       11892 :     if (name == NULL || name[0] == '\0')
    1628             :     {
    1629             :         /* Use a random nonconflicting name */
    1630       11844 :         portal = CreateNewPortal();
    1631             :     }
    1632             :     else
    1633             :     {
    1634             :         /* In this path, error if portal of same name already exists */
    1635          48 :         portal = CreatePortal(name, false, false);
    1636             :     }
    1637             : 
    1638             :     /* Copy the plan's query string into the portal */
    1639       11892 :     query_string = MemoryContextStrdup(portal->portalContext,
    1640             :                                        plansource->query_string);
    1641             : 
    1642             :     /*
    1643             :      * Setup error traceback support for ereport(), in case GetCachedPlan
    1644             :      * throws an error.
    1645             :      */
    1646       11892 :     spicallbackarg.query = plansource->query_string;
    1647       11892 :     spicallbackarg.mode = plan->parse_mode;
    1648       11892 :     spierrcontext.callback = _SPI_error_callback;
    1649       11892 :     spierrcontext.arg = &spicallbackarg;
    1650       11892 :     spierrcontext.previous = error_context_stack;
    1651       11892 :     error_context_stack = &spierrcontext;
    1652             : 
    1653             :     /*
    1654             :      * Note: for a saved plan, we mustn't have any failure occur between
    1655             :      * GetCachedPlan and PortalDefineQuery; that would result in leaking our
    1656             :      * plancache refcount.
    1657             :      */
    1658             : 
    1659             :     /* Replan if needed, and increment plan refcount for portal */
    1660       11892 :     cplan = GetCachedPlan(plansource, paramLI, NULL, _SPI_current->queryEnv);
    1661       11892 :     stmt_list = cplan->stmt_list;
    1662             : 
    1663       11892 :     if (!plan->saved)
    1664             :     {
    1665             :         /*
    1666             :          * We don't want the portal to depend on an unsaved CachedPlanSource,
    1667             :          * so must copy the plan into the portal's context.  An error here
    1668             :          * will result in leaking our refcount on the plan, but it doesn't
    1669             :          * matter because the plan is unsaved and hence transient anyway.
    1670             :          */
    1671        9360 :         oldcontext = MemoryContextSwitchTo(portal->portalContext);
    1672        9360 :         stmt_list = copyObject(stmt_list);
    1673        9360 :         MemoryContextSwitchTo(oldcontext);
    1674        9360 :         ReleaseCachedPlan(cplan, NULL);
    1675        9360 :         cplan = NULL;           /* portal shouldn't depend on cplan */
    1676             :     }
    1677             : 
    1678             :     /*
    1679             :      * Set up the portal.
    1680             :      */
    1681       11892 :     PortalDefineQuery(portal,
    1682             :                       NULL,     /* no statement name */
    1683             :                       query_string,
    1684             :                       plansource->commandTag,
    1685             :                       stmt_list,
    1686             :                       cplan);
    1687             : 
    1688             :     /*
    1689             :      * Set up options for portal.  Default SCROLL type is chosen the same way
    1690             :      * as PerformCursorOpen does it.
    1691             :      */
    1692       11892 :     portal->cursorOptions = plan->cursor_options;
    1693       11892 :     if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
    1694             :     {
    1695         420 :         if (list_length(stmt_list) == 1 &&
    1696         420 :             linitial_node(PlannedStmt, stmt_list)->commandType != CMD_UTILITY &&
    1697         838 :             linitial_node(PlannedStmt, stmt_list)->rowMarks == NIL &&
    1698         418 :             ExecSupportsBackwardScan(linitial_node(PlannedStmt, stmt_list)->planTree))
    1699         388 :             portal->cursorOptions |= CURSOR_OPT_SCROLL;
    1700             :         else
    1701          32 :             portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
    1702             :     }
    1703             : 
    1704             :     /*
    1705             :      * Disallow SCROLL with SELECT FOR UPDATE.  This is not redundant with the
    1706             :      * check in transformDeclareCursorStmt because the cursor options might
    1707             :      * not have come through there.
    1708             :      */
    1709       11892 :     if (portal->cursorOptions & CURSOR_OPT_SCROLL)
    1710             :     {
    1711         412 :         if (list_length(stmt_list) == 1 &&
    1712         412 :             linitial_node(PlannedStmt, stmt_list)->commandType != CMD_UTILITY &&
    1713         412 :             linitial_node(PlannedStmt, stmt_list)->rowMarks != NIL)
    1714           0 :             ereport(ERROR,
    1715             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1716             :                      errmsg("DECLARE SCROLL CURSOR ... FOR UPDATE/SHARE is not supported"),
    1717             :                      errdetail("Scrollable cursors must be READ ONLY.")));
    1718             :     }
    1719             : 
    1720             :     /* Make current query environment available to portal at execution time. */
    1721       11892 :     portal->queryEnv = _SPI_current->queryEnv;
    1722             : 
    1723             :     /*
    1724             :      * If told to be read-only, we'd better check for read-only queries. This
    1725             :      * can't be done earlier because we need to look at the finished, planned
    1726             :      * queries.  (In particular, we don't want to do it between GetCachedPlan
    1727             :      * and PortalDefineQuery, because throwing an error between those steps
    1728             :      * would result in leaking our plancache refcount.)
    1729             :      */
    1730       11892 :     if (read_only)
    1731             :     {
    1732             :         ListCell   *lc;
    1733             : 
    1734         312 :         foreach(lc, stmt_list)
    1735             :         {
    1736         156 :             PlannedStmt *pstmt = lfirst_node(PlannedStmt, lc);
    1737             : 
    1738         156 :             if (!CommandIsReadOnly(pstmt))
    1739           0 :                 ereport(ERROR,
    1740             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1741             :                 /* translator: %s is a SQL statement name */
    1742             :                          errmsg("%s is not allowed in a non-volatile function",
    1743             :                                 CreateCommandName((Node *) pstmt))));
    1744             :         }
    1745             :     }
    1746             : 
    1747             :     /* Set up the snapshot to use. */
    1748       11892 :     if (read_only)
    1749         156 :         snapshot = GetActiveSnapshot();
    1750             :     else
    1751             :     {
    1752       11736 :         CommandCounterIncrement();
    1753       11736 :         snapshot = GetTransactionSnapshot();
    1754             :     }
    1755             : 
    1756             :     /*
    1757             :      * If the plan has parameters, copy them into the portal.  Note that this
    1758             :      * must be done after revalidating the plan, because in dynamic parameter
    1759             :      * cases the set of parameters could have changed during re-parsing.
    1760             :      */
    1761       11892 :     if (paramLI)
    1762             :     {
    1763         694 :         oldcontext = MemoryContextSwitchTo(portal->portalContext);
    1764         694 :         paramLI = copyParamList(paramLI);
    1765         694 :         MemoryContextSwitchTo(oldcontext);
    1766             :     }
    1767             : 
    1768             :     /*
    1769             :      * Start portal execution.
    1770             :      */
    1771       11892 :     PortalStart(portal, paramLI, 0, snapshot);
    1772             : 
    1773             :     Assert(portal->strategy != PORTAL_MULTI_QUERY);
    1774             : 
    1775             :     /* Pop the error context stack */
    1776       11892 :     error_context_stack = spierrcontext.previous;
    1777             : 
    1778             :     /* Pop the SPI stack */
    1779       11892 :     _SPI_end_call(true);
    1780             : 
    1781             :     /* Return the created portal */
    1782       11892 :     return portal;
    1783             : }
    1784             : 
    1785             : 
    1786             : /*
    1787             :  * SPI_cursor_find()
    1788             :  *
    1789             :  *  Find the portal of an existing open cursor
    1790             :  */
    1791             : Portal
    1792         560 : SPI_cursor_find(const char *name)
    1793             : {
    1794         560 :     return GetPortalByName(name);
    1795             : }
    1796             : 
    1797             : 
    1798             : /*
    1799             :  * SPI_cursor_fetch()
    1800             :  *
    1801             :  *  Fetch rows in a cursor
    1802             :  */
    1803             : void
    1804       43164 : SPI_cursor_fetch(Portal portal, bool forward, long count)
    1805             : {
    1806       43164 :     _SPI_cursor_operation(portal,
    1807       43164 :                           forward ? FETCH_FORWARD : FETCH_BACKWARD, count,
    1808             :                           CreateDestReceiver(DestSPI));
    1809             :     /* we know that the DestSPI receiver doesn't need a destroy call */
    1810       43156 : }
    1811             : 
    1812             : 
    1813             : /*
    1814             :  * SPI_cursor_move()
    1815             :  *
    1816             :  *  Move in a cursor
    1817             :  */
    1818             : void
    1819           0 : SPI_cursor_move(Portal portal, bool forward, long count)
    1820             : {
    1821           0 :     _SPI_cursor_operation(portal,
    1822           0 :                           forward ? FETCH_FORWARD : FETCH_BACKWARD, count,
    1823             :                           None_Receiver);
    1824           0 : }
    1825             : 
    1826             : 
    1827             : /*
    1828             :  * SPI_scroll_cursor_fetch()
    1829             :  *
    1830             :  *  Fetch rows in a scrollable cursor
    1831             :  */
    1832             : void
    1833         300 : SPI_scroll_cursor_fetch(Portal portal, FetchDirection direction, long count)
    1834             : {
    1835         300 :     _SPI_cursor_operation(portal,
    1836             :                           direction, count,
    1837             :                           CreateDestReceiver(DestSPI));
    1838             :     /* we know that the DestSPI receiver doesn't need a destroy call */
    1839         294 : }
    1840             : 
    1841             : 
    1842             : /*
    1843             :  * SPI_scroll_cursor_move()
    1844             :  *
    1845             :  *  Move in a scrollable cursor
    1846             :  */
    1847             : void
    1848          42 : SPI_scroll_cursor_move(Portal portal, FetchDirection direction, long count)
    1849             : {
    1850          42 :     _SPI_cursor_operation(portal, direction, count, None_Receiver);
    1851          42 : }
    1852             : 
    1853             : 
    1854             : /*
    1855             :  * SPI_cursor_close()
    1856             :  *
    1857             :  *  Close a cursor
    1858             :  */
    1859             : void
    1860       11784 : SPI_cursor_close(Portal portal)
    1861             : {
    1862       11784 :     if (!PortalIsValid(portal))
    1863           0 :         elog(ERROR, "invalid portal in SPI cursor operation");
    1864             : 
    1865       11784 :     PortalDrop(portal, false);
    1866       11784 : }
    1867             : 
    1868             : /*
    1869             :  * Returns the Oid representing the type id for argument at argIndex. First
    1870             :  * parameter is at index zero.
    1871             :  */
    1872             : Oid
    1873           0 : SPI_getargtypeid(SPIPlanPtr plan, int argIndex)
    1874             : {
    1875           0 :     if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC ||
    1876           0 :         argIndex < 0 || argIndex >= plan->nargs)
    1877             :     {
    1878           0 :         SPI_result = SPI_ERROR_ARGUMENT;
    1879           0 :         return InvalidOid;
    1880             :     }
    1881           0 :     return plan->argtypes[argIndex];
    1882             : }
    1883             : 
    1884             : /*
    1885             :  * Returns the number of arguments for the prepared plan.
    1886             :  */
    1887             : int
    1888           0 : SPI_getargcount(SPIPlanPtr plan)
    1889             : {
    1890           0 :     if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
    1891             :     {
    1892           0 :         SPI_result = SPI_ERROR_ARGUMENT;
    1893           0 :         return -1;
    1894             :     }
    1895           0 :     return plan->nargs;
    1896             : }
    1897             : 
    1898             : /*
    1899             :  * Returns true if the plan contains exactly one command
    1900             :  * and that command returns tuples to the caller (eg, SELECT or
    1901             :  * INSERT ... RETURNING, but not SELECT ... INTO). In essence,
    1902             :  * the result indicates if the command can be used with SPI_cursor_open
    1903             :  *
    1904             :  * Parameters
    1905             :  *    plan: A plan previously prepared using SPI_prepare
    1906             :  */
    1907             : bool
    1908       11892 : SPI_is_cursor_plan(SPIPlanPtr plan)
    1909             : {
    1910             :     CachedPlanSource *plansource;
    1911             : 
    1912       11892 :     if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
    1913             :     {
    1914           0 :         SPI_result = SPI_ERROR_ARGUMENT;
    1915           0 :         return false;
    1916             :     }
    1917             : 
    1918       11892 :     if (list_length(plan->plancache_list) != 1)
    1919             :     {
    1920           0 :         SPI_result = 0;
    1921           0 :         return false;           /* not exactly 1 pre-rewrite command */
    1922             :     }
    1923       11892 :     plansource = (CachedPlanSource *) linitial(plan->plancache_list);
    1924             : 
    1925             :     /*
    1926             :      * We used to force revalidation of the cached plan here, but that seems
    1927             :      * unnecessary: invalidation could mean a change in the rowtype of the
    1928             :      * tuples returned by a plan, but not whether it returns tuples at all.
    1929             :      */
    1930       11892 :     SPI_result = 0;
    1931             : 
    1932             :     /* Does it return tuples? */
    1933       11892 :     if (plansource->resultDesc)
    1934       11892 :         return true;
    1935             : 
    1936           0 :     return false;
    1937             : }
    1938             : 
    1939             : /*
    1940             :  * SPI_plan_is_valid --- test whether a SPI plan is currently valid
    1941             :  * (that is, not marked as being in need of revalidation).
    1942             :  *
    1943             :  * See notes for CachedPlanIsValid before using this.
    1944             :  */
    1945             : bool
    1946        3056 : SPI_plan_is_valid(SPIPlanPtr plan)
    1947             : {
    1948             :     ListCell   *lc;
    1949             : 
    1950             :     Assert(plan->magic == _SPI_PLAN_MAGIC);
    1951             : 
    1952        5864 :     foreach(lc, plan->plancache_list)
    1953             :     {
    1954        3056 :         CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
    1955             : 
    1956        3056 :         if (!CachedPlanIsValid(plansource))
    1957         248 :             return false;
    1958             :     }
    1959        2808 :     return true;
    1960             : }
    1961             : 
    1962             : /*
    1963             :  * SPI_result_code_string --- convert any SPI return code to a string
    1964             :  *
    1965             :  * This is often useful in error messages.  Most callers will probably
    1966             :  * only pass negative (error-case) codes, but for generality we recognize
    1967             :  * the success codes too.
    1968             :  */
    1969             : const char *
    1970         118 : SPI_result_code_string(int code)
    1971             : {
    1972             :     static char buf[64];
    1973             : 
    1974         118 :     switch (code)
    1975             :     {
    1976           0 :         case SPI_ERROR_CONNECT:
    1977           0 :             return "SPI_ERROR_CONNECT";
    1978           0 :         case SPI_ERROR_COPY:
    1979           0 :             return "SPI_ERROR_COPY";
    1980           0 :         case SPI_ERROR_OPUNKNOWN:
    1981           0 :             return "SPI_ERROR_OPUNKNOWN";
    1982           0 :         case SPI_ERROR_UNCONNECTED:
    1983           0 :             return "SPI_ERROR_UNCONNECTED";
    1984           0 :         case SPI_ERROR_ARGUMENT:
    1985           0 :             return "SPI_ERROR_ARGUMENT";
    1986           0 :         case SPI_ERROR_PARAM:
    1987           0 :             return "SPI_ERROR_PARAM";
    1988           6 :         case SPI_ERROR_TRANSACTION:
    1989           6 :             return "SPI_ERROR_TRANSACTION";
    1990           0 :         case SPI_ERROR_NOATTRIBUTE:
    1991           0 :             return "SPI_ERROR_NOATTRIBUTE";
    1992           0 :         case SPI_ERROR_NOOUTFUNC:
    1993           0 :             return "SPI_ERROR_NOOUTFUNC";
    1994           0 :         case SPI_ERROR_TYPUNKNOWN:
    1995           0 :             return "SPI_ERROR_TYPUNKNOWN";
    1996           0 :         case SPI_ERROR_REL_DUPLICATE:
    1997           0 :             return "SPI_ERROR_REL_DUPLICATE";
    1998           0 :         case SPI_ERROR_REL_NOT_FOUND:
    1999           0 :             return "SPI_ERROR_REL_NOT_FOUND";
    2000           0 :         case SPI_OK_CONNECT:
    2001           0 :             return "SPI_OK_CONNECT";
    2002           0 :         case SPI_OK_FINISH:
    2003           0 :             return "SPI_OK_FINISH";
    2004           0 :         case SPI_OK_FETCH:
    2005           0 :             return "SPI_OK_FETCH";
    2006           2 :         case SPI_OK_UTILITY:
    2007           2 :             return "SPI_OK_UTILITY";
    2008          20 :         case SPI_OK_SELECT:
    2009          20 :             return "SPI_OK_SELECT";
    2010           0 :         case SPI_OK_SELINTO:
    2011           0 :             return "SPI_OK_SELINTO";
    2012          90 :         case SPI_OK_INSERT:
    2013          90 :             return "SPI_OK_INSERT";
    2014           0 :         case SPI_OK_DELETE:
    2015           0 :             return "SPI_OK_DELETE";
    2016           0 :         case SPI_OK_UPDATE:
    2017           0 :             return "SPI_OK_UPDATE";
    2018           0 :         case SPI_OK_CURSOR:
    2019           0 :             return "SPI_OK_CURSOR";
    2020           0 :         case SPI_OK_INSERT_RETURNING:
    2021           0 :             return "SPI_OK_INSERT_RETURNING";
    2022           0 :         case SPI_OK_DELETE_RETURNING:
    2023           0 :             return "SPI_OK_DELETE_RETURNING";
    2024           0 :         case SPI_OK_UPDATE_RETURNING:
    2025           0 :             return "SPI_OK_UPDATE_RETURNING";
    2026           0 :         case SPI_OK_REWRITTEN:
    2027           0 :             return "SPI_OK_REWRITTEN";
    2028           0 :         case SPI_OK_REL_REGISTER:
    2029           0 :             return "SPI_OK_REL_REGISTER";
    2030           0 :         case SPI_OK_REL_UNREGISTER:
    2031           0 :             return "SPI_OK_REL_UNREGISTER";
    2032           0 :         case SPI_OK_TD_REGISTER:
    2033           0 :             return "SPI_OK_TD_REGISTER";
    2034           0 :         case SPI_OK_MERGE:
    2035           0 :             return "SPI_OK_MERGE";
    2036             :     }
    2037             :     /* Unrecognized code ... return something useful ... */
    2038           0 :     sprintf(buf, "Unrecognized SPI code %d", code);
    2039           0 :     return buf;
    2040             : }
    2041             : 
    2042             : /*
    2043             :  * SPI_plan_get_plan_sources --- get a SPI plan's underlying list of
    2044             :  * CachedPlanSources.
    2045             :  *
    2046             :  * This is exported so that PL/pgSQL can use it (this beats letting PL/pgSQL
    2047             :  * look directly into the SPIPlan for itself).  It's not documented in
    2048             :  * spi.sgml because we'd just as soon not have too many places using this.
    2049             :  */
    2050             : List *
    2051       25916 : SPI_plan_get_plan_sources(SPIPlanPtr plan)
    2052             : {
    2053             :     Assert(plan->magic == _SPI_PLAN_MAGIC);
    2054       25916 :     return plan->plancache_list;
    2055             : }
    2056             : 
    2057             : /*
    2058             :  * SPI_plan_get_cached_plan --- get a SPI plan's generic CachedPlan,
    2059             :  * if the SPI plan contains exactly one CachedPlanSource.  If not,
    2060             :  * return NULL.
    2061             :  *
    2062             :  * The plan's refcount is incremented (and logged in CurrentResourceOwner,
    2063             :  * if it's a saved plan).  Caller is responsible for doing ReleaseCachedPlan.
    2064             :  *
    2065             :  * This is exported so that PL/pgSQL can use it (this beats letting PL/pgSQL
    2066             :  * look directly into the SPIPlan for itself).  It's not documented in
    2067             :  * spi.sgml because we'd just as soon not have too many places using this.
    2068             :  */
    2069             : CachedPlan *
    2070       25888 : SPI_plan_get_cached_plan(SPIPlanPtr plan)
    2071             : {
    2072             :     CachedPlanSource *plansource;
    2073             :     CachedPlan *cplan;
    2074             :     SPICallbackArg spicallbackarg;
    2075             :     ErrorContextCallback spierrcontext;
    2076             : 
    2077             :     Assert(plan->magic == _SPI_PLAN_MAGIC);
    2078             : 
    2079             :     /* Can't support one-shot plans here */
    2080       25888 :     if (plan->oneshot)
    2081           0 :         return NULL;
    2082             : 
    2083             :     /* Must have exactly one CachedPlanSource */
    2084       25888 :     if (list_length(plan->plancache_list) != 1)
    2085           0 :         return NULL;
    2086       25888 :     plansource = (CachedPlanSource *) linitial(plan->plancache_list);
    2087             : 
    2088             :     /* Setup error traceback support for ereport() */
    2089       25888 :     spicallbackarg.query = plansource->query_string;
    2090       25888 :     spicallbackarg.mode = plan->parse_mode;
    2091       25888 :     spierrcontext.callback = _SPI_error_callback;
    2092       25888 :     spierrcontext.arg = &spicallbackarg;
    2093       25888 :     spierrcontext.previous = error_context_stack;
    2094       25888 :     error_context_stack = &spierrcontext;
    2095             : 
    2096             :     /* Get the generic plan for the query */
    2097       25888 :     cplan = GetCachedPlan(plansource, NULL,
    2098       25888 :                           plan->saved ? CurrentResourceOwner : NULL,
    2099       25888 :                           _SPI_current->queryEnv);
    2100             :     Assert(cplan == plansource->gplan);
    2101             : 
    2102             :     /* Pop the error context stack */
    2103       25856 :     error_context_stack = spierrcontext.previous;
    2104             : 
    2105       25856 :     return cplan;
    2106             : }
    2107             : 
    2108             : 
    2109             : /* =================== private functions =================== */
    2110             : 
    2111             : /*
    2112             :  * spi_dest_startup
    2113             :  *      Initialize to receive tuples from Executor into SPITupleTable
    2114             :  *      of current SPI procedure
    2115             :  */
    2116             : void
    2117       88836 : spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
    2118             : {
    2119             :     SPITupleTable *tuptable;
    2120             :     MemoryContext oldcxt;
    2121             :     MemoryContext tuptabcxt;
    2122             : 
    2123       88836 :     if (_SPI_current == NULL)
    2124           0 :         elog(ERROR, "spi_dest_startup called while not connected to SPI");
    2125             : 
    2126       88836 :     if (_SPI_current->tuptable != NULL)
    2127           0 :         elog(ERROR, "improper call to spi_dest_startup");
    2128             : 
    2129             :     /* We create the tuple table context as a child of procCxt */
    2130             : 
    2131       88836 :     oldcxt = _SPI_procmem();    /* switch to procedure memory context */
    2132             : 
    2133       88836 :     tuptabcxt = AllocSetContextCreate(CurrentMemoryContext,
    2134             :                                       "SPI TupTable",
    2135             :                                       ALLOCSET_DEFAULT_SIZES);
    2136       88836 :     MemoryContextSwitchTo(tuptabcxt);
    2137             : 
    2138       88836 :     _SPI_current->tuptable = tuptable = (SPITupleTable *)
    2139       88836 :         palloc0(sizeof(SPITupleTable));
    2140       88836 :     tuptable->tuptabcxt = tuptabcxt;
    2141       88836 :     tuptable->subid = GetCurrentSubTransactionId();
    2142             : 
    2143             :     /*
    2144             :      * The tuptable is now valid enough to be freed by AtEOSubXact_SPI, so put
    2145             :      * it onto the SPI context's tuptables list.  This will ensure it's not
    2146             :      * leaked even in the unlikely event the following few lines fail.
    2147             :      */
    2148       88836 :     slist_push_head(&_SPI_current->tuptables, &tuptable->next);
    2149             : 
    2150             :     /* set up initial allocations */
    2151       88836 :     tuptable->alloced = 128;
    2152       88836 :     tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
    2153       88836 :     tuptable->numvals = 0;
    2154       88836 :     tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
    2155             : 
    2156       88836 :     MemoryContextSwitchTo(oldcxt);
    2157       88836 : }
    2158             : 
    2159             : /*
    2160             :  * spi_printtup
    2161             :  *      store tuple retrieved by Executor into SPITupleTable
    2162             :  *      of current SPI procedure
    2163             :  */
    2164             : bool
    2165      106644 : spi_printtup(TupleTableSlot *slot, DestReceiver *self)
    2166             : {
    2167             :     SPITupleTable *tuptable;
    2168             :     MemoryContext oldcxt;
    2169             : 
    2170      106644 :     if (_SPI_current == NULL)
    2171           0 :         elog(ERROR, "spi_printtup called while not connected to SPI");
    2172             : 
    2173      106644 :     tuptable = _SPI_current->tuptable;
    2174      106644 :     if (tuptable == NULL)
    2175           0 :         elog(ERROR, "improper call to spi_printtup");
    2176             : 
    2177      106644 :     oldcxt = MemoryContextSwitchTo(tuptable->tuptabcxt);
    2178             : 
    2179      106644 :     if (tuptable->numvals >= tuptable->alloced)
    2180             :     {
    2181             :         /* Double the size of the pointer array */
    2182           0 :         uint64      newalloced = tuptable->alloced * 2;
    2183             : 
    2184           0 :         tuptable->vals = (HeapTuple *) repalloc_huge(tuptable->vals,
    2185             :                                                      newalloced * sizeof(HeapTuple));
    2186           0 :         tuptable->alloced = newalloced;
    2187             :     }
    2188             : 
    2189      106644 :     tuptable->vals[tuptable->numvals] = ExecCopySlotHeapTuple(slot);
    2190      106644 :     (tuptable->numvals)++;
    2191             : 
    2192      106644 :     MemoryContextSwitchTo(oldcxt);
    2193             : 
    2194      106644 :     return true;
    2195             : }
    2196             : 
    2197             : /*
    2198             :  * Static functions
    2199             :  */
    2200             : 
    2201             : /*
    2202             :  * Parse and analyze a querystring.
    2203             :  *
    2204             :  * At entry, plan->argtypes and plan->nargs (or alternatively plan->parserSetup
    2205             :  * and plan->parserSetupArg) must be valid, as must plan->parse_mode and
    2206             :  * plan->cursor_options.
    2207             :  *
    2208             :  * Results are stored into *plan (specifically, plan->plancache_list).
    2209             :  * Note that the result data is all in CurrentMemoryContext or child contexts
    2210             :  * thereof; in practice this means it is in the SPI executor context, and
    2211             :  * what we are creating is a "temporary" SPIPlan.  Cruft generated during
    2212             :  * parsing is also left in CurrentMemoryContext.
    2213             :  */
    2214             : static void
    2215       37942 : _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
    2216             : {
    2217             :     List       *raw_parsetree_list;
    2218             :     List       *plancache_list;
    2219             :     ListCell   *list_item;
    2220             :     SPICallbackArg spicallbackarg;
    2221             :     ErrorContextCallback spierrcontext;
    2222             : 
    2223             :     /*
    2224             :      * Setup error traceback support for ereport()
    2225             :      */
    2226       37942 :     spicallbackarg.query = src;
    2227       37942 :     spicallbackarg.mode = plan->parse_mode;
    2228       37942 :     spierrcontext.callback = _SPI_error_callback;
    2229       37942 :     spierrcontext.arg = &spicallbackarg;
    2230       37942 :     spierrcontext.previous = error_context_stack;
    2231       37942 :     error_context_stack = &spierrcontext;
    2232             : 
    2233             :     /*
    2234             :      * Parse the request string into a list of raw parse trees.
    2235             :      */
    2236       37942 :     raw_parsetree_list = raw_parser(src, plan->parse_mode);
    2237             : 
    2238             :     /*
    2239             :      * Do parse analysis and rule rewrite for each raw parsetree, storing the
    2240             :      * results into unsaved plancache entries.
    2241             :      */
    2242       37942 :     plancache_list = NIL;
    2243             : 
    2244       75796 :     foreach(list_item, raw_parsetree_list)
    2245             :     {
    2246       37942 :         RawStmt    *parsetree = lfirst_node(RawStmt, list_item);
    2247             :         List       *stmt_list;
    2248             :         CachedPlanSource *plansource;
    2249             : 
    2250             :         /*
    2251             :          * Create the CachedPlanSource before we do parse analysis, since it
    2252             :          * needs to see the unmodified raw parse tree.
    2253             :          */
    2254       37942 :         plansource = CreateCachedPlan(parsetree,
    2255             :                                       src,
    2256             :                                       CreateCommandTag(parsetree->stmt));
    2257             : 
    2258             :         /*
    2259             :          * Parameter datatypes are driven by parserSetup hook if provided,
    2260             :          * otherwise we use the fixed parameter list.
    2261             :          */
    2262       37942 :         if (plan->parserSetup != NULL)
    2263             :         {
    2264             :             Assert(plan->nargs == 0);
    2265       24054 :             stmt_list = pg_analyze_and_rewrite_withcb(parsetree,
    2266             :                                                       src,
    2267             :                                                       plan->parserSetup,
    2268             :                                                       plan->parserSetupArg,
    2269       24054 :                                                       _SPI_current->queryEnv);
    2270             :         }
    2271             :         else
    2272             :         {
    2273       13888 :             stmt_list = pg_analyze_and_rewrite_fixedparams(parsetree,
    2274             :                                                            src,
    2275       13888 :                                                            plan->argtypes,
    2276             :                                                            plan->nargs,
    2277       13888 :                                                            _SPI_current->queryEnv);
    2278             :         }
    2279             : 
    2280             :         /* Finish filling in the CachedPlanSource */
    2281       37854 :         CompleteCachedPlan(plansource,
    2282             :                            stmt_list,
    2283             :                            NULL,
    2284             :                            plan->argtypes,
    2285             :                            plan->nargs,
    2286             :                            plan->parserSetup,
    2287             :                            plan->parserSetupArg,
    2288             :                            plan->cursor_options,
    2289             :                            false);  /* not fixed result */
    2290             : 
    2291       37854 :         plancache_list = lappend(plancache_list, plansource);
    2292             :     }
    2293             : 
    2294       37854 :     plan->plancache_list = plancache_list;
    2295       37854 :     plan->oneshot = false;
    2296             : 
    2297             :     /*
    2298             :      * Pop the error context stack
    2299             :      */
    2300       37854 :     error_context_stack = spierrcontext.previous;
    2301       37854 : }
    2302             : 
    2303             : /*
    2304             :  * Parse, but don't analyze, a querystring.
    2305             :  *
    2306             :  * This is a stripped-down version of _SPI_prepare_plan that only does the
    2307             :  * initial raw parsing.  It creates "one shot" CachedPlanSources
    2308             :  * that still require parse analysis before execution is possible.
    2309             :  *
    2310             :  * The advantage of using the "one shot" form of CachedPlanSource is that
    2311             :  * we eliminate data copying and invalidation overhead.  Postponing parse
    2312             :  * analysis also prevents issues if some of the raw parsetrees are DDL
    2313             :  * commands that affect validity of later parsetrees.  Both of these
    2314             :  * attributes are good things for SPI_execute() and similar cases.
    2315             :  *
    2316             :  * Results are stored into *plan (specifically, plan->plancache_list).
    2317             :  * Note that the result data is all in CurrentMemoryContext or child contexts
    2318             :  * thereof; in practice this means it is in the SPI executor context, and
    2319             :  * what we are creating is a "temporary" SPIPlan.  Cruft generated during
    2320             :  * parsing is also left in CurrentMemoryContext.
    2321             :  */
    2322             : static void
    2323       12336 : _SPI_prepare_oneshot_plan(const char *src, SPIPlanPtr plan)
    2324             : {
    2325             :     List       *raw_parsetree_list;
    2326             :     List       *plancache_list;
    2327             :     ListCell   *list_item;
    2328             :     SPICallbackArg spicallbackarg;
    2329             :     ErrorContextCallback spierrcontext;
    2330             : 
    2331             :     /*
    2332             :      * Setup error traceback support for ereport()
    2333             :      */
    2334       12336 :     spicallbackarg.query = src;
    2335       12336 :     spicallbackarg.mode = plan->parse_mode;
    2336       12336 :     spierrcontext.callback = _SPI_error_callback;
    2337       12336 :     spierrcontext.arg = &spicallbackarg;
    2338       12336 :     spierrcontext.previous = error_context_stack;
    2339       12336 :     error_context_stack = &spierrcontext;
    2340             : 
    2341             :     /*
    2342             :      * Parse the request string into a list of raw parse trees.
    2343             :      */
    2344       12336 :     raw_parsetree_list = raw_parser(src, plan->parse_mode);
    2345             : 
    2346             :     /*
    2347             :      * Construct plancache entries, but don't do parse analysis yet.
    2348             :      */
    2349       12320 :     plancache_list = NIL;
    2350             : 
    2351       24642 :     foreach(list_item, raw_parsetree_list)
    2352             :     {
    2353       12322 :         RawStmt    *parsetree = lfirst_node(RawStmt, list_item);
    2354             :         CachedPlanSource *plansource;
    2355             : 
    2356       12322 :         plansource = CreateOneShotCachedPlan(parsetree,
    2357             :                                              src,
    2358             :                                              CreateCommandTag(parsetree->stmt));
    2359             : 
    2360       12322 :         plancache_list = lappend(plancache_list, plansource);
    2361             :     }
    2362             : 
    2363       12320 :     plan->plancache_list = plancache_list;
    2364       12320 :     plan->oneshot = true;
    2365             : 
    2366             :     /*
    2367             :      * Pop the error context stack
    2368             :      */
    2369       12320 :     error_context_stack = spierrcontext.previous;
    2370       12320 : }
    2371             : 
    2372             : /*
    2373             :  * _SPI_execute_plan: execute the given plan with the given options
    2374             :  *
    2375             :  * options contains options accessible from outside SPI:
    2376             :  * params: parameter values to pass to query
    2377             :  * read_only: true for read-only execution (no CommandCounterIncrement)
    2378             :  * allow_nonatomic: true to allow nonatomic CALL/DO execution
    2379             :  * must_return_tuples: throw error if query doesn't return tuples
    2380             :  * tcount: execution tuple-count limit, or 0 for none
    2381             :  * dest: DestReceiver to receive output, or NULL for normal SPI output
    2382             :  * owner: ResourceOwner that will be used to hold refcount on plan;
    2383             :  *      if NULL, CurrentResourceOwner is used (ignored for non-saved plan)
    2384             :  *
    2385             :  * Additional, only-internally-accessible options:
    2386             :  * snapshot: query snapshot to use, or InvalidSnapshot for the normal
    2387             :  *      behavior of taking a new snapshot for each query.
    2388             :  * crosscheck_snapshot: for RI use, all others pass InvalidSnapshot
    2389             :  * fire_triggers: true to fire AFTER triggers at end of query (normal case);
    2390             :  *      false means any AFTER triggers are postponed to end of outer query
    2391             :  */
    2392             : static int
    2393       87280 : _SPI_execute_plan(SPIPlanPtr plan, const SPIExecuteOptions *options,
    2394             :                   Snapshot snapshot, Snapshot crosscheck_snapshot,
    2395             :                   bool fire_triggers)
    2396             : {
    2397       87280 :     int         my_res = 0;
    2398       87280 :     uint64      my_processed = 0;
    2399       87280 :     SPITupleTable *my_tuptable = NULL;
    2400       87280 :     int         res = 0;
    2401       87280 :     bool        pushed_active_snap = false;
    2402       87280 :     ResourceOwner plan_owner = options->owner;
    2403             :     SPICallbackArg spicallbackarg;
    2404             :     ErrorContextCallback spierrcontext;
    2405       87280 :     CachedPlan *cplan = NULL;
    2406             :     ListCell   *lc1;
    2407             : 
    2408             :     /*
    2409             :      * Setup error traceback support for ereport()
    2410             :      */
    2411       87280 :     spicallbackarg.query = NULL;    /* we'll fill this below */
    2412       87280 :     spicallbackarg.mode = plan->parse_mode;
    2413       87280 :     spierrcontext.callback = _SPI_error_callback;
    2414       87280 :     spierrcontext.arg = &spicallbackarg;
    2415       87280 :     spierrcontext.previous = error_context_stack;
    2416       87280 :     error_context_stack = &spierrcontext;
    2417             : 
    2418             :     /*
    2419             :      * We support four distinct snapshot management behaviors:
    2420             :      *
    2421             :      * snapshot != InvalidSnapshot, read_only = true: use exactly the given
    2422             :      * snapshot.
    2423             :      *
    2424             :      * snapshot != InvalidSnapshot, read_only = false: use the given snapshot,
    2425             :      * modified by advancing its command ID before each querytree.
    2426             :      *
    2427             :      * snapshot == InvalidSnapshot, read_only = true: use the entry-time
    2428             :      * ActiveSnapshot, if any (if there isn't one, we run with no snapshot).
    2429             :      *
    2430             :      * snapshot == InvalidSnapshot, read_only = false: take a full new
    2431             :      * snapshot for each user command, and advance its command ID before each
    2432             :      * querytree within the command.
    2433             :      *
    2434             :      * In the first two cases, we can just push the snap onto the stack once
    2435             :      * for the whole plan list.
    2436             :      *
    2437             :      * Note that snapshot != InvalidSnapshot implies an atomic execution
    2438             :      * context.
    2439             :      */
    2440       87280 :     if (snapshot != InvalidSnapshot)
    2441             :     {
    2442             :         Assert(!options->allow_nonatomic);
    2443        1044 :         if (options->read_only)
    2444             :         {
    2445        1012 :             PushActiveSnapshot(snapshot);
    2446        1012 :             pushed_active_snap = true;
    2447             :         }
    2448             :         else
    2449             :         {
    2450             :             /* Make sure we have a private copy of the snapshot to modify */
    2451          32 :             PushCopiedSnapshot(snapshot);
    2452          32 :             pushed_active_snap = true;
    2453             :         }
    2454             :     }
    2455             : 
    2456             :     /*
    2457             :      * Ensure that we have a resource owner if plan is saved, and not if it
    2458             :      * isn't.
    2459             :      */
    2460       87280 :     if (!plan->saved)
    2461       13480 :         plan_owner = NULL;
    2462       73800 :     else if (plan_owner == NULL)
    2463       73704 :         plan_owner = CurrentResourceOwner;
    2464             : 
    2465             :     /*
    2466             :      * We interpret must_return_tuples as "there must be at least one query,
    2467             :      * and all of them must return tuples".  This is a bit laxer than
    2468             :      * SPI_is_cursor_plan's check, but there seems no reason to enforce that
    2469             :      * there be only one query.
    2470             :      */
    2471       87280 :     if (options->must_return_tuples && plan->plancache_list == NIL)
    2472           0 :         ereport(ERROR,
    2473             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    2474             :                  errmsg("empty query does not return tuples")));
    2475             : 
    2476      168896 :     foreach(lc1, plan->plancache_list)
    2477             :     {
    2478       87280 :         CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc1);
    2479             :         List       *stmt_list;
    2480             :         ListCell   *lc2;
    2481             : 
    2482       87280 :         spicallbackarg.query = plansource->query_string;
    2483             : 
    2484             :         /*
    2485             :          * If this is a one-shot plan, we still need to do parse analysis.
    2486             :          */
    2487       87280 :         if (plan->oneshot)
    2488             :         {
    2489       12320 :             RawStmt    *parsetree = plansource->raw_parse_tree;
    2490       12320 :             const char *src = plansource->query_string;
    2491             :             List       *querytree_list;
    2492             : 
    2493             :             /*
    2494             :              * Parameter datatypes are driven by parserSetup hook if provided,
    2495             :              * otherwise we use the fixed parameter list.
    2496             :              */
    2497       12320 :             if (parsetree == NULL)
    2498           0 :                 querytree_list = NIL;
    2499       12320 :             else if (plan->parserSetup != NULL)
    2500             :             {
    2501             :                 Assert(plan->nargs == 0);
    2502         570 :                 querytree_list = pg_analyze_and_rewrite_withcb(parsetree,
    2503             :                                                                src,
    2504             :                                                                plan->parserSetup,
    2505             :                                                                plan->parserSetupArg,
    2506         570 :                                                                _SPI_current->queryEnv);
    2507             :             }
    2508             :             else
    2509             :             {
    2510       11750 :                 querytree_list = pg_analyze_and_rewrite_fixedparams(parsetree,
    2511             :                                                                     src,
    2512       11750 :                                                                     plan->argtypes,
    2513             :                                                                     plan->nargs,
    2514       11750 :                                                                     _SPI_current->queryEnv);
    2515             :             }
    2516             : 
    2517             :             /* Finish filling in the CachedPlanSource */
    2518       12306 :             CompleteCachedPlan(plansource,
    2519             :                                querytree_list,
    2520             :                                NULL,
    2521             :                                plan->argtypes,
    2522             :                                plan->nargs,
    2523             :                                plan->parserSetup,
    2524             :                                plan->parserSetupArg,
    2525             :                                plan->cursor_options,
    2526             :                                false);  /* not fixed result */
    2527             :         }
    2528             : 
    2529             :         /*
    2530             :          * If asked to, complain when query does not return tuples.
    2531             :          * (Replanning can't change this, so we can check it before that.
    2532             :          * However, we can't check it till after parse analysis, so in the
    2533             :          * case of a one-shot plan this is the earliest we could check.)
    2534             :          */
    2535       87266 :         if (options->must_return_tuples && !plansource->resultDesc)
    2536             :         {
    2537             :             /* try to give a good error message */
    2538             :             const char *cmdtag;
    2539             : 
    2540             :             /* A SELECT without resultDesc must be SELECT INTO */
    2541          12 :             if (plansource->commandTag == CMDTAG_SELECT)
    2542          12 :                 cmdtag = "SELECT INTO";
    2543             :             else
    2544           0 :                 cmdtag = GetCommandTagName(plansource->commandTag);
    2545          12 :             ereport(ERROR,
    2546             :                     (errcode(ERRCODE_SYNTAX_ERROR),
    2547             :             /* translator: %s is name of a SQL command, eg INSERT */
    2548             :                      errmsg("%s query does not return tuples", cmdtag)));
    2549             :         }
    2550             : 
    2551             :         /*
    2552             :          * Replan if needed, and increment plan refcount.  If it's a saved
    2553             :          * plan, the refcount must be backed by the plan_owner.
    2554             :          */
    2555       87254 :         cplan = GetCachedPlan(plansource, options->params,
    2556       87254 :                               plan_owner, _SPI_current->queryEnv);
    2557             : 
    2558       87112 :         stmt_list = cplan->stmt_list;
    2559             : 
    2560             :         /*
    2561             :          * If we weren't given a specific snapshot to use, and the statement
    2562             :          * list requires a snapshot, set that up.
    2563             :          */
    2564      173180 :         if (snapshot == InvalidSnapshot &&
    2565      172136 :             (list_length(stmt_list) > 1 ||
    2566      172136 :              (list_length(stmt_list) == 1 &&
    2567       86068 :               PlannedStmtRequiresSnapshot(linitial_node(PlannedStmt,
    2568             :                                                         stmt_list)))))
    2569             :         {
    2570             :             /*
    2571             :              * First, ensure there's a Portal-level snapshot.  This back-fills
    2572             :              * the snapshot stack in case the previous operation was a COMMIT
    2573             :              * or ROLLBACK inside a procedure or DO block.  (We can't put back
    2574             :              * the Portal snapshot any sooner, or we'd break cases like doing
    2575             :              * SET or LOCK just after COMMIT.)  It's enough to check once per
    2576             :              * statement list, since COMMIT/ROLLBACK/CALL/DO can't appear
    2577             :              * within a multi-statement list.
    2578             :              */
    2579       75394 :             EnsurePortalSnapshotExists();
    2580             : 
    2581             :             /*
    2582             :              * In the default non-read-only case, get a new per-statement-list
    2583             :              * snapshot, replacing any that we pushed in a previous cycle.
    2584             :              * Skip it when doing non-atomic execution, though (we rely
    2585             :              * entirely on the Portal snapshot in that case).
    2586             :              */
    2587       75394 :             if (!options->read_only && !options->allow_nonatomic)
    2588             :             {
    2589       71022 :                 if (pushed_active_snap)
    2590           0 :                     PopActiveSnapshot();
    2591       71022 :                 PushActiveSnapshot(GetTransactionSnapshot());
    2592       71022 :                 pushed_active_snap = true;
    2593             :             }
    2594             :         }
    2595             : 
    2596      168728 :         foreach(lc2, stmt_list)
    2597             :         {
    2598       87112 :             PlannedStmt *stmt = lfirst_node(PlannedStmt, lc2);
    2599       87112 :             bool        canSetTag = stmt->canSetTag;
    2600             :             DestReceiver *dest;
    2601             : 
    2602             :             /*
    2603             :              * Reset output state.  (Note that if a non-SPI receiver is used,
    2604             :              * _SPI_current->processed will stay zero, and that's what we'll
    2605             :              * report to the caller.  It's the receiver's job to count tuples
    2606             :              * in that case.)
    2607             :              */
    2608       87112 :             _SPI_current->processed = 0;
    2609       87112 :             _SPI_current->tuptable = NULL;
    2610             : 
    2611             :             /* Check for unsupported cases. */
    2612       87112 :             if (stmt->utilityStmt)
    2613             :             {
    2614       11882 :                 if (IsA(stmt->utilityStmt, CopyStmt))
    2615             :                 {
    2616          18 :                     CopyStmt   *cstmt = (CopyStmt *) stmt->utilityStmt;
    2617             : 
    2618          18 :                     if (cstmt->filename == NULL)
    2619             :                     {
    2620           8 :                         my_res = SPI_ERROR_COPY;
    2621          18 :                         goto fail;
    2622             :                     }
    2623             :                 }
    2624       11864 :                 else if (IsA(stmt->utilityStmt, TransactionStmt))
    2625             :                 {
    2626          10 :                     my_res = SPI_ERROR_TRANSACTION;
    2627          10 :                     goto fail;
    2628             :                 }
    2629             :             }
    2630             : 
    2631       87094 :             if (options->read_only && !CommandIsReadOnly(stmt))
    2632           0 :                 ereport(ERROR,
    2633             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2634             :                 /* translator: %s is a SQL statement name */
    2635             :                          errmsg("%s is not allowed in a non-volatile function",
    2636             :                                 CreateCommandName((Node *) stmt))));
    2637             : 
    2638             :             /*
    2639             :              * If not read-only mode, advance the command counter before each
    2640             :              * command and update the snapshot.  (But skip it if the snapshot
    2641             :              * isn't under our control.)
    2642             :              */
    2643       87094 :             if (!options->read_only && pushed_active_snap)
    2644             :             {
    2645       71046 :                 CommandCounterIncrement();
    2646       71046 :                 UpdateActiveSnapshotCommandId();
    2647             :             }
    2648             : 
    2649             :             /*
    2650             :              * Select appropriate tuple receiver.  Output from non-canSetTag
    2651             :              * subqueries always goes to the bit bucket.
    2652             :              */
    2653       87094 :             if (!canSetTag)
    2654           0 :                 dest = CreateDestReceiver(DestNone);
    2655       87094 :             else if (options->dest)
    2656        2654 :                 dest = options->dest;
    2657             :             else
    2658       84440 :                 dest = CreateDestReceiver(DestSPI);
    2659             : 
    2660       87094 :             if (stmt->utilityStmt == NULL)
    2661             :             {
    2662             :                 QueryDesc  *qdesc;
    2663             :                 Snapshot    snap;
    2664             : 
    2665       75230 :                 if (ActiveSnapshotSet())
    2666       75230 :                     snap = GetActiveSnapshot();
    2667             :                 else
    2668           0 :                     snap = InvalidSnapshot;
    2669             : 
    2670       75230 :                 qdesc = CreateQueryDesc(stmt,
    2671             :                                         plansource->query_string,
    2672             :                                         snap, crosscheck_snapshot,
    2673             :                                         dest,
    2674             :                                         options->params,
    2675       75230 :                                         _SPI_current->queryEnv,
    2676             :                                         0);
    2677       75230 :                 res = _SPI_pquery(qdesc, fire_triggers,
    2678             :                                   canSetTag ? options->tcount : 0);
    2679       69870 :                 FreeQueryDesc(qdesc);
    2680             :             }
    2681             :             else
    2682             :             {
    2683             :                 ProcessUtilityContext context;
    2684             :                 QueryCompletion qc;
    2685             : 
    2686             :                 /*
    2687             :                  * If the SPI context is atomic, or we were not told to allow
    2688             :                  * nonatomic operations, tell ProcessUtility this is an atomic
    2689             :                  * execution context.
    2690             :                  */
    2691       11864 :                 if (_SPI_current->atomic || !options->allow_nonatomic)
    2692       11768 :                     context = PROCESS_UTILITY_QUERY;
    2693             :                 else
    2694          96 :                     context = PROCESS_UTILITY_QUERY_NONATOMIC;
    2695             : 
    2696       11864 :                 InitializeQueryCompletion(&qc);
    2697       11864 :                 ProcessUtility(stmt,
    2698             :                                plansource->query_string,
    2699             :                                true,    /* protect plancache's node tree */
    2700             :                                context,
    2701             :                                options->params,
    2702       11864 :                                _SPI_current->queryEnv,
    2703             :                                dest,
    2704             :                                &qc);
    2705             : 
    2706             :                 /* Update "processed" if stmt returned tuples */
    2707       11746 :                 if (_SPI_current->tuptable)
    2708         150 :                     _SPI_current->processed = _SPI_current->tuptable->numvals;
    2709             : 
    2710       11746 :                 res = SPI_OK_UTILITY;
    2711             : 
    2712             :                 /*
    2713             :                  * Some utility statements return a row count, even though the
    2714             :                  * tuples are not returned to the caller.
    2715             :                  */
    2716       11746 :                 if (IsA(stmt->utilityStmt, CreateTableAsStmt))
    2717             :                 {
    2718         110 :                     CreateTableAsStmt *ctastmt = (CreateTableAsStmt *) stmt->utilityStmt;
    2719             : 
    2720         110 :                     if (qc.commandTag == CMDTAG_SELECT)
    2721         104 :                         _SPI_current->processed = qc.nprocessed;
    2722             :                     else
    2723             :                     {
    2724             :                         /*
    2725             :                          * Must be an IF NOT EXISTS that did nothing, or a
    2726             :                          * CREATE ... WITH NO DATA.
    2727             :                          */
    2728             :                         Assert(ctastmt->if_not_exists ||
    2729             :                                ctastmt->into->skipData);
    2730           6 :                         _SPI_current->processed = 0;
    2731             :                     }
    2732             : 
    2733             :                     /*
    2734             :                      * For historical reasons, if CREATE TABLE AS was spelled
    2735             :                      * as SELECT INTO, return a special return code.
    2736             :                      */
    2737         110 :                     if (ctastmt->is_select_into)
    2738           0 :                         res = SPI_OK_SELINTO;
    2739             :                 }
    2740       11636 :                 else if (IsA(stmt->utilityStmt, CopyStmt))
    2741             :                 {
    2742             :                     Assert(qc.commandTag == CMDTAG_COPY);
    2743          10 :                     _SPI_current->processed = qc.nprocessed;
    2744             :                 }
    2745             :             }
    2746             : 
    2747             :             /*
    2748             :              * The last canSetTag query sets the status values returned to the
    2749             :              * caller.  Be careful to free any tuptables not returned, to
    2750             :              * avoid intra-transaction memory leak.
    2751             :              */
    2752       81616 :             if (canSetTag)
    2753             :             {
    2754       81616 :                 my_processed = _SPI_current->processed;
    2755       81616 :                 SPI_freetuptable(my_tuptable);
    2756       81616 :                 my_tuptable = _SPI_current->tuptable;
    2757       81616 :                 my_res = res;
    2758             :             }
    2759             :             else
    2760             :             {
    2761           0 :                 SPI_freetuptable(_SPI_current->tuptable);
    2762           0 :                 _SPI_current->tuptable = NULL;
    2763             :             }
    2764             : 
    2765             :             /*
    2766             :              * We don't issue a destroy call to the receiver.  The SPI and
    2767             :              * None receivers would ignore it anyway, while if the caller
    2768             :              * supplied a receiver, it's not our job to destroy it.
    2769             :              */
    2770             : 
    2771       81616 :             if (res < 0)
    2772             :             {
    2773           0 :                 my_res = res;
    2774           0 :                 goto fail;
    2775             :             }
    2776             :         }
    2777             : 
    2778             :         /* Done with this plan, so release refcount */
    2779       81616 :         ReleaseCachedPlan(cplan, plan_owner);
    2780       81616 :         cplan = NULL;
    2781             : 
    2782             :         /*
    2783             :          * If not read-only mode, advance the command counter after the last
    2784             :          * command.  This ensures that its effects are visible, in case it was
    2785             :          * DDL that would affect the next CachedPlanSource.
    2786             :          */
    2787       81616 :         if (!options->read_only)
    2788       76390 :             CommandCounterIncrement();
    2789             :     }
    2790             : 
    2791       81634 : fail:
    2792             : 
    2793             :     /* Pop the snapshot off the stack if we pushed one */
    2794       81634 :     if (pushed_active_snap)
    2795       66648 :         PopActiveSnapshot();
    2796             : 
    2797             :     /* We no longer need the cached plan refcount, if any */
    2798       81634 :     if (cplan)
    2799          18 :         ReleaseCachedPlan(cplan, plan_owner);
    2800             : 
    2801             :     /*
    2802             :      * Pop the error context stack
    2803             :      */
    2804       81634 :     error_context_stack = spierrcontext.previous;
    2805             : 
    2806             :     /* Save results for caller */
    2807       81634 :     SPI_processed = my_processed;
    2808       81634 :     SPI_tuptable = my_tuptable;
    2809             : 
    2810             :     /* tuptable now is caller's responsibility, not SPI's */
    2811       81634 :     _SPI_current->tuptable = NULL;
    2812             : 
    2813             :     /*
    2814             :      * If none of the queries had canSetTag, return SPI_OK_REWRITTEN. Prior to
    2815             :      * 8.4, we used return the last query's result code, but not its auxiliary
    2816             :      * results, but that's confusing.
    2817             :      */
    2818       81634 :     if (my_res == 0)
    2819           0 :         my_res = SPI_OK_REWRITTEN;
    2820             : 
    2821       81634 :     return my_res;
    2822             : }
    2823             : 
    2824             : /*
    2825             :  * Convert arrays of query parameters to form wanted by planner and executor
    2826             :  */
    2827             : static ParamListInfo
    2828       11040 : _SPI_convert_params(int nargs, Oid *argtypes,
    2829             :                     Datum *Values, const char *Nulls)
    2830             : {
    2831             :     ParamListInfo paramLI;
    2832             : 
    2833       11040 :     if (nargs > 0)
    2834             :     {
    2835        9648 :         paramLI = makeParamList(nargs);
    2836             : 
    2837       24350 :         for (int i = 0; i < nargs; i++)
    2838             :         {
    2839       14702 :             ParamExternData *prm = &paramLI->params[i];
    2840             : 
    2841       14702 :             prm->value = Values[i];
    2842       14702 :             prm->isnull = (Nulls && Nulls[i] == 'n');
    2843       14702 :             prm->pflags = PARAM_FLAG_CONST;
    2844       14702 :             prm->ptype = argtypes[i];
    2845             :         }
    2846             :     }
    2847             :     else
    2848        1392 :         paramLI = NULL;
    2849       11040 :     return paramLI;
    2850             : }
    2851             : 
    2852             : static int
    2853       75230 : _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount)
    2854             : {
    2855       75230 :     int         operation = queryDesc->operation;
    2856             :     int         eflags;
    2857             :     int         res;
    2858             : 
    2859       75230 :     switch (operation)
    2860             :     {
    2861       46878 :         case CMD_SELECT:
    2862       46878 :             if (queryDesc->dest->mydest == DestNone)
    2863             :             {
    2864             :                 /* Don't return SPI_OK_SELECT if we're discarding result */
    2865           0 :                 res = SPI_OK_UTILITY;
    2866             :             }
    2867             :             else
    2868       46878 :                 res = SPI_OK_SELECT;
    2869       46878 :             break;
    2870       18354 :         case CMD_INSERT:
    2871       18354 :             if (queryDesc->plannedstmt->hasReturning)
    2872         946 :                 res = SPI_OK_INSERT_RETURNING;
    2873             :             else
    2874       17408 :                 res = SPI_OK_INSERT;
    2875       18354 :             break;
    2876        8466 :         case CMD_DELETE:
    2877        8466 :             if (queryDesc->plannedstmt->hasReturning)
    2878           0 :                 res = SPI_OK_DELETE_RETURNING;
    2879             :             else
    2880        8466 :                 res = SPI_OK_DELETE;
    2881        8466 :             break;
    2882        1508 :         case CMD_UPDATE:
    2883        1508 :             if (queryDesc->plannedstmt->hasReturning)
    2884           4 :                 res = SPI_OK_UPDATE_RETURNING;
    2885             :             else
    2886        1504 :                 res = SPI_OK_UPDATE;
    2887        1508 :             break;
    2888          24 :         case CMD_MERGE:
    2889          24 :             res = SPI_OK_MERGE;
    2890          24 :             break;
    2891           0 :         default:
    2892           0 :             return SPI_ERROR_OPUNKNOWN;
    2893             :     }
    2894             : 
    2895             : #ifdef SPI_EXECUTOR_STATS
    2896             :     if (ShowExecutorStats)
    2897             :         ResetUsage();
    2898             : #endif
    2899             : 
    2900             :     /* Select execution options */
    2901       75230 :     if (fire_triggers)
    2902       68416 :         eflags = 0;             /* default run-to-completion flags */
    2903             :     else
    2904        6814 :         eflags = EXEC_FLAG_SKIP_TRIGGERS;
    2905             : 
    2906       75230 :     ExecutorStart(queryDesc, eflags);
    2907             : 
    2908       75230 :     ExecutorRun(queryDesc, ForwardScanDirection, tcount, true);
    2909             : 
    2910       69872 :     _SPI_current->processed = queryDesc->estate->es_processed;
    2911             : 
    2912       69872 :     if ((res == SPI_OK_SELECT || queryDesc->plannedstmt->hasReturning) &&
    2913       42518 :         queryDesc->dest->mydest == DestSPI)
    2914             :     {
    2915       39924 :         if (_SPI_checktuples())
    2916           0 :             elog(ERROR, "consistency check on SPI tuple count failed");
    2917             :     }
    2918             : 
    2919       69872 :     ExecutorFinish(queryDesc);
    2920       69870 :     ExecutorEnd(queryDesc);
    2921             :     /* FreeQueryDesc is done by the caller */
    2922             : 
    2923             : #ifdef SPI_EXECUTOR_STATS
    2924             :     if (ShowExecutorStats)
    2925             :         ShowUsage("SPI EXECUTOR STATS");
    2926             : #endif
    2927             : 
    2928       69870 :     return res;
    2929             : }
    2930             : 
    2931             : /*
    2932             :  * _SPI_error_callback
    2933             :  *
    2934             :  * Add context information when a query invoked via SPI fails
    2935             :  */
    2936             : static void
    2937        6414 : _SPI_error_callback(void *arg)
    2938             : {
    2939        6414 :     SPICallbackArg *carg = (SPICallbackArg *) arg;
    2940        6414 :     const char *query = carg->query;
    2941             :     int         syntaxerrposition;
    2942             : 
    2943        6414 :     if (query == NULL)          /* in case arg wasn't set yet */
    2944           0 :         return;
    2945             : 
    2946             :     /*
    2947             :      * If there is a syntax error position, convert to internal syntax error;
    2948             :      * otherwise treat the query as an item of context stack
    2949             :      */
    2950        6414 :     syntaxerrposition = geterrposition();
    2951        6414 :     if (syntaxerrposition > 0)
    2952             :     {
    2953         100 :         errposition(0);
    2954         100 :         internalerrposition(syntaxerrposition);
    2955         100 :         internalerrquery(query);
    2956             :     }
    2957             :     else
    2958             :     {
    2959             :         /* Use the parse mode to decide how to describe the query */
    2960        6314 :         switch (carg->mode)
    2961             :         {
    2962          64 :             case RAW_PARSE_PLPGSQL_EXPR:
    2963          64 :                 errcontext("SQL expression \"%s\"", query);
    2964          64 :                 break;
    2965          16 :             case RAW_PARSE_PLPGSQL_ASSIGN1:
    2966             :             case RAW_PARSE_PLPGSQL_ASSIGN2:
    2967             :             case RAW_PARSE_PLPGSQL_ASSIGN3:
    2968          16 :                 errcontext("PL/pgSQL assignment \"%s\"", query);
    2969          16 :                 break;
    2970        6234 :             default:
    2971        6234 :                 errcontext("SQL statement \"%s\"", query);
    2972        6234 :                 break;
    2973             :         }
    2974             :     }
    2975             : }
    2976             : 
    2977             : /*
    2978             :  * _SPI_cursor_operation()
    2979             :  *
    2980             :  *  Do a FETCH or MOVE in a cursor
    2981             :  */
    2982             : static void
    2983       43506 : _SPI_cursor_operation(Portal portal, FetchDirection direction, long count,
    2984             :                       DestReceiver *dest)
    2985             : {
    2986             :     uint64      nfetched;
    2987             : 
    2988             :     /* Check that the portal is valid */
    2989       43506 :     if (!PortalIsValid(portal))
    2990           0 :         elog(ERROR, "invalid portal in SPI cursor operation");
    2991             : 
    2992             :     /* Push the SPI stack */
    2993       43506 :     if (_SPI_begin_call(true) < 0)
    2994           0 :         elog(ERROR, "SPI cursor operation called while not connected");
    2995             : 
    2996             :     /* Reset the SPI result (note we deliberately don't touch lastoid) */
    2997       43506 :     SPI_processed = 0;
    2998       43506 :     SPI_tuptable = NULL;
    2999       43506 :     _SPI_current->processed = 0;
    3000       43506 :     _SPI_current->tuptable = NULL;
    3001             : 
    3002             :     /* Run the cursor */
    3003       43506 :     nfetched = PortalRunFetch(portal,
    3004             :                               direction,
    3005             :                               count,
    3006             :                               dest);
    3007             : 
    3008             :     /*
    3009             :      * Think not to combine this store with the preceding function call. If
    3010             :      * the portal contains calls to functions that use SPI, then _SPI_stack is
    3011             :      * likely to move around while the portal runs.  When control returns,
    3012             :      * _SPI_current will point to the correct stack entry... but the pointer
    3013             :      * may be different than it was beforehand. So we must be sure to re-fetch
    3014             :      * the pointer after the function call completes.
    3015             :      */
    3016       43492 :     _SPI_current->processed = nfetched;
    3017             : 
    3018       43492 :     if (dest->mydest == DestSPI && _SPI_checktuples())
    3019           0 :         elog(ERROR, "consistency check on SPI tuple count failed");
    3020             : 
    3021             :     /* Put the result into place for access by caller */
    3022       43492 :     SPI_processed = _SPI_current->processed;
    3023       43492 :     SPI_tuptable = _SPI_current->tuptable;
    3024             : 
    3025             :     /* tuptable now is caller's responsibility, not SPI's */
    3026       43492 :     _SPI_current->tuptable = NULL;
    3027             : 
    3028             :     /* Pop the SPI stack */
    3029       43492 :     _SPI_end_call(true);
    3030       43492 : }
    3031             : 
    3032             : 
    3033             : static MemoryContext
    3034      180636 : _SPI_execmem(void)
    3035             : {
    3036      180636 :     return MemoryContextSwitchTo(_SPI_current->execCxt);
    3037             : }
    3038             : 
    3039             : static MemoryContext
    3040      263708 : _SPI_procmem(void)
    3041             : {
    3042      263708 :     return MemoryContextSwitchTo(_SPI_current->procCxt);
    3043             : }
    3044             : 
    3045             : /*
    3046             :  * _SPI_begin_call: begin a SPI operation within a connected procedure
    3047             :  *
    3048             :  * use_exec is true if we intend to make use of the procedure's execCxt
    3049             :  * during this SPI operation.  We'll switch into that context, and arrange
    3050             :  * for it to be cleaned up at _SPI_end_call or if an error occurs.
    3051             :  */
    3052             : static int
    3053      277176 : _SPI_begin_call(bool use_exec)
    3054             : {
    3055      277176 :     if (_SPI_current == NULL)
    3056           0 :         return SPI_ERROR_UNCONNECTED;
    3057             : 
    3058      277176 :     if (use_exec)
    3059             :     {
    3060             :         /* remember when the Executor operation started */
    3061      180636 :         _SPI_current->execSubid = GetCurrentSubTransactionId();
    3062             :         /* switch to the Executor memory context */
    3063      180636 :         _SPI_execmem();
    3064             :     }
    3065             : 
    3066      277176 :     return 0;
    3067             : }
    3068             : 
    3069             : /*
    3070             :  * _SPI_end_call: end a SPI operation within a connected procedure
    3071             :  *
    3072             :  * use_exec must be the same as in the previous _SPI_begin_call
    3073             :  *
    3074             :  * Note: this currently has no failure return cases, so callers don't check
    3075             :  */
    3076             : static int
    3077      175556 : _SPI_end_call(bool use_exec)
    3078             : {
    3079      175556 :     if (use_exec)
    3080             :     {
    3081             :         /* switch to the procedure memory context */
    3082      174872 :         _SPI_procmem();
    3083             :         /* mark Executor context no longer in use */
    3084      174872 :         _SPI_current->execSubid = InvalidSubTransactionId;
    3085             :         /* and free Executor memory */
    3086      174872 :         MemoryContextReset(_SPI_current->execCxt);
    3087             :     }
    3088             : 
    3089      175556 :     return 0;
    3090             : }
    3091             : 
    3092             : static bool
    3093       83374 : _SPI_checktuples(void)
    3094             : {
    3095       83374 :     uint64      processed = _SPI_current->processed;
    3096       83374 :     SPITupleTable *tuptable = _SPI_current->tuptable;
    3097       83374 :     bool        failed = false;
    3098             : 
    3099       83374 :     if (tuptable == NULL)       /* spi_dest_startup was not called */
    3100           0 :         failed = true;
    3101       83374 :     else if (processed != tuptable->numvals)
    3102           0 :         failed = true;
    3103             : 
    3104       83374 :     return failed;
    3105             : }
    3106             : 
    3107             : /*
    3108             :  * Convert a "temporary" SPIPlan into an "unsaved" plan.
    3109             :  *
    3110             :  * The passed _SPI_plan struct is on the stack, and all its subsidiary data
    3111             :  * is in or under the current SPI executor context.  Copy the plan into the
    3112             :  * SPI procedure context so it will survive _SPI_end_call().  To minimize
    3113             :  * data copying, this destructively modifies the input plan, by taking the
    3114             :  * plancache entries away from it and reparenting them to the new SPIPlan.
    3115             :  */
    3116             : static SPIPlanPtr
    3117       28696 : _SPI_make_plan_non_temp(SPIPlanPtr plan)
    3118             : {
    3119             :     SPIPlanPtr  newplan;
    3120       28696 :     MemoryContext parentcxt = _SPI_current->procCxt;
    3121             :     MemoryContext plancxt;
    3122             :     MemoryContext oldcxt;
    3123             :     ListCell   *lc;
    3124             : 
    3125             :     /* Assert the input is a temporary SPIPlan */
    3126             :     Assert(plan->magic == _SPI_PLAN_MAGIC);
    3127             :     Assert(plan->plancxt == NULL);
    3128             :     /* One-shot plans can't be saved */
    3129             :     Assert(!plan->oneshot);
    3130             : 
    3131             :     /*
    3132             :      * Create a memory context for the plan, underneath the procedure context.
    3133             :      * We don't expect the plan to be very large.
    3134             :      */
    3135       28696 :     plancxt = AllocSetContextCreate(parentcxt,
    3136             :                                     "SPI Plan",
    3137             :                                     ALLOCSET_SMALL_SIZES);
    3138       28696 :     oldcxt = MemoryContextSwitchTo(plancxt);
    3139             : 
    3140             :     /* Copy the _SPI_plan struct and subsidiary data into the new context */
    3141       28696 :     newplan = (SPIPlanPtr) palloc0(sizeof(_SPI_plan));
    3142       28696 :     newplan->magic = _SPI_PLAN_MAGIC;
    3143       28696 :     newplan->plancxt = plancxt;
    3144       28696 :     newplan->parse_mode = plan->parse_mode;
    3145       28696 :     newplan->cursor_options = plan->cursor_options;
    3146       28696 :     newplan->nargs = plan->nargs;
    3147       28696 :     if (plan->nargs > 0)
    3148             :     {
    3149        3348 :         newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
    3150        3348 :         memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
    3151             :     }
    3152             :     else
    3153       25348 :         newplan->argtypes = NULL;
    3154       28696 :     newplan->parserSetup = plan->parserSetup;
    3155       28696 :     newplan->parserSetupArg = plan->parserSetupArg;
    3156             : 
    3157             :     /*
    3158             :      * Reparent all the CachedPlanSources into the procedure context.  In
    3159             :      * theory this could fail partway through due to the pallocs, but we don't
    3160             :      * care too much since both the procedure context and the executor context
    3161             :      * would go away on error.
    3162             :      */
    3163       57392 :     foreach(lc, plan->plancache_list)
    3164             :     {
    3165       28696 :         CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
    3166             : 
    3167       28696 :         CachedPlanSetParentContext(plansource, parentcxt);
    3168             : 
    3169             :         /* Build new list, with list cells in plancxt */
    3170       28696 :         newplan->plancache_list = lappend(newplan->plancache_list, plansource);
    3171             :     }
    3172             : 
    3173       28696 :     MemoryContextSwitchTo(oldcxt);
    3174             : 
    3175             :     /* For safety, unlink the CachedPlanSources from the temporary plan */
    3176       28696 :     plan->plancache_list = NIL;
    3177             : 
    3178       28696 :     return newplan;
    3179             : }
    3180             : 
    3181             : /*
    3182             :  * Make a "saved" copy of the given plan.
    3183             :  */
    3184             : static SPIPlanPtr
    3185           0 : _SPI_save_plan(SPIPlanPtr plan)
    3186             : {
    3187             :     SPIPlanPtr  newplan;
    3188             :     MemoryContext plancxt;
    3189             :     MemoryContext oldcxt;
    3190             :     ListCell   *lc;
    3191             : 
    3192             :     /* One-shot plans can't be saved */
    3193             :     Assert(!plan->oneshot);
    3194             : 
    3195             :     /*
    3196             :      * Create a memory context for the plan.  We don't expect the plan to be
    3197             :      * very large, so use smaller-than-default alloc parameters.  It's a
    3198             :      * transient context until we finish copying everything.
    3199             :      */
    3200           0 :     plancxt = AllocSetContextCreate(CurrentMemoryContext,
    3201             :                                     "SPI Plan",
    3202             :                                     ALLOCSET_SMALL_SIZES);
    3203           0 :     oldcxt = MemoryContextSwitchTo(plancxt);
    3204             : 
    3205             :     /* Copy the SPI plan into its own context */
    3206           0 :     newplan = (SPIPlanPtr) palloc0(sizeof(_SPI_plan));
    3207           0 :     newplan->magic = _SPI_PLAN_MAGIC;
    3208           0 :     newplan->plancxt = plancxt;
    3209           0 :     newplan->parse_mode = plan->parse_mode;
    3210           0 :     newplan->cursor_options = plan->cursor_options;
    3211           0 :     newplan->nargs = plan->nargs;
    3212           0 :     if (plan->nargs > 0)
    3213             :     {
    3214           0 :         newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
    3215           0 :         memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
    3216             :     }
    3217             :     else
    3218           0 :         newplan->argtypes = NULL;
    3219           0 :     newplan->parserSetup = plan->parserSetup;
    3220           0 :     newplan->parserSetupArg = plan->parserSetupArg;
    3221             : 
    3222             :     /* Copy all the plancache entries */
    3223           0 :     foreach(lc, plan->plancache_list)
    3224             :     {
    3225           0 :         CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
    3226             :         CachedPlanSource *newsource;
    3227             : 
    3228           0 :         newsource = CopyCachedPlan(plansource);
    3229           0 :         newplan->plancache_list = lappend(newplan->plancache_list, newsource);
    3230             :     }
    3231             : 
    3232           0 :     MemoryContextSwitchTo(oldcxt);
    3233             : 
    3234             :     /*
    3235             :      * Mark it saved, reparent it under CacheMemoryContext, and mark all the
    3236             :      * component CachedPlanSources as saved.  This sequence cannot fail
    3237             :      * partway through, so there's no risk of long-term memory leakage.
    3238             :      */
    3239           0 :     newplan->saved = true;
    3240           0 :     MemoryContextSetParent(newplan->plancxt, CacheMemoryContext);
    3241             : 
    3242           0 :     foreach(lc, newplan->plancache_list)
    3243             :     {
    3244           0 :         CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
    3245             : 
    3246           0 :         SaveCachedPlan(plansource);
    3247             :     }
    3248             : 
    3249           0 :     return newplan;
    3250             : }
    3251             : 
    3252             : /*
    3253             :  * Internal lookup of ephemeral named relation by name.
    3254             :  */
    3255             : static EphemeralNamedRelation
    3256         684 : _SPI_find_ENR_by_name(const char *name)
    3257             : {
    3258             :     /* internal static function; any error is bug in SPI itself */
    3259             :     Assert(name != NULL);
    3260             : 
    3261             :     /* fast exit if no tuplestores have been added */
    3262         684 :     if (_SPI_current->queryEnv == NULL)
    3263         534 :         return NULL;
    3264             : 
    3265         150 :     return get_ENR(_SPI_current->queryEnv, name);
    3266             : }
    3267             : 
    3268             : /*
    3269             :  * Register an ephemeral named relation for use by the planner and executor on
    3270             :  * subsequent calls using this SPI connection.
    3271             :  */
    3272             : int
    3273         684 : SPI_register_relation(EphemeralNamedRelation enr)
    3274             : {
    3275             :     EphemeralNamedRelation match;
    3276             :     int         res;
    3277             : 
    3278         684 :     if (enr == NULL || enr->md.name == NULL)
    3279           0 :         return SPI_ERROR_ARGUMENT;
    3280             : 
    3281         684 :     res = _SPI_begin_call(false);   /* keep current memory context */
    3282         684 :     if (res < 0)
    3283           0 :         return res;
    3284             : 
    3285         684 :     match = _SPI_find_ENR_by_name(enr->md.name);
    3286         684 :     if (match)
    3287           0 :         res = SPI_ERROR_REL_DUPLICATE;
    3288             :     else
    3289             :     {
    3290         684 :         if (_SPI_current->queryEnv == NULL)
    3291         534 :             _SPI_current->queryEnv = create_queryEnv();
    3292             : 
    3293         684 :         register_ENR(_SPI_current->queryEnv, enr);
    3294         684 :         res = SPI_OK_REL_REGISTER;
    3295             :     }
    3296             : 
    3297         684 :     _SPI_end_call(false);
    3298             : 
    3299         684 :     return res;
    3300             : }
    3301             : 
    3302             : /*
    3303             :  * Unregister an ephemeral named relation by name.  This will probably be a
    3304             :  * rarely used function, since SPI_finish will clear it automatically.
    3305             :  */
    3306             : int
    3307           0 : SPI_unregister_relation(const char *name)
    3308             : {
    3309             :     EphemeralNamedRelation match;
    3310             :     int         res;
    3311             : 
    3312           0 :     if (name == NULL)
    3313           0 :         return SPI_ERROR_ARGUMENT;
    3314             : 
    3315           0 :     res = _SPI_begin_call(false);   /* keep current memory context */
    3316           0 :     if (res < 0)
    3317           0 :         return res;
    3318             : 
    3319           0 :     match = _SPI_find_ENR_by_name(name);
    3320           0 :     if (match)
    3321             :     {
    3322           0 :         unregister_ENR(_SPI_current->queryEnv, match->md.name);
    3323           0 :         res = SPI_OK_REL_UNREGISTER;
    3324             :     }
    3325             :     else
    3326           0 :         res = SPI_ERROR_REL_NOT_FOUND;
    3327             : 
    3328           0 :     _SPI_end_call(false);
    3329             : 
    3330           0 :     return res;
    3331             : }
    3332             : 
    3333             : /*
    3334             :  * Register the transient relations from 'tdata' using this SPI connection.
    3335             :  * This should be called by PL implementations' trigger handlers after
    3336             :  * connecting, in order to make transition tables visible to any queries run
    3337             :  * in this connection.
    3338             :  */
    3339             : int
    3340       14958 : SPI_register_trigger_data(TriggerData *tdata)
    3341             : {
    3342       14958 :     if (tdata == NULL)
    3343           0 :         return SPI_ERROR_ARGUMENT;
    3344             : 
    3345       14958 :     if (tdata->tg_newtable)
    3346             :     {
    3347             :         EphemeralNamedRelation enr =
    3348         384 :             palloc(sizeof(EphemeralNamedRelationData));
    3349             :         int         rc;
    3350             : 
    3351         384 :         enr->md.name = tdata->tg_trigger->tgnewtable;
    3352         384 :         enr->md.reliddesc = tdata->tg_relation->rd_id;
    3353         384 :         enr->md.tupdesc = NULL;
    3354         384 :         enr->md.enrtype = ENR_NAMED_TUPLESTORE;
    3355         384 :         enr->md.enrtuples = tuplestore_tuple_count(tdata->tg_newtable);
    3356         384 :         enr->reldata = tdata->tg_newtable;
    3357         384 :         rc = SPI_register_relation(enr);
    3358         384 :         if (rc != SPI_OK_REL_REGISTER)
    3359           0 :             return rc;
    3360             :     }
    3361             : 
    3362       14958 :     if (tdata->tg_oldtable)
    3363             :     {
    3364             :         EphemeralNamedRelation enr =
    3365         300 :             palloc(sizeof(EphemeralNamedRelationData));
    3366             :         int         rc;
    3367             : 
    3368         300 :         enr->md.name = tdata->tg_trigger->tgoldtable;
    3369         300 :         enr->md.reliddesc = tdata->tg_relation->rd_id;
    3370         300 :         enr->md.tupdesc = NULL;
    3371         300 :         enr->md.enrtype = ENR_NAMED_TUPLESTORE;
    3372         300 :         enr->md.enrtuples = tuplestore_tuple_count(tdata->tg_oldtable);
    3373         300 :         enr->reldata = tdata->tg_oldtable;
    3374         300 :         rc = SPI_register_relation(enr);
    3375         300 :         if (rc != SPI_OK_REL_REGISTER)
    3376           0 :             return rc;
    3377             :     }
    3378             : 
    3379       14958 :     return SPI_OK_TD_REGISTER;
    3380             : }

Generated by: LCOV version 1.14