LCOV - code coverage report
Current view: top level - src/backend/executor - spi.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 72.2 % 1240 895
Test Date: 2026-03-01 00:15:48 Functions: 83.3 % 84 70
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.0-1