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

Generated by: LCOV version 1.14