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

Generated by: LCOV version 1.14