LCOV - code coverage report
Current view: top level - src/backend/executor - spi.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 805 1060 75.9 %
Date: 2020-05-29 01:06:25 Functions: 71 81 87.7 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13