LCOV - code coverage report
Current view: top level - src/backend/executor - spi.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 908 1244 73.0 %
Date: 2025-02-22 07:14:56 Functions: 70 84 83.3 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14