LCOV - code coverage report
Current view: top level - src/backend/executor - spi.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 792 1029 77.0 %
Date: 2019-08-24 06:06:56 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-2019, 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        9184 : SPI_connect(void)
      90             : {
      91        9184 :     return SPI_connect_ext(0);
      92             : }
      93             : 
      94             : int
      95       76490 : SPI_connect_ext(int options)
      96             : {
      97             :     int         newdepth;
      98             : 
      99             :     /* Enlarge stack if necessary */
     100       76490 :     if (_SPI_stack == NULL)
     101             :     {
     102         840 :         if (_SPI_connected != -1 || _SPI_stack_depth != 0)
     103           0 :             elog(ERROR, "SPI stack corrupted");
     104         840 :         newdepth = 16;
     105         840 :         _SPI_stack = (_SPI_connection *)
     106         840 :             MemoryContextAlloc(TopMemoryContext,
     107             :                                newdepth * sizeof(_SPI_connection));
     108         840 :         _SPI_stack_depth = newdepth;
     109             :     }
     110             :     else
     111             :     {
     112       75650 :         if (_SPI_stack_depth <= 0 || _SPI_stack_depth <= _SPI_connected)
     113           0 :             elog(ERROR, "SPI stack corrupted");
     114       75650 :         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       76490 :     _SPI_connected++;
     126             :     Assert(_SPI_connected >= 0 && _SPI_connected < _SPI_stack_depth);
     127             : 
     128       76490 :     _SPI_current = &(_SPI_stack[_SPI_connected]);
     129       76490 :     _SPI_current->processed = 0;
     130       76490 :     _SPI_current->tuptable = NULL;
     131       76490 :     _SPI_current->execSubid = InvalidSubTransactionId;
     132       76490 :     slist_init(&_SPI_current->tuptables);
     133       76490 :     _SPI_current->procCxt = NULL;    /* in case we fail to create 'em */
     134       76490 :     _SPI_current->execCxt = NULL;
     135       76490 :     _SPI_current->connectSubid = GetCurrentSubTransactionId();
     136       76490 :     _SPI_current->queryEnv = NULL;
     137       76490 :     _SPI_current->atomic = (options & SPI_OPT_NONATOMIC ? false : true);
     138       76490 :     _SPI_current->internal_xact = false;
     139       76490 :     _SPI_current->outer_processed = SPI_processed;
     140       76490 :     _SPI_current->outer_tuptable = SPI_tuptable;
     141       76490 :     _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       76490 :     _SPI_current->procCxt = AllocSetContextCreate(_SPI_current->atomic ? TopTransactionContext : PortalContext,
     156             :                                                   "SPI Proc",
     157             :                                                   ALLOCSET_DEFAULT_SIZES);
     158       76490 :     _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       76490 :     _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       76490 :     SPI_processed = 0;
     169       76490 :     SPI_tuptable = NULL;
     170       76490 :     SPI_result = 0;
     171             : 
     172       76490 :     return SPI_OK_CONNECT;
     173             : }
     174             : 
     175             : int
     176       74834 : SPI_finish(void)
     177             : {
     178             :     int         res;
     179             : 
     180       74834 :     res = _SPI_begin_call(false);   /* just check we're connected */
     181       74834 :     if (res < 0)
     182           0 :         return res;
     183             : 
     184             :     /* Restore memory context as it was before procedure call */
     185       74834 :     MemoryContextSwitchTo(_SPI_current->savedcxt);
     186             : 
     187             :     /* Release memory used in procedure call (including tuptables) */
     188       74834 :     MemoryContextDelete(_SPI_current->execCxt);
     189       74834 :     _SPI_current->execCxt = NULL;
     190       74834 :     MemoryContextDelete(_SPI_current->procCxt);
     191       74834 :     _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       74834 :     SPI_processed = _SPI_current->outer_processed;
     198       74834 :     SPI_tuptable = _SPI_current->outer_tuptable;
     199       74834 :     SPI_result = _SPI_current->outer_result;
     200             : 
     201             :     /* Exit stack level */
     202       74834 :     _SPI_connected--;
     203       74834 :     if (_SPI_connected < 0)
     204       65938 :         _SPI_current = NULL;
     205             :     else
     206        8896 :         _SPI_current = &(_SPI_stack[_SPI_connected]);
     207             : 
     208       74834 :     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        8422 :     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      472728 : SPICleanup(void)
     350             : {
     351      472728 :     _SPI_current = NULL;
     352      472728 :     _SPI_connected = -1;
     353             :     /* Reset API global variables, too */
     354      472728 :     SPI_processed = 0;
     355      472728 :     SPI_tuptable = NULL;
     356      472728 :     SPI_result = 0;
     357      472728 : }
     358             : 
     359             : /*
     360             :  * Clean up SPI state at transaction commit or abort.
     361             :  */
     362             : void
     363      459234 : AtEOXact_SPI(bool isCommit)
     364             : {
     365             :     /* Do nothing if the transaction end was initiated by SPI. */
     366      459234 :     if (_SPI_current && _SPI_current->internal_xact)
     367        4324 :         return;
     368             : 
     369      454910 :     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      454910 :     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        7364 : AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid)
     386             : {
     387        7364 :     bool        found = false;
     388             : 
     389       14860 :     while (_SPI_connected >= 0)
     390             :     {
     391        5156 :         _SPI_connection *connection = &(_SPI_stack[_SPI_connected]);
     392             : 
     393        5156 :         if (connection->connectSubid != mySubid)
     394        5024 :             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        7364 :     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        7364 :     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        1140 :         if (_SPI_current->execSubid >= mySubid)
     450             :         {
     451         982 :             _SPI_current->execSubid = InvalidSubTransactionId;
     452         982 :             MemoryContextResetAndDeleteChildren(_SPI_current->execCxt);
     453             :         }
     454             : 
     455             :         /* throw away any tuple tables created within current subxact */
     456        4420 :         slist_foreach_modify(siter, &_SPI_current->tuptables)
     457             :         {
     458             :             SPITupleTable *tuptable;
     459             : 
     460        3280 :             tuptable = slist_container(SPITupleTable, next, siter.cur);
     461        3280 :             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         868 :                 slist_delete_current(&siter);
     470         868 :                 if (tuptable == _SPI_current->tuptable)
     471         864 :                     _SPI_current->tuptable = NULL;
     472         868 :                 if (tuptable == SPI_tuptable)
     473           4 :                     SPI_tuptable = NULL;
     474         868 :                 MemoryContextDelete(tuptable->tuptabcxt);
     475             :             }
     476             :         }
     477             :     }
     478        7364 : }
     479             : 
     480             : /*
     481             :  * Are we executing inside a procedure (that is, a nonatomic SPI context)?
     482             :  */
     483             : bool
     484      454386 : SPI_inside_nonatomic_context(void)
     485             : {
     486      454386 :     if (_SPI_current == NULL)
     487      450064 :         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        7358 : SPI_execute(const char *src, bool read_only, long tcount)
     497             : {
     498             :     _SPI_plan   plan;
     499             :     int         res;
     500             : 
     501        7358 :     if (src == NULL || tcount < 0)
     502           0 :         return SPI_ERROR_ARGUMENT;
     503             : 
     504        7358 :     res = _SPI_begin_call(true);
     505        7358 :     if (res < 0)
     506           0 :         return res;
     507             : 
     508        7358 :     memset(&plan, 0, sizeof(_SPI_plan));
     509        7358 :     plan.magic = _SPI_PLAN_MAGIC;
     510        7358 :     plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
     511             : 
     512        7358 :     _SPI_prepare_oneshot_plan(src, &plan);
     513             : 
     514        7342 :     res = _SPI_execute_plan(&plan, NULL,
     515             :                             InvalidSnapshot, InvalidSnapshot,
     516             :                             read_only, true, tcount);
     517             : 
     518        7218 :     _SPI_end_call(true);
     519        7218 :     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        6442 : SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
     532             :                  bool read_only, long tcount)
     533             : {
     534             :     int         res;
     535             : 
     536        6442 :     if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
     537           0 :         return SPI_ERROR_ARGUMENT;
     538             : 
     539        6442 :     if (plan->nargs > 0 && Values == NULL)
     540           0 :         return SPI_ERROR_PARAM;
     541             : 
     542        6442 :     res = _SPI_begin_call(true);
     543        6442 :     if (res < 0)
     544           0 :         return res;
     545             : 
     546        6442 :     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        6430 :     _SPI_end_call(true);
     553        6430 :     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       13092 : SPI_execute_plan_with_paramlist(SPIPlanPtr plan, ParamListInfo params,
     566             :                                 bool read_only, long tcount)
     567             : {
     568             :     int         res;
     569             : 
     570       13092 :     if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
     571           0 :         return SPI_ERROR_ARGUMENT;
     572             : 
     573       13092 :     res = _SPI_begin_call(true);
     574       13092 :     if (res < 0)
     575           0 :         return res;
     576             : 
     577       13092 :     res = _SPI_execute_plan(plan, params,
     578             :                             InvalidSnapshot, InvalidSnapshot,
     579             :                             read_only, true, tcount);
     580             : 
     581       12188 :     _SPI_end_call(true);
     582       12188 :     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        4314 : 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        4314 :     if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
     607           0 :         return SPI_ERROR_ARGUMENT;
     608             : 
     609        4314 :     if (plan->nargs > 0 && Values == NULL)
     610           0 :         return SPI_ERROR_PARAM;
     611             : 
     612        4314 :     res = _SPI_begin_call(true);
     613        4314 :     if (res < 0)
     614           0 :         return res;
     615             : 
     616        4314 :     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        4304 :     _SPI_end_call(true);
     623        4304 :     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          12 : 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          12 :     if (src == NULL || nargs < 0 || tcount < 0)
     643           0 :         return SPI_ERROR_ARGUMENT;
     644             : 
     645          12 :     if (nargs > 0 && (argtypes == NULL || Values == NULL))
     646           0 :         return SPI_ERROR_PARAM;
     647             : 
     648          12 :     res = _SPI_begin_call(true);
     649          12 :     if (res < 0)
     650           0 :         return res;
     651             : 
     652          12 :     memset(&plan, 0, sizeof(_SPI_plan));
     653          12 :     plan.magic = _SPI_PLAN_MAGIC;
     654          12 :     plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
     655          12 :     plan.nargs = nargs;
     656          12 :     plan.argtypes = argtypes;
     657          12 :     plan.parserSetup = NULL;
     658          12 :     plan.parserSetupArg = NULL;
     659             : 
     660          12 :     paramLI = _SPI_convert_params(nargs, argtypes,
     661             :                                   Values, Nulls);
     662             : 
     663          12 :     _SPI_prepare_oneshot_plan(src, &plan);
     664             : 
     665          12 :     res = _SPI_execute_plan(&plan, paramLI,
     666             :                             InvalidSnapshot, InvalidSnapshot,
     667             :                             read_only, true, tcount);
     668             : 
     669          12 :     _SPI_end_call(true);
     670          12 :     return res;
     671             : }
     672             : 
     673             : SPIPlanPtr
     674        3340 : SPI_prepare(const char *src, int nargs, Oid *argtypes)
     675             : {
     676        3340 :     return SPI_prepare_cursor(src, nargs, argtypes, 0);
     677             : }
     678             : 
     679             : SPIPlanPtr
     680        3340 : SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
     681             :                    int cursorOptions)
     682             : {
     683             :     _SPI_plan   plan;
     684             :     SPIPlanPtr  result;
     685             : 
     686        3340 :     if (src == NULL || nargs < 0 || (nargs > 0 && argtypes == NULL))
     687             :     {
     688           0 :         SPI_result = SPI_ERROR_ARGUMENT;
     689           0 :         return NULL;
     690             :     }
     691             : 
     692        3340 :     SPI_result = _SPI_begin_call(true);
     693        3340 :     if (SPI_result < 0)
     694           0 :         return NULL;
     695             : 
     696        3340 :     memset(&plan, 0, sizeof(_SPI_plan));
     697        3340 :     plan.magic = _SPI_PLAN_MAGIC;
     698        3340 :     plan.cursor_options = cursorOptions;
     699        3340 :     plan.nargs = nargs;
     700        3340 :     plan.argtypes = argtypes;
     701        3340 :     plan.parserSetup = NULL;
     702        3340 :     plan.parserSetupArg = NULL;
     703             : 
     704        3340 :     _SPI_prepare_plan(src, &plan);
     705             : 
     706             :     /* copy plan to procedure context */
     707        3338 :     result = _SPI_make_plan_non_temp(&plan);
     708             : 
     709        3338 :     _SPI_end_call(true);
     710             : 
     711        3338 :     return result;
     712             : }
     713             : 
     714             : SPIPlanPtr
     715       10148 : 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       10148 :     if (src == NULL)
     724             :     {
     725           0 :         SPI_result = SPI_ERROR_ARGUMENT;
     726           0 :         return NULL;
     727             :     }
     728             : 
     729       10148 :     SPI_result = _SPI_begin_call(true);
     730       10148 :     if (SPI_result < 0)
     731           0 :         return NULL;
     732             : 
     733       10148 :     memset(&plan, 0, sizeof(_SPI_plan));
     734       10148 :     plan.magic = _SPI_PLAN_MAGIC;
     735       10148 :     plan.cursor_options = cursorOptions;
     736       10148 :     plan.nargs = 0;
     737       10148 :     plan.argtypes = NULL;
     738       10148 :     plan.parserSetup = parserSetup;
     739       10148 :     plan.parserSetupArg = parserSetupArg;
     740             : 
     741       10148 :     _SPI_prepare_plan(src, &plan);
     742             : 
     743             :     /* copy plan to procedure context */
     744       10120 :     result = _SPI_make_plan_non_temp(&plan);
     745             : 
     746       10120 :     _SPI_end_call(true);
     747             : 
     748       10120 :     return result;
     749             : }
     750             : 
     751             : int
     752       12454 : SPI_keepplan(SPIPlanPtr plan)
     753             : {
     754             :     ListCell   *lc;
     755             : 
     756       24908 :     if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC ||
     757       24908 :         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       12454 :     plan->saved = true;
     766       12454 :     MemoryContextSetParent(plan->plancxt, CacheMemoryContext);
     767             : 
     768       24908 :     foreach(lc, plan->plancache_list)
     769             :     {
     770       12454 :         CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
     771             : 
     772       12454 :         SaveCachedPlan(plansource);
     773             :     }
     774             : 
     775       12454 :     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        2348 : SPI_freeplan(SPIPlanPtr plan)
     802             : {
     803             :     ListCell   *lc;
     804             : 
     805        2348 :     if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
     806           0 :         return SPI_ERROR_ARGUMENT;
     807             : 
     808             :     /* Release the plancache entries */
     809        4696 :     foreach(lc, plan->plancache_list)
     810             :     {
     811        2348 :         CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
     812             : 
     813        2348 :         DropCachedPlan(plansource);
     814             :     }
     815             : 
     816             :     /* Now get rid of the _SPI_plan and subsidiary data in its plancxt */
     817        2348 :     MemoryContextDelete(plan->plancxt);
     818             : 
     819        2348 :     return 0;
     820             : }
     821             : 
     822             : HeapTuple
     823        1484 : SPI_copytuple(HeapTuple tuple)
     824             : {
     825             :     MemoryContext oldcxt;
     826             :     HeapTuple   ctuple;
     827             : 
     828        1484 :     if (tuple == NULL)
     829             :     {
     830           0 :         SPI_result = SPI_ERROR_ARGUMENT;
     831           0 :         return NULL;
     832             :     }
     833             : 
     834        1484 :     if (_SPI_current == NULL)
     835             :     {
     836           0 :         SPI_result = SPI_ERROR_UNCONNECTED;
     837           0 :         return NULL;
     838             :     }
     839             : 
     840        1484 :     oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
     841             : 
     842        1484 :     ctuple = heap_copytuple(tuple);
     843             : 
     844        1484 :     MemoryContextSwitchTo(oldcxt);
     845             : 
     846        1484 :     return ctuple;
     847             : }
     848             : 
     849             : HeapTupleHeader
     850         100 : SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
     851             : {
     852             :     MemoryContext oldcxt;
     853             :     HeapTupleHeader dtup;
     854             : 
     855         100 :     if (tuple == NULL || tupdesc == NULL)
     856             :     {
     857           0 :         SPI_result = SPI_ERROR_ARGUMENT;
     858           0 :         return NULL;
     859             :     }
     860             : 
     861         100 :     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         186 :     if (tupdesc->tdtypeid == RECORDOID &&
     869          86 :         tupdesc->tdtypmod < 0)
     870           0 :         assign_record_type_typmod(tupdesc);
     871             : 
     872         100 :     oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
     873             : 
     874         100 :     dtup = DatumGetHeapTupleHeader(heap_copy_tuple_as_datum(tuple, tupdesc));
     875             : 
     876         100 :     MemoryContextSwitchTo(oldcxt);
     877             : 
     878         100 :     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       11314 : SPI_fnumber(TupleDesc tupdesc, const char *fname)
     952             : {
     953             :     int         res;
     954             :     const FormData_pg_attribute *sysatt;
     955             : 
     956       58802 :     for (res = 0; res < tupdesc->natts; res++)
     957             :     {
     958       58788 :         Form_pg_attribute attr = TupleDescAttr(tupdesc, res);
     959             : 
     960       70088 :         if (namestrcmp(&attr->attname, fname) == 0 &&
     961       11300 :             !attr->attisdropped)
     962       11300 :             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        5280 : 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        5280 :     SPI_result = 0;
    1005             : 
    1006        5280 :     if (fnumber > tupdesc->natts || fnumber == 0 ||
    1007             :         fnumber <= FirstLowInvalidHeapAttributeNumber)
    1008             :     {
    1009           0 :         SPI_result = SPI_ERROR_NOATTRIBUTE;
    1010           0 :         return NULL;
    1011             :     }
    1012             : 
    1013        5280 :     val = heap_getattr(tuple, fnumber, tupdesc, &isnull);
    1014        5280 :     if (isnull)
    1015         130 :         return NULL;
    1016             : 
    1017        5150 :     if (fnumber > 0)
    1018        5150 :         typoid = TupleDescAttr(tupdesc, fnumber - 1)->atttypid;
    1019             :     else
    1020           0 :         typoid = (SystemAttributeDefinition(fnumber))->atttypid;
    1021             : 
    1022        5150 :     getTypeOutputInfo(typoid, &foutoid, &typisvarlena);
    1023             : 
    1024        5150 :     return OidOutputFunctionCall(foutoid, val);
    1025             : }
    1026             : 
    1027             : Datum
    1028       46490 : SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
    1029             : {
    1030       46490 :     SPI_result = 0;
    1031             : 
    1032       46490 :     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       46490 :     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        4584 : SPI_datumTransfer(Datum value, bool typByVal, int typLen)
    1138             : {
    1139             :     MemoryContext oldcxt;
    1140             :     Datum       result;
    1141             : 
    1142        4584 :     if (_SPI_current == NULL)
    1143           0 :         elog(ERROR, "SPI_datumTransfer called while not connected to SPI");
    1144             : 
    1145        4584 :     oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
    1146             : 
    1147        4584 :     result = datumTransfer(value, typByVal, typLen);
    1148             : 
    1149        4584 :     MemoryContextSwitchTo(oldcxt);
    1150             : 
    1151        4584 :     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       52886 : SPI_freetuptable(SPITupleTable *tuptable)
    1163             : {
    1164       52886 :     bool        found = false;
    1165             : 
    1166             :     /* ignore call if NULL pointer */
    1167       52886 :     if (tuptable == NULL)
    1168       30406 :         return;
    1169             : 
    1170             :     /*
    1171             :      * Search only the topmost SPI context for a matching tuple table.
    1172             :      */
    1173       22480 :     if (_SPI_current != NULL)
    1174             :     {
    1175             :         slist_mutable_iter siter;
    1176             : 
    1177             :         /* find tuptable in active list, then remove it */
    1178       22480 :         slist_foreach_modify(siter, &_SPI_current->tuptables)
    1179             :         {
    1180             :             SPITupleTable *tt;
    1181             : 
    1182       22480 :             tt = slist_container(SPITupleTable, next, siter.cur);
    1183       22480 :             if (tt == tuptable)
    1184             :             {
    1185       22480 :                 slist_delete_current(&siter);
    1186       22480 :                 found = true;
    1187       22480 :                 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       22480 :     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       22480 :     if (tuptable == _SPI_current->tuptable)
    1206           0 :         _SPI_current->tuptable = NULL;
    1207       22480 :     if (tuptable == SPI_tuptable)
    1208       21778 :         SPI_tuptable = NULL;
    1209             : 
    1210             :     /* release all memory belonging to tuptable */
    1211       22480 :     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        2410 : 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        2410 :     if (src == NULL || nargs < 0)
    1259           0 :         elog(ERROR, "SPI_cursor_open_with_args called with invalid arguments");
    1260             : 
    1261        2410 :     if (nargs > 0 && (argtypes == NULL || Values == NULL))
    1262           0 :         elog(ERROR, "SPI_cursor_open_with_args called with missing parameters");
    1263             : 
    1264        2410 :     SPI_result = _SPI_begin_call(true);
    1265        2410 :     if (SPI_result < 0)
    1266           0 :         elog(ERROR, "SPI_cursor_open_with_args called while not connected");
    1267             : 
    1268        2410 :     memset(&plan, 0, sizeof(_SPI_plan));
    1269        2410 :     plan.magic = _SPI_PLAN_MAGIC;
    1270        2410 :     plan.cursor_options = cursorOptions;
    1271        2410 :     plan.nargs = nargs;
    1272        2410 :     plan.argtypes = argtypes;
    1273        2410 :     plan.parserSetup = NULL;
    1274        2410 :     plan.parserSetupArg = NULL;
    1275             : 
    1276             :     /* build transient ParamListInfo in executor context */
    1277        2410 :     paramLI = _SPI_convert_params(nargs, argtypes,
    1278             :                                   Values, Nulls);
    1279             : 
    1280        2410 :     _SPI_prepare_plan(src, &plan);
    1281             : 
    1282             :     /* We needn't copy the plan; SPI_cursor_open_internal will do so */
    1283             : 
    1284        2410 :     result = SPI_cursor_open_internal(name, &plan, paramLI, read_only);
    1285             : 
    1286             :     /* And clean up */
    1287        2410 :     _SPI_end_call(true);
    1288             : 
    1289        2410 :     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        1710 : SPI_cursor_open_with_paramlist(const char *name, SPIPlanPtr plan,
    1301             :                                ParamListInfo params, bool read_only)
    1302             : {
    1303        1710 :     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        4280 : 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        4280 :     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             :                         plansource->commandTag)));
    1342             :     }
    1343             : 
    1344             :     Assert(list_length(plan->plancache_list) == 1);
    1345        4280 :     plansource = (CachedPlanSource *) linitial(plan->plancache_list);
    1346             : 
    1347             :     /* Push the SPI stack */
    1348        4280 :     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        4280 :     SPI_processed = 0;
    1353        4280 :     SPI_tuptable = NULL;
    1354        4280 :     _SPI_current->processed = 0;
    1355        4280 :     _SPI_current->tuptable = NULL;
    1356             : 
    1357             :     /* Create the portal */
    1358        4280 :     if (name == NULL || name[0] == '\0')
    1359             :     {
    1360             :         /* Use a random nonconflicting name */
    1361        4204 :         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        4280 :     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        4280 :     spierrcontext.callback = _SPI_error_callback;
    1378        4280 :     spierrcontext.arg = unconstify(char *, plansource->query_string);
    1379        4280 :     spierrcontext.previous = error_context_stack;
    1380        4280 :     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        4280 :     cplan = GetCachedPlan(plansource, paramLI, false, _SPI_current->queryEnv);
    1390        4280 :     stmt_list = cplan->stmt_list;
    1391             : 
    1392        4280 :     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        2560 :         oldcontext = MemoryContextSwitchTo(portal->portalContext);
    1401        2560 :         stmt_list = copyObject(stmt_list);
    1402        2560 :         MemoryContextSwitchTo(oldcontext);
    1403        2560 :         ReleaseCachedPlan(cplan, false);
    1404        2560 :         cplan = NULL;           /* portal shouldn't depend on cplan */
    1405             :     }
    1406             : 
    1407             :     /*
    1408             :      * Set up the portal.
    1409             :      */
    1410        4280 :     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        4280 :     portal->cursorOptions = plan->cursor_options;
    1422        4280 :     if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
    1423             :     {
    1424        8520 :         if (list_length(stmt_list) == 1 &&
    1425        6156 :             linitial_node(PlannedStmt, stmt_list)->commandType != CMD_UTILITY &&
    1426        3792 :             linitial_node(PlannedStmt, stmt_list)->rowMarks == NIL &&
    1427        1896 :             ExecSupportsBackwardScan(linitial_node(PlannedStmt, stmt_list)->planTree))
    1428        1236 :             portal->cursorOptions |= CURSOR_OPT_SCROLL;
    1429             :         else
    1430        3024 :             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        4280 :     if (portal->cursorOptions & CURSOR_OPT_SCROLL)
    1439             :     {
    1440        2504 :         if (list_length(stmt_list) == 1 &&
    1441        2504 :             linitial_node(PlannedStmt, stmt_list)->commandType != CMD_UTILITY &&
    1442        1252 :             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        4280 :     portal->queryEnv = _SPI_current->queryEnv;
    1451             : 
    1452             :     /*
    1453             :      * If told to be read-only, or in parallel mode, verify that this query is
    1454             :      * in fact read-only.  This can't be done earlier because we need to look
    1455             :      * at the finished, planned queries.  (In particular, we don't want to do
    1456             :      * it between GetCachedPlan and PortalDefineQuery, because throwing an
    1457             :      * error between those steps would result in leaking our plancache
    1458             :      * refcount.)
    1459             :      */
    1460        4280 :     if (read_only || IsInParallelMode())
    1461             :     {
    1462             :         ListCell   *lc;
    1463             : 
    1464         216 :         foreach(lc, stmt_list)
    1465             :         {
    1466         108 :             PlannedStmt *pstmt = lfirst_node(PlannedStmt, lc);
    1467             : 
    1468         108 :             if (!CommandIsReadOnly(pstmt))
    1469             :             {
    1470           0 :                 if (read_only)
    1471           0 :                     ereport(ERROR,
    1472             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1473             :                     /* translator: %s is a SQL statement name */
    1474             :                              errmsg("%s is not allowed in a non-volatile function",
    1475             :                                     CreateCommandTag((Node *) pstmt))));
    1476             :                 else
    1477           0 :                     PreventCommandIfParallelMode(CreateCommandTag((Node *) pstmt));
    1478             :             }
    1479             :         }
    1480             :     }
    1481             : 
    1482             :     /* Set up the snapshot to use. */
    1483        4280 :     if (read_only)
    1484         108 :         snapshot = GetActiveSnapshot();
    1485             :     else
    1486             :     {
    1487        4172 :         CommandCounterIncrement();
    1488        4172 :         snapshot = GetTransactionSnapshot();
    1489             :     }
    1490             : 
    1491             :     /*
    1492             :      * If the plan has parameters, copy them into the portal.  Note that this
    1493             :      * must be done after revalidating the plan, because in dynamic parameter
    1494             :      * cases the set of parameters could have changed during re-parsing.
    1495             :      */
    1496        4280 :     if (paramLI)
    1497             :     {
    1498         702 :         oldcontext = MemoryContextSwitchTo(portal->portalContext);
    1499         702 :         paramLI = copyParamList(paramLI);
    1500         702 :         MemoryContextSwitchTo(oldcontext);
    1501             :     }
    1502             : 
    1503             :     /*
    1504             :      * Start portal execution.
    1505             :      */
    1506        4280 :     PortalStart(portal, paramLI, 0, snapshot);
    1507             : 
    1508             :     Assert(portal->strategy != PORTAL_MULTI_QUERY);
    1509             : 
    1510             :     /* Pop the error context stack */
    1511        4280 :     error_context_stack = spierrcontext.previous;
    1512             : 
    1513             :     /* Pop the SPI stack */
    1514        4280 :     _SPI_end_call(true);
    1515             : 
    1516             :     /* Return the created portal */
    1517        4280 :     return portal;
    1518             : }
    1519             : 
    1520             : 
    1521             : /*
    1522             :  * SPI_cursor_find()
    1523             :  *
    1524             :  *  Find the portal of an existing open cursor
    1525             :  */
    1526             : Portal
    1527         446 : SPI_cursor_find(const char *name)
    1528             : {
    1529         446 :     return GetPortalByName(name);
    1530             : }
    1531             : 
    1532             : 
    1533             : /*
    1534             :  * SPI_cursor_fetch()
    1535             :  *
    1536             :  *  Fetch rows in a cursor
    1537             :  */
    1538             : void
    1539        8220 : SPI_cursor_fetch(Portal portal, bool forward, long count)
    1540             : {
    1541       16440 :     _SPI_cursor_operation(portal,
    1542        8220 :                           forward ? FETCH_FORWARD : FETCH_BACKWARD, count,
    1543             :                           CreateDestReceiver(DestSPI));
    1544             :     /* we know that the DestSPI receiver doesn't need a destroy call */
    1545        8220 : }
    1546             : 
    1547             : 
    1548             : /*
    1549             :  * SPI_cursor_move()
    1550             :  *
    1551             :  *  Move in a cursor
    1552             :  */
    1553             : void
    1554           0 : SPI_cursor_move(Portal portal, bool forward, long count)
    1555             : {
    1556           0 :     _SPI_cursor_operation(portal,
    1557           0 :                           forward ? FETCH_FORWARD : FETCH_BACKWARD, count,
    1558             :                           None_Receiver);
    1559           0 : }
    1560             : 
    1561             : 
    1562             : /*
    1563             :  * SPI_scroll_cursor_fetch()
    1564             :  *
    1565             :  *  Fetch rows in a scrollable cursor
    1566             :  */
    1567             : void
    1568         200 : SPI_scroll_cursor_fetch(Portal portal, FetchDirection direction, long count)
    1569             : {
    1570         200 :     _SPI_cursor_operation(portal,
    1571             :                           direction, count,
    1572             :                           CreateDestReceiver(DestSPI));
    1573             :     /* we know that the DestSPI receiver doesn't need a destroy call */
    1574         196 : }
    1575             : 
    1576             : 
    1577             : /*
    1578             :  * SPI_scroll_cursor_move()
    1579             :  *
    1580             :  *  Move in a scrollable cursor
    1581             :  */
    1582             : void
    1583          28 : SPI_scroll_cursor_move(Portal portal, FetchDirection direction, long count)
    1584             : {
    1585          28 :     _SPI_cursor_operation(portal, direction, count, None_Receiver);
    1586          28 : }
    1587             : 
    1588             : 
    1589             : /*
    1590             :  * SPI_cursor_close()
    1591             :  *
    1592             :  *  Close a cursor
    1593             :  */
    1594             : void
    1595        4212 : SPI_cursor_close(Portal portal)
    1596             : {
    1597        4212 :     if (!PortalIsValid(portal))
    1598           0 :         elog(ERROR, "invalid portal in SPI cursor operation");
    1599             : 
    1600        4212 :     PortalDrop(portal, false);
    1601        4212 : }
    1602             : 
    1603             : /*
    1604             :  * Returns the Oid representing the type id for argument at argIndex. First
    1605             :  * parameter is at index zero.
    1606             :  */
    1607             : Oid
    1608           0 : SPI_getargtypeid(SPIPlanPtr plan, int argIndex)
    1609             : {
    1610           0 :     if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC ||
    1611           0 :         argIndex < 0 || argIndex >= plan->nargs)
    1612             :     {
    1613           0 :         SPI_result = SPI_ERROR_ARGUMENT;
    1614           0 :         return InvalidOid;
    1615             :     }
    1616           0 :     return plan->argtypes[argIndex];
    1617             : }
    1618             : 
    1619             : /*
    1620             :  * Returns the number of arguments for the prepared plan.
    1621             :  */
    1622             : int
    1623           0 : SPI_getargcount(SPIPlanPtr plan)
    1624             : {
    1625           0 :     if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
    1626             :     {
    1627           0 :         SPI_result = SPI_ERROR_ARGUMENT;
    1628           0 :         return -1;
    1629             :     }
    1630           0 :     return plan->nargs;
    1631             : }
    1632             : 
    1633             : /*
    1634             :  * Returns true if the plan contains exactly one command
    1635             :  * and that command returns tuples to the caller (eg, SELECT or
    1636             :  * INSERT ... RETURNING, but not SELECT ... INTO). In essence,
    1637             :  * the result indicates if the command can be used with SPI_cursor_open
    1638             :  *
    1639             :  * Parameters
    1640             :  *    plan: A plan previously prepared using SPI_prepare
    1641             :  */
    1642             : bool
    1643        4280 : SPI_is_cursor_plan(SPIPlanPtr plan)
    1644             : {
    1645             :     CachedPlanSource *plansource;
    1646             : 
    1647        4280 :     if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
    1648             :     {
    1649           0 :         SPI_result = SPI_ERROR_ARGUMENT;
    1650           0 :         return false;
    1651             :     }
    1652             : 
    1653        4280 :     if (list_length(plan->plancache_list) != 1)
    1654             :     {
    1655           0 :         SPI_result = 0;
    1656           0 :         return false;           /* not exactly 1 pre-rewrite command */
    1657             :     }
    1658        4280 :     plansource = (CachedPlanSource *) linitial(plan->plancache_list);
    1659             : 
    1660             :     /*
    1661             :      * We used to force revalidation of the cached plan here, but that seems
    1662             :      * unnecessary: invalidation could mean a change in the rowtype of the
    1663             :      * tuples returned by a plan, but not whether it returns tuples at all.
    1664             :      */
    1665        4280 :     SPI_result = 0;
    1666             : 
    1667             :     /* Does it return tuples? */
    1668        4280 :     if (plansource->resultDesc)
    1669        4280 :         return true;
    1670             : 
    1671           0 :     return false;
    1672             : }
    1673             : 
    1674             : /*
    1675             :  * SPI_plan_is_valid --- test whether a SPI plan is currently valid
    1676             :  * (that is, not marked as being in need of revalidation).
    1677             :  *
    1678             :  * See notes for CachedPlanIsValid before using this.
    1679             :  */
    1680             : bool
    1681        1676 : SPI_plan_is_valid(SPIPlanPtr plan)
    1682             : {
    1683             :     ListCell   *lc;
    1684             : 
    1685             :     Assert(plan->magic == _SPI_PLAN_MAGIC);
    1686             : 
    1687        3196 :     foreach(lc, plan->plancache_list)
    1688             :     {
    1689        1676 :         CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
    1690             : 
    1691        1676 :         if (!CachedPlanIsValid(plansource))
    1692         156 :             return false;
    1693             :     }
    1694        1520 :     return true;
    1695             : }
    1696             : 
    1697             : /*
    1698             :  * SPI_result_code_string --- convert any SPI return code to a string
    1699             :  *
    1700             :  * This is often useful in error messages.  Most callers will probably
    1701             :  * only pass negative (error-case) codes, but for generality we recognize
    1702             :  * the success codes too.
    1703             :  */
    1704             : const char *
    1705         106 : SPI_result_code_string(int code)
    1706             : {
    1707             :     static char buf[64];
    1708             : 
    1709         106 :     switch (code)
    1710             :     {
    1711             :         case SPI_ERROR_CONNECT:
    1712           0 :             return "SPI_ERROR_CONNECT";
    1713             :         case SPI_ERROR_COPY:
    1714           0 :             return "SPI_ERROR_COPY";
    1715             :         case SPI_ERROR_OPUNKNOWN:
    1716           0 :             return "SPI_ERROR_OPUNKNOWN";
    1717             :         case SPI_ERROR_UNCONNECTED:
    1718           0 :             return "SPI_ERROR_UNCONNECTED";
    1719             :         case SPI_ERROR_ARGUMENT:
    1720           0 :             return "SPI_ERROR_ARGUMENT";
    1721             :         case SPI_ERROR_PARAM:
    1722           0 :             return "SPI_ERROR_PARAM";
    1723             :         case SPI_ERROR_TRANSACTION:
    1724           6 :             return "SPI_ERROR_TRANSACTION";
    1725             :         case SPI_ERROR_NOATTRIBUTE:
    1726           0 :             return "SPI_ERROR_NOATTRIBUTE";
    1727             :         case SPI_ERROR_NOOUTFUNC:
    1728           0 :             return "SPI_ERROR_NOOUTFUNC";
    1729             :         case SPI_ERROR_TYPUNKNOWN:
    1730           0 :             return "SPI_ERROR_TYPUNKNOWN";
    1731             :         case SPI_ERROR_REL_DUPLICATE:
    1732           0 :             return "SPI_ERROR_REL_DUPLICATE";
    1733             :         case SPI_ERROR_REL_NOT_FOUND:
    1734           0 :             return "SPI_ERROR_REL_NOT_FOUND";
    1735             :         case SPI_OK_CONNECT:
    1736           0 :             return "SPI_OK_CONNECT";
    1737             :         case SPI_OK_FINISH:
    1738           0 :             return "SPI_OK_FINISH";
    1739             :         case SPI_OK_FETCH:
    1740           0 :             return "SPI_OK_FETCH";
    1741             :         case SPI_OK_UTILITY:
    1742           2 :             return "SPI_OK_UTILITY";
    1743             :         case SPI_OK_SELECT:
    1744          16 :             return "SPI_OK_SELECT";
    1745             :         case SPI_OK_SELINTO:
    1746           0 :             return "SPI_OK_SELINTO";
    1747             :         case SPI_OK_INSERT:
    1748          82 :             return "SPI_OK_INSERT";
    1749             :         case SPI_OK_DELETE:
    1750           0 :             return "SPI_OK_DELETE";
    1751             :         case SPI_OK_UPDATE:
    1752           0 :             return "SPI_OK_UPDATE";
    1753             :         case SPI_OK_CURSOR:
    1754           0 :             return "SPI_OK_CURSOR";
    1755             :         case SPI_OK_INSERT_RETURNING:
    1756           0 :             return "SPI_OK_INSERT_RETURNING";
    1757             :         case SPI_OK_DELETE_RETURNING:
    1758           0 :             return "SPI_OK_DELETE_RETURNING";
    1759             :         case SPI_OK_UPDATE_RETURNING:
    1760           0 :             return "SPI_OK_UPDATE_RETURNING";
    1761             :         case SPI_OK_REWRITTEN:
    1762           0 :             return "SPI_OK_REWRITTEN";
    1763             :         case SPI_OK_REL_REGISTER:
    1764           0 :             return "SPI_OK_REL_REGISTER";
    1765             :         case SPI_OK_REL_UNREGISTER:
    1766           0 :             return "SPI_OK_REL_UNREGISTER";
    1767             :     }
    1768             :     /* Unrecognized code ... return something useful ... */
    1769           0 :     sprintf(buf, "Unrecognized SPI code %d", code);
    1770           0 :     return buf;
    1771             : }
    1772             : 
    1773             : /*
    1774             :  * SPI_plan_get_plan_sources --- get a SPI plan's underlying list of
    1775             :  * CachedPlanSources.
    1776             :  *
    1777             :  * This is exported so that PL/pgSQL can use it (this beats letting PL/pgSQL
    1778             :  * look directly into the SPIPlan for itself).  It's not documented in
    1779             :  * spi.sgml because we'd just as soon not have too many places using this.
    1780             :  */
    1781             : List *
    1782       10824 : SPI_plan_get_plan_sources(SPIPlanPtr plan)
    1783             : {
    1784             :     Assert(plan->magic == _SPI_PLAN_MAGIC);
    1785       10824 :     return plan->plancache_list;
    1786             : }
    1787             : 
    1788             : /*
    1789             :  * SPI_plan_get_cached_plan --- get a SPI plan's generic CachedPlan,
    1790             :  * if the SPI plan contains exactly one CachedPlanSource.  If not,
    1791             :  * return NULL.  Caller is responsible for doing ReleaseCachedPlan().
    1792             :  *
    1793             :  * This is exported so that PL/pgSQL can use it (this beats letting PL/pgSQL
    1794             :  * look directly into the SPIPlan for itself).  It's not documented in
    1795             :  * spi.sgml because we'd just as soon not have too many places using this.
    1796             :  */
    1797             : CachedPlan *
    1798      153150 : SPI_plan_get_cached_plan(SPIPlanPtr plan)
    1799             : {
    1800             :     CachedPlanSource *plansource;
    1801             :     CachedPlan *cplan;
    1802             :     ErrorContextCallback spierrcontext;
    1803             : 
    1804             :     Assert(plan->magic == _SPI_PLAN_MAGIC);
    1805             : 
    1806             :     /* Can't support one-shot plans here */
    1807      153150 :     if (plan->oneshot)
    1808           0 :         return NULL;
    1809             : 
    1810             :     /* Must have exactly one CachedPlanSource */
    1811      153150 :     if (list_length(plan->plancache_list) != 1)
    1812           0 :         return NULL;
    1813      153150 :     plansource = (CachedPlanSource *) linitial(plan->plancache_list);
    1814             : 
    1815             :     /* Setup error traceback support for ereport() */
    1816      153150 :     spierrcontext.callback = _SPI_error_callback;
    1817      153150 :     spierrcontext.arg = unconstify(char *, plansource->query_string);
    1818      153150 :     spierrcontext.previous = error_context_stack;
    1819      153150 :     error_context_stack = &spierrcontext;
    1820             : 
    1821             :     /* Get the generic plan for the query */
    1822      153150 :     cplan = GetCachedPlan(plansource, NULL, plan->saved,
    1823      153150 :                           _SPI_current->queryEnv);
    1824             :     Assert(cplan == plansource->gplan);
    1825             : 
    1826             :     /* Pop the error context stack */
    1827      153128 :     error_context_stack = spierrcontext.previous;
    1828             : 
    1829      153128 :     return cplan;
    1830             : }
    1831             : 
    1832             : 
    1833             : /* =================== private functions =================== */
    1834             : 
    1835             : /*
    1836             :  * spi_dest_startup
    1837             :  *      Initialize to receive tuples from Executor into SPITupleTable
    1838             :  *      of current SPI procedure
    1839             :  */
    1840             : void
    1841       30094 : spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
    1842             : {
    1843             :     SPITupleTable *tuptable;
    1844             :     MemoryContext oldcxt;
    1845             :     MemoryContext tuptabcxt;
    1846             : 
    1847       30094 :     if (_SPI_current == NULL)
    1848           0 :         elog(ERROR, "spi_dest_startup called while not connected to SPI");
    1849             : 
    1850       30094 :     if (_SPI_current->tuptable != NULL)
    1851           0 :         elog(ERROR, "improper call to spi_dest_startup");
    1852             : 
    1853             :     /* We create the tuple table context as a child of procCxt */
    1854             : 
    1855       30094 :     oldcxt = _SPI_procmem();    /* switch to procedure memory context */
    1856             : 
    1857       30094 :     tuptabcxt = AllocSetContextCreate(CurrentMemoryContext,
    1858             :                                       "SPI TupTable",
    1859             :                                       ALLOCSET_DEFAULT_SIZES);
    1860       30094 :     MemoryContextSwitchTo(tuptabcxt);
    1861             : 
    1862       30094 :     _SPI_current->tuptable = tuptable = (SPITupleTable *)
    1863             :         palloc0(sizeof(SPITupleTable));
    1864       30094 :     tuptable->tuptabcxt = tuptabcxt;
    1865       30094 :     tuptable->subid = GetCurrentSubTransactionId();
    1866             : 
    1867             :     /*
    1868             :      * The tuptable is now valid enough to be freed by AtEOSubXact_SPI, so put
    1869             :      * it onto the SPI context's tuptables list.  This will ensure it's not
    1870             :      * leaked even in the unlikely event the following few lines fail.
    1871             :      */
    1872       30094 :     slist_push_head(&_SPI_current->tuptables, &tuptable->next);
    1873             : 
    1874             :     /* set up initial allocations */
    1875       30094 :     tuptable->alloced = 128;
    1876       30094 :     tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
    1877       30094 :     tuptable->numvals = 0;
    1878       30094 :     tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
    1879             : 
    1880       30094 :     MemoryContextSwitchTo(oldcxt);
    1881       30094 : }
    1882             : 
    1883             : /*
    1884             :  * spi_printtup
    1885             :  *      store tuple retrieved by Executor into SPITupleTable
    1886             :  *      of current SPI procedure
    1887             :  */
    1888             : bool
    1889       54762 : spi_printtup(TupleTableSlot *slot, DestReceiver *self)
    1890             : {
    1891             :     SPITupleTable *tuptable;
    1892             :     MemoryContext oldcxt;
    1893             : 
    1894       54762 :     if (_SPI_current == NULL)
    1895           0 :         elog(ERROR, "spi_printtup called while not connected to SPI");
    1896             : 
    1897       54762 :     tuptable = _SPI_current->tuptable;
    1898       54762 :     if (tuptable == NULL)
    1899           0 :         elog(ERROR, "improper call to spi_printtup");
    1900             : 
    1901       54762 :     oldcxt = MemoryContextSwitchTo(tuptable->tuptabcxt);
    1902             : 
    1903       54762 :     if (tuptable->numvals >= tuptable->alloced)
    1904             :     {
    1905             :         /* Double the size of the pointer array */
    1906           0 :         uint64      newalloced = tuptable->alloced * 2;
    1907             : 
    1908           0 :         tuptable->vals = (HeapTuple *) repalloc_huge(tuptable->vals,
    1909             :                                                      newalloced * sizeof(HeapTuple));
    1910           0 :         tuptable->alloced = newalloced;
    1911             :     }
    1912             : 
    1913       54762 :     tuptable->vals[tuptable->numvals] = ExecCopySlotHeapTuple(slot);
    1914       54762 :     (tuptable->numvals)++;
    1915             : 
    1916       54762 :     MemoryContextSwitchTo(oldcxt);
    1917             : 
    1918       54762 :     return true;
    1919             : }
    1920             : 
    1921             : /*
    1922             :  * Static functions
    1923             :  */
    1924             : 
    1925             : /*
    1926             :  * Parse and analyze a querystring.
    1927             :  *
    1928             :  * At entry, plan->argtypes and plan->nargs (or alternatively plan->parserSetup
    1929             :  * and plan->parserSetupArg) must be valid, as must plan->cursor_options.
    1930             :  *
    1931             :  * Results are stored into *plan (specifically, plan->plancache_list).
    1932             :  * Note that the result data is all in CurrentMemoryContext or child contexts
    1933             :  * thereof; in practice this means it is in the SPI executor context, and
    1934             :  * what we are creating is a "temporary" SPIPlan.  Cruft generated during
    1935             :  * parsing is also left in CurrentMemoryContext.
    1936             :  */
    1937             : static void
    1938       15898 : _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
    1939             : {
    1940             :     List       *raw_parsetree_list;
    1941             :     List       *plancache_list;
    1942             :     ListCell   *list_item;
    1943             :     ErrorContextCallback spierrcontext;
    1944             : 
    1945             :     /*
    1946             :      * Setup error traceback support for ereport()
    1947             :      */
    1948       15898 :     spierrcontext.callback = _SPI_error_callback;
    1949       15898 :     spierrcontext.arg = unconstify(char *, src);
    1950       15898 :     spierrcontext.previous = error_context_stack;
    1951       15898 :     error_context_stack = &spierrcontext;
    1952             : 
    1953             :     /*
    1954             :      * Parse the request string into a list of raw parse trees.
    1955             :      */
    1956       15898 :     raw_parsetree_list = pg_parse_query(src);
    1957             : 
    1958             :     /*
    1959             :      * Do parse analysis and rule rewrite for each raw parsetree, storing the
    1960             :      * results into unsaved plancache entries.
    1961             :      */
    1962       15898 :     plancache_list = NIL;
    1963             : 
    1964       31766 :     foreach(list_item, raw_parsetree_list)
    1965             :     {
    1966       15898 :         RawStmt    *parsetree = lfirst_node(RawStmt, list_item);
    1967             :         List       *stmt_list;
    1968             :         CachedPlanSource *plansource;
    1969             : 
    1970             :         /*
    1971             :          * Create the CachedPlanSource before we do parse analysis, since it
    1972             :          * needs to see the unmodified raw parse tree.
    1973             :          */
    1974       15898 :         plansource = CreateCachedPlan(parsetree,
    1975             :                                       src,
    1976             :                                       CreateCommandTag(parsetree->stmt));
    1977             : 
    1978             :         /*
    1979             :          * Parameter datatypes are driven by parserSetup hook if provided,
    1980             :          * otherwise we use the fixed parameter list.
    1981             :          */
    1982       15898 :         if (plan->parserSetup != NULL)
    1983             :         {
    1984             :             Assert(plan->nargs == 0);
    1985       10148 :             stmt_list = pg_analyze_and_rewrite_params(parsetree,
    1986             :                                                       src,
    1987             :                                                       plan->parserSetup,
    1988             :                                                       plan->parserSetupArg,
    1989       10148 :                                                       _SPI_current->queryEnv);
    1990             :         }
    1991             :         else
    1992             :         {
    1993        5750 :             stmt_list = pg_analyze_and_rewrite(parsetree,
    1994             :                                                src,
    1995             :                                                plan->argtypes,
    1996             :                                                plan->nargs,
    1997        5750 :                                                _SPI_current->queryEnv);
    1998             :         }
    1999             : 
    2000             :         /* Finish filling in the CachedPlanSource */
    2001       15868 :         CompleteCachedPlan(plansource,
    2002             :                            stmt_list,
    2003             :                            NULL,
    2004             :                            plan->argtypes,
    2005             :                            plan->nargs,
    2006             :                            plan->parserSetup,
    2007             :                            plan->parserSetupArg,
    2008             :                            plan->cursor_options,
    2009             :                            false);  /* not fixed result */
    2010             : 
    2011       15868 :         plancache_list = lappend(plancache_list, plansource);
    2012             :     }
    2013             : 
    2014       15868 :     plan->plancache_list = plancache_list;
    2015       15868 :     plan->oneshot = false;
    2016             : 
    2017             :     /*
    2018             :      * Pop the error context stack
    2019             :      */
    2020       15868 :     error_context_stack = spierrcontext.previous;
    2021       15868 : }
    2022             : 
    2023             : /*
    2024             :  * Parse, but don't analyze, a querystring.
    2025             :  *
    2026             :  * This is a stripped-down version of _SPI_prepare_plan that only does the
    2027             :  * initial raw parsing.  It creates "one shot" CachedPlanSources
    2028             :  * that still require parse analysis before execution is possible.
    2029             :  *
    2030             :  * The advantage of using the "one shot" form of CachedPlanSource is that
    2031             :  * we eliminate data copying and invalidation overhead.  Postponing parse
    2032             :  * analysis also prevents issues if some of the raw parsetrees are DDL
    2033             :  * commands that affect validity of later parsetrees.  Both of these
    2034             :  * attributes are good things for SPI_execute() and similar cases.
    2035             :  *
    2036             :  * Results are stored into *plan (specifically, plan->plancache_list).
    2037             :  * Note that the result data is all in CurrentMemoryContext or child contexts
    2038             :  * thereof; in practice this means it is in the SPI executor context, and
    2039             :  * what we are creating is a "temporary" SPIPlan.  Cruft generated during
    2040             :  * parsing is also left in CurrentMemoryContext.
    2041             :  */
    2042             : static void
    2043        7370 : _SPI_prepare_oneshot_plan(const char *src, SPIPlanPtr plan)
    2044             : {
    2045             :     List       *raw_parsetree_list;
    2046             :     List       *plancache_list;
    2047             :     ListCell   *list_item;
    2048             :     ErrorContextCallback spierrcontext;
    2049             : 
    2050             :     /*
    2051             :      * Setup error traceback support for ereport()
    2052             :      */
    2053        7370 :     spierrcontext.callback = _SPI_error_callback;
    2054        7370 :     spierrcontext.arg = unconstify(char *, src);
    2055        7370 :     spierrcontext.previous = error_context_stack;
    2056        7370 :     error_context_stack = &spierrcontext;
    2057             : 
    2058             :     /*
    2059             :      * Parse the request string into a list of raw parse trees.
    2060             :      */
    2061        7370 :     raw_parsetree_list = pg_parse_query(src);
    2062             : 
    2063             :     /*
    2064             :      * Construct plancache entries, but don't do parse analysis yet.
    2065             :      */
    2066        7354 :     plancache_list = NIL;
    2067             : 
    2068       14710 :     foreach(list_item, raw_parsetree_list)
    2069             :     {
    2070        7356 :         RawStmt    *parsetree = lfirst_node(RawStmt, list_item);
    2071             :         CachedPlanSource *plansource;
    2072             : 
    2073        7356 :         plansource = CreateOneShotCachedPlan(parsetree,
    2074             :                                              src,
    2075             :                                              CreateCommandTag(parsetree->stmt));
    2076             : 
    2077        7356 :         plancache_list = lappend(plancache_list, plansource);
    2078             :     }
    2079             : 
    2080        7354 :     plan->plancache_list = plancache_list;
    2081        7354 :     plan->oneshot = true;
    2082             : 
    2083             :     /*
    2084             :      * Pop the error context stack
    2085             :      */
    2086        7354 :     error_context_stack = spierrcontext.previous;
    2087        7354 : }
    2088             : 
    2089             : /*
    2090             :  * Execute the given plan with the given parameter values
    2091             :  *
    2092             :  * snapshot: query snapshot to use, or InvalidSnapshot for the normal
    2093             :  *      behavior of taking a new snapshot for each query.
    2094             :  * crosscheck_snapshot: for RI use, all others pass InvalidSnapshot
    2095             :  * read_only: true for read-only execution (no CommandCounterIncrement)
    2096             :  * fire_triggers: true to fire AFTER triggers at end of query (normal case);
    2097             :  *      false means any AFTER triggers are postponed to end of outer query
    2098             :  * tcount: execution tuple-count limit, or 0 for none
    2099             :  */
    2100             : static int
    2101       31202 : _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
    2102             :                   Snapshot snapshot, Snapshot crosscheck_snapshot,
    2103             :                   bool read_only, bool fire_triggers, uint64 tcount)
    2104             : {
    2105       31202 :     int         my_res = 0;
    2106       31202 :     uint64      my_processed = 0;
    2107       31202 :     SPITupleTable *my_tuptable = NULL;
    2108       31202 :     int         res = 0;
    2109       31202 :     bool        pushed_active_snap = false;
    2110             :     ErrorContextCallback spierrcontext;
    2111       31202 :     CachedPlan *cplan = NULL;
    2112             :     ListCell   *lc1;
    2113             : 
    2114             :     /*
    2115             :      * Setup error traceback support for ereport()
    2116             :      */
    2117       31202 :     spierrcontext.callback = _SPI_error_callback;
    2118       31202 :     spierrcontext.arg = NULL;   /* we'll fill this below */
    2119       31202 :     spierrcontext.previous = error_context_stack;
    2120       31202 :     error_context_stack = &spierrcontext;
    2121             : 
    2122             :     /*
    2123             :      * We support four distinct snapshot management behaviors:
    2124             :      *
    2125             :      * snapshot != InvalidSnapshot, read_only = true: use exactly the given
    2126             :      * snapshot.
    2127             :      *
    2128             :      * snapshot != InvalidSnapshot, read_only = false: use the given snapshot,
    2129             :      * modified by advancing its command ID before each querytree.
    2130             :      *
    2131             :      * snapshot == InvalidSnapshot, read_only = true: use the entry-time
    2132             :      * ActiveSnapshot, if any (if there isn't one, we run with no snapshot).
    2133             :      *
    2134             :      * snapshot == InvalidSnapshot, read_only = false: take a full new
    2135             :      * snapshot for each user command, and advance its command ID before each
    2136             :      * querytree within the command.
    2137             :      *
    2138             :      * In the first two cases, we can just push the snap onto the stack once
    2139             :      * for the whole plan list.
    2140             :      *
    2141             :      * But if the plan has no_snapshots set to true, then don't manage
    2142             :      * snapshots at all.  The caller should then take care of that.
    2143             :      */
    2144       31202 :     if (snapshot != InvalidSnapshot && !plan->no_snapshots)
    2145             :     {
    2146         648 :         if (read_only)
    2147             :         {
    2148         648 :             PushActiveSnapshot(snapshot);
    2149         648 :             pushed_active_snap = true;
    2150             :         }
    2151             :         else
    2152             :         {
    2153             :             /* Make sure we have a private copy of the snapshot to modify */
    2154           0 :             PushCopiedSnapshot(snapshot);
    2155           0 :             pushed_active_snap = true;
    2156             :         }
    2157             :     }
    2158             : 
    2159       61344 :     foreach(lc1, plan->plancache_list)
    2160             :     {
    2161       31202 :         CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc1);
    2162             :         List       *stmt_list;
    2163             :         ListCell   *lc2;
    2164             : 
    2165       31202 :         spierrcontext.arg = unconstify(char *, plansource->query_string);
    2166             : 
    2167             :         /*
    2168             :          * If this is a one-shot plan, we still need to do parse analysis.
    2169             :          */
    2170       31202 :         if (plan->oneshot)
    2171             :         {
    2172        7354 :             RawStmt    *parsetree = plansource->raw_parse_tree;
    2173        7354 :             const char *src = plansource->query_string;
    2174             :             List       *stmt_list;
    2175             : 
    2176             :             /*
    2177             :              * Parameter datatypes are driven by parserSetup hook if provided,
    2178             :              * otherwise we use the fixed parameter list.
    2179             :              */
    2180        7354 :             if (parsetree == NULL)
    2181           0 :                 stmt_list = NIL;
    2182        7354 :             else if (plan->parserSetup != NULL)
    2183             :             {
    2184             :                 Assert(plan->nargs == 0);
    2185           0 :                 stmt_list = pg_analyze_and_rewrite_params(parsetree,
    2186             :                                                           src,
    2187             :                                                           plan->parserSetup,
    2188             :                                                           plan->parserSetupArg,
    2189           0 :                                                           _SPI_current->queryEnv);
    2190             :             }
    2191             :             else
    2192             :             {
    2193        7354 :                 stmt_list = pg_analyze_and_rewrite(parsetree,
    2194             :                                                    src,
    2195             :                                                    plan->argtypes,
    2196             :                                                    plan->nargs,
    2197        7354 :                                                    _SPI_current->queryEnv);
    2198             :             }
    2199             : 
    2200             :             /* Finish filling in the CachedPlanSource */
    2201        7338 :             CompleteCachedPlan(plansource,
    2202             :                                stmt_list,
    2203             :                                NULL,
    2204             :                                plan->argtypes,
    2205             :                                plan->nargs,
    2206             :                                plan->parserSetup,
    2207             :                                plan->parserSetupArg,
    2208             :                                plan->cursor_options,
    2209             :                                false);  /* not fixed result */
    2210             :         }
    2211             : 
    2212             :         /*
    2213             :          * Replan if needed, and increment plan refcount.  If it's a saved
    2214             :          * plan, the refcount must be backed by the CurrentResourceOwner.
    2215             :          */
    2216       31186 :         cplan = GetCachedPlan(plansource, paramLI, plan->saved, _SPI_current->queryEnv);
    2217       31150 :         stmt_list = cplan->stmt_list;
    2218             : 
    2219             :         /*
    2220             :          * In the default non-read-only case, get a new snapshot, replacing
    2221             :          * any that we pushed in a previous cycle.
    2222             :          */
    2223       31150 :         if (snapshot == InvalidSnapshot && !read_only && !plan->no_snapshots)
    2224             :         {
    2225       21096 :             if (pushed_active_snap)
    2226           0 :                 PopActiveSnapshot();
    2227       21096 :             PushActiveSnapshot(GetTransactionSnapshot());
    2228       21096 :             pushed_active_snap = true;
    2229             :         }
    2230             : 
    2231       61292 :         foreach(lc2, stmt_list)
    2232             :         {
    2233       31150 :             PlannedStmt *stmt = lfirst_node(PlannedStmt, lc2);
    2234       31150 :             bool        canSetTag = stmt->canSetTag;
    2235             :             DestReceiver *dest;
    2236             : 
    2237       31150 :             _SPI_current->processed = 0;
    2238       31150 :             _SPI_current->tuptable = NULL;
    2239             : 
    2240       31150 :             if (stmt->utilityStmt)
    2241             :             {
    2242        4444 :                 if (IsA(stmt->utilityStmt, CopyStmt))
    2243             :                 {
    2244           0 :                     CopyStmt   *cstmt = (CopyStmt *) stmt->utilityStmt;
    2245             : 
    2246           0 :                     if (cstmt->filename == NULL)
    2247             :                     {
    2248           0 :                         my_res = SPI_ERROR_COPY;
    2249          10 :                         goto fail;
    2250             :                     }
    2251             :                 }
    2252        4444 :                 else if (IsA(stmt->utilityStmt, TransactionStmt))
    2253             :                 {
    2254          10 :                     my_res = SPI_ERROR_TRANSACTION;
    2255          10 :                     goto fail;
    2256             :                 }
    2257             :             }
    2258             : 
    2259       31140 :             if (read_only && !CommandIsReadOnly(stmt))
    2260           0 :                 ereport(ERROR,
    2261             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2262             :                 /* translator: %s is a SQL statement name */
    2263             :                          errmsg("%s is not allowed in a non-volatile function",
    2264             :                                 CreateCommandTag((Node *) stmt))));
    2265             : 
    2266       31140 :             if (IsInParallelMode() && !CommandIsReadOnly(stmt))
    2267           0 :                 PreventCommandIfParallelMode(CreateCommandTag((Node *) stmt));
    2268             : 
    2269             :             /*
    2270             :              * If not read-only mode, advance the command counter before each
    2271             :              * command and update the snapshot.
    2272             :              */
    2273       31140 :             if (!read_only && !plan->no_snapshots)
    2274             :             {
    2275       21086 :                 CommandCounterIncrement();
    2276       21086 :                 UpdateActiveSnapshotCommandId();
    2277             :             }
    2278             : 
    2279       31140 :             dest = CreateDestReceiver(canSetTag ? DestSPI : DestNone);
    2280             : 
    2281       31140 :             if (stmt->utilityStmt == NULL)
    2282             :             {
    2283             :                 QueryDesc  *qdesc;
    2284             :                 Snapshot    snap;
    2285             : 
    2286       26706 :                 if (ActiveSnapshotSet())
    2287       26706 :                     snap = GetActiveSnapshot();
    2288             :                 else
    2289           0 :                     snap = InvalidSnapshot;
    2290             : 
    2291       26706 :                 qdesc = CreateQueryDesc(stmt,
    2292             :                                         plansource->query_string,
    2293             :                                         snap, crosscheck_snapshot,
    2294             :                                         dest,
    2295       26706 :                                         paramLI, _SPI_current->queryEnv,
    2296             :                                         0);
    2297       26706 :                 res = _SPI_pquery(qdesc, fire_triggers,
    2298             :                                   canSetTag ? tcount : 0);
    2299       25794 :                 FreeQueryDesc(qdesc);
    2300             :             }
    2301             :             else
    2302             :             {
    2303             :                 char        completionTag[COMPLETION_TAG_BUFSIZE];
    2304             :                 ProcessUtilityContext context;
    2305             : 
    2306             :                 /*
    2307             :                  * If the SPI context is atomic, or we are asked to manage
    2308             :                  * snapshots, then we are in an atomic execution context.
    2309             :                  * Conversely, to propagate a nonatomic execution context, the
    2310             :                  * caller must be in a nonatomic SPI context and manage
    2311             :                  * snapshots itself.
    2312             :                  */
    2313        4434 :                 if (_SPI_current->atomic || !plan->no_snapshots)
    2314         396 :                     context = PROCESS_UTILITY_QUERY;
    2315             :                 else
    2316        4038 :                     context = PROCESS_UTILITY_QUERY_NONATOMIC;
    2317             : 
    2318        4434 :                 ProcessUtility(stmt,
    2319             :                                plansource->query_string,
    2320             :                                context,
    2321             :                                paramLI,
    2322        4434 :                                _SPI_current->queryEnv,
    2323             :                                dest,
    2324             :                                completionTag);
    2325             : 
    2326             :                 /* Update "processed" if stmt returned tuples */
    2327        4348 :                 if (_SPI_current->tuptable)
    2328          54 :                     _SPI_current->processed = _SPI_current->tuptable->numvals;
    2329             : 
    2330        4348 :                 res = SPI_OK_UTILITY;
    2331             : 
    2332             :                 /*
    2333             :                  * Some utility statements return a row count, even though the
    2334             :                  * tuples are not returned to the caller.
    2335             :                  */
    2336        4348 :                 if (IsA(stmt->utilityStmt, CreateTableAsStmt))
    2337             :                 {
    2338          40 :                     CreateTableAsStmt *ctastmt = (CreateTableAsStmt *) stmt->utilityStmt;
    2339             : 
    2340          40 :                     if (strncmp(completionTag, "SELECT ", 7) == 0)
    2341          72 :                         _SPI_current->processed =
    2342          36 :                             pg_strtouint64(completionTag + 7, NULL, 10);
    2343             :                     else
    2344             :                     {
    2345             :                         /*
    2346             :                          * Must be an IF NOT EXISTS that did nothing, or a
    2347             :                          * CREATE ... WITH NO DATA.
    2348             :                          */
    2349             :                         Assert(ctastmt->if_not_exists ||
    2350             :                                ctastmt->into->skipData);
    2351           4 :                         _SPI_current->processed = 0;
    2352             :                     }
    2353             : 
    2354             :                     /*
    2355             :                      * For historical reasons, if CREATE TABLE AS was spelled
    2356             :                      * as SELECT INTO, return a special return code.
    2357             :                      */
    2358          40 :                     if (ctastmt->is_select_into)
    2359           0 :                         res = SPI_OK_SELINTO;
    2360             :                 }
    2361        4308 :                 else if (IsA(stmt->utilityStmt, CopyStmt))
    2362             :                 {
    2363             :                     Assert(strncmp(completionTag, "COPY ", 5) == 0);
    2364           0 :                     _SPI_current->processed = pg_strtouint64(completionTag + 5,
    2365             :                                                              NULL, 10);
    2366             :                 }
    2367             :             }
    2368             : 
    2369             :             /*
    2370             :              * The last canSetTag query sets the status values returned to the
    2371             :              * caller.  Be careful to free any tuptables not returned, to
    2372             :              * avoid intra-transaction memory leak.
    2373             :              */
    2374       30142 :             if (canSetTag)
    2375             :             {
    2376       30142 :                 my_processed = _SPI_current->processed;
    2377       30142 :                 SPI_freetuptable(my_tuptable);
    2378       30142 :                 my_tuptable = _SPI_current->tuptable;
    2379       30142 :                 my_res = res;
    2380             :             }
    2381             :             else
    2382             :             {
    2383           0 :                 SPI_freetuptable(_SPI_current->tuptable);
    2384           0 :                 _SPI_current->tuptable = NULL;
    2385             :             }
    2386             :             /* we know that the receiver doesn't need a destroy call */
    2387       30142 :             if (res < 0)
    2388             :             {
    2389           0 :                 my_res = res;
    2390           0 :                 goto fail;
    2391             :             }
    2392             :         }
    2393             : 
    2394             :         /* Done with this plan, so release refcount */
    2395       30142 :         ReleaseCachedPlan(cplan, plan->saved);
    2396       30142 :         cplan = NULL;
    2397             : 
    2398             :         /*
    2399             :          * If not read-only mode, advance the command counter after the last
    2400             :          * command.  This ensures that its effects are visible, in case it was
    2401             :          * DDL that would affect the next CachedPlanSource.
    2402             :          */
    2403       30142 :         if (!read_only)
    2404       24156 :             CommandCounterIncrement();
    2405             :     }
    2406             : 
    2407             : fail:
    2408             : 
    2409             :     /* Pop the snapshot off the stack if we pushed one */
    2410       30152 :     if (pushed_active_snap)
    2411       20752 :         PopActiveSnapshot();
    2412             : 
    2413             :     /* We no longer need the cached plan refcount, if any */
    2414       30152 :     if (cplan)
    2415          10 :         ReleaseCachedPlan(cplan, plan->saved);
    2416             : 
    2417             :     /*
    2418             :      * Pop the error context stack
    2419             :      */
    2420       30152 :     error_context_stack = spierrcontext.previous;
    2421             : 
    2422             :     /* Save results for caller */
    2423       30152 :     SPI_processed = my_processed;
    2424       30152 :     SPI_tuptable = my_tuptable;
    2425             : 
    2426             :     /* tuptable now is caller's responsibility, not SPI's */
    2427       30152 :     _SPI_current->tuptable = NULL;
    2428             : 
    2429             :     /*
    2430             :      * If none of the queries had canSetTag, return SPI_OK_REWRITTEN. Prior to
    2431             :      * 8.4, we used return the last query's result code, but not its auxiliary
    2432             :      * results, but that's confusing.
    2433             :      */
    2434       30152 :     if (my_res == 0)
    2435           0 :         my_res = SPI_OK_REWRITTEN;
    2436             : 
    2437       30152 :     return my_res;
    2438             : }
    2439             : 
    2440             : /*
    2441             :  * Convert arrays of query parameters to form wanted by planner and executor
    2442             :  */
    2443             : static ParamListInfo
    2444       13338 : _SPI_convert_params(int nargs, Oid *argtypes,
    2445             :                     Datum *Values, const char *Nulls)
    2446             : {
    2447             :     ParamListInfo paramLI;
    2448             : 
    2449       13338 :     if (nargs > 0)
    2450             :     {
    2451        5962 :         paramLI = makeParamList(nargs);
    2452             : 
    2453       14772 :         for (int i = 0; i < nargs; i++)
    2454             :         {
    2455        8810 :             ParamExternData *prm = &paramLI->params[i];
    2456             : 
    2457        8810 :             prm->value = Values[i];
    2458        8810 :             prm->isnull = (Nulls && Nulls[i] == 'n');
    2459        8810 :             prm->pflags = PARAM_FLAG_CONST;
    2460        8810 :             prm->ptype = argtypes[i];
    2461             :         }
    2462             :     }
    2463             :     else
    2464        7376 :         paramLI = NULL;
    2465       13338 :     return paramLI;
    2466             : }
    2467             : 
    2468             : static int
    2469       26706 : _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount)
    2470             : {
    2471       26706 :     int         operation = queryDesc->operation;
    2472             :     int         eflags;
    2473             :     int         res;
    2474             : 
    2475       26706 :     switch (operation)
    2476             :     {
    2477             :         case CMD_SELECT:
    2478       21314 :             if (queryDesc->dest->mydest != DestSPI)
    2479             :             {
    2480             :                 /* Don't return SPI_OK_SELECT if we're discarding result */
    2481           0 :                 res = SPI_OK_UTILITY;
    2482             :             }
    2483             :             else
    2484       21314 :                 res = SPI_OK_SELECT;
    2485       21314 :             break;
    2486             :         case CMD_INSERT:
    2487        4318 :             if (queryDesc->plannedstmt->hasReturning)
    2488         304 :                 res = SPI_OK_INSERT_RETURNING;
    2489             :             else
    2490        4014 :                 res = SPI_OK_INSERT;
    2491        4318 :             break;
    2492             :         case CMD_DELETE:
    2493         200 :             if (queryDesc->plannedstmt->hasReturning)
    2494           0 :                 res = SPI_OK_DELETE_RETURNING;
    2495             :             else
    2496         200 :                 res = SPI_OK_DELETE;
    2497         200 :             break;
    2498             :         case CMD_UPDATE:
    2499         874 :             if (queryDesc->plannedstmt->hasReturning)
    2500           6 :                 res = SPI_OK_UPDATE_RETURNING;
    2501             :             else
    2502         868 :                 res = SPI_OK_UPDATE;
    2503         874 :             break;
    2504             :         default:
    2505           0 :             return SPI_ERROR_OPUNKNOWN;
    2506             :     }
    2507             : 
    2508             : #ifdef SPI_EXECUTOR_STATS
    2509             :     if (ShowExecutorStats)
    2510             :         ResetUsage();
    2511             : #endif
    2512             : 
    2513             :     /* Select execution options */
    2514       26706 :     if (fire_triggers)
    2515       22392 :         eflags = 0;             /* default run-to-completion flags */
    2516             :     else
    2517        4314 :         eflags = EXEC_FLAG_SKIP_TRIGGERS;
    2518             : 
    2519       26706 :     ExecutorStart(queryDesc, eflags);
    2520             : 
    2521       26706 :     ExecutorRun(queryDesc, ForwardScanDirection, tcount, true);
    2522             : 
    2523       25796 :     _SPI_current->processed = queryDesc->estate->es_processed;
    2524             : 
    2525       46542 :     if ((res == SPI_OK_SELECT || queryDesc->plannedstmt->hasReturning) &&
    2526       20746 :         queryDesc->dest->mydest == DestSPI)
    2527             :     {
    2528       20746 :         if (_SPI_checktuples())
    2529           0 :             elog(ERROR, "consistency check on SPI tuple count failed");
    2530             :     }
    2531             : 
    2532       25796 :     ExecutorFinish(queryDesc);
    2533       25794 :     ExecutorEnd(queryDesc);
    2534             :     /* FreeQueryDesc is done by the caller */
    2535             : 
    2536             : #ifdef SPI_EXECUTOR_STATS
    2537             :     if (ShowExecutorStats)
    2538             :         ShowUsage("SPI EXECUTOR STATS");
    2539             : #endif
    2540             : 
    2541       25794 :     return res;
    2542             : }
    2543             : 
    2544             : /*
    2545             :  * _SPI_error_callback
    2546             :  *
    2547             :  * Add context information when a query invoked via SPI fails
    2548             :  */
    2549             : static void
    2550        1434 : _SPI_error_callback(void *arg)
    2551             : {
    2552        1434 :     const char *query = (const char *) arg;
    2553             :     int         syntaxerrposition;
    2554             : 
    2555        1434 :     if (query == NULL)          /* in case arg wasn't set yet */
    2556           0 :         return;
    2557             : 
    2558             :     /*
    2559             :      * If there is a syntax error position, convert to internal syntax error;
    2560             :      * otherwise treat the query as an item of context stack
    2561             :      */
    2562        1434 :     syntaxerrposition = geterrposition();
    2563        1434 :     if (syntaxerrposition > 0)
    2564             :     {
    2565          58 :         errposition(0);
    2566          58 :         internalerrposition(syntaxerrposition);
    2567          58 :         internalerrquery(query);
    2568             :     }
    2569             :     else
    2570        1376 :         errcontext("SQL statement \"%s\"", query);
    2571             : }
    2572             : 
    2573             : /*
    2574             :  * _SPI_cursor_operation()
    2575             :  *
    2576             :  *  Do a FETCH or MOVE in a cursor
    2577             :  */
    2578             : static void
    2579        8448 : _SPI_cursor_operation(Portal portal, FetchDirection direction, long count,
    2580             :                       DestReceiver *dest)
    2581             : {
    2582             :     uint64      nfetched;
    2583             : 
    2584             :     /* Check that the portal is valid */
    2585        8448 :     if (!PortalIsValid(portal))
    2586           0 :         elog(ERROR, "invalid portal in SPI cursor operation");
    2587             : 
    2588             :     /* Push the SPI stack */
    2589        8448 :     if (_SPI_begin_call(true) < 0)
    2590           0 :         elog(ERROR, "SPI cursor operation called while not connected");
    2591             : 
    2592             :     /* Reset the SPI result (note we deliberately don't touch lastoid) */
    2593        8448 :     SPI_processed = 0;
    2594        8448 :     SPI_tuptable = NULL;
    2595        8448 :     _SPI_current->processed = 0;
    2596        8448 :     _SPI_current->tuptable = NULL;
    2597             : 
    2598             :     /* Run the cursor */
    2599        8448 :     nfetched = PortalRunFetch(portal,
    2600             :                               direction,
    2601             :                               count,
    2602             :                               dest);
    2603             : 
    2604             :     /*
    2605             :      * Think not to combine this store with the preceding function call. If
    2606             :      * the portal contains calls to functions that use SPI, then _SPI_stack is
    2607             :      * likely to move around while the portal runs.  When control returns,
    2608             :      * _SPI_current will point to the correct stack entry... but the pointer
    2609             :      * may be different than it was beforehand. So we must be sure to re-fetch
    2610             :      * the pointer after the function call completes.
    2611             :      */
    2612        8444 :     _SPI_current->processed = nfetched;
    2613             : 
    2614        8444 :     if (dest->mydest == DestSPI && _SPI_checktuples())
    2615           0 :         elog(ERROR, "consistency check on SPI tuple count failed");
    2616             : 
    2617             :     /* Put the result into place for access by caller */
    2618        8444 :     SPI_processed = _SPI_current->processed;
    2619        8444 :     SPI_tuptable = _SPI_current->tuptable;
    2620             : 
    2621             :     /* tuptable now is caller's responsibility, not SPI's */
    2622        8444 :     _SPI_current->tuptable = NULL;
    2623             : 
    2624             :     /* Pop the SPI stack */
    2625        8444 :     _SPI_end_call(true);
    2626        8444 : }
    2627             : 
    2628             : 
    2629             : static MemoryContext
    2630       59844 : _SPI_execmem(void)
    2631             : {
    2632       59844 :     return MemoryContextSwitchTo(_SPI_current->execCxt);
    2633             : }
    2634             : 
    2635             : static MemoryContext
    2636       88838 : _SPI_procmem(void)
    2637             : {
    2638       88838 :     return MemoryContextSwitchTo(_SPI_current->procCxt);
    2639             : }
    2640             : 
    2641             : /*
    2642             :  * _SPI_begin_call: begin a SPI operation within a connected procedure
    2643             :  *
    2644             :  * use_exec is true if we intend to make use of the procedure's execCxt
    2645             :  * during this SPI operation.  We'll switch into that context, and arrange
    2646             :  * for it to be cleaned up at _SPI_end_call or if an error occurs.
    2647             :  */
    2648             : static int
    2649      135070 : _SPI_begin_call(bool use_exec)
    2650             : {
    2651      135070 :     if (_SPI_current == NULL)
    2652           0 :         return SPI_ERROR_UNCONNECTED;
    2653             : 
    2654      135070 :     if (use_exec)
    2655             :     {
    2656             :         /* remember when the Executor operation started */
    2657       59844 :         _SPI_current->execSubid = GetCurrentSubTransactionId();
    2658             :         /* switch to the Executor memory context */
    2659       59844 :         _SPI_execmem();
    2660             :     }
    2661             : 
    2662      135070 :     return 0;
    2663             : }
    2664             : 
    2665             : /*
    2666             :  * _SPI_end_call: end a SPI operation within a connected procedure
    2667             :  *
    2668             :  * use_exec must be the same as in the previous _SPI_begin_call
    2669             :  *
    2670             :  * Note: this currently has no failure return cases, so callers don't check
    2671             :  */
    2672             : static int
    2673       59136 : _SPI_end_call(bool use_exec)
    2674             : {
    2675       59136 :     if (use_exec)
    2676             :     {
    2677             :         /* switch to the procedure memory context */
    2678       58744 :         _SPI_procmem();
    2679             :         /* mark Executor context no longer in use */
    2680       58744 :         _SPI_current->execSubid = InvalidSubTransactionId;
    2681             :         /* and free Executor memory */
    2682       58744 :         MemoryContextResetAndDeleteChildren(_SPI_current->execCxt);
    2683             :     }
    2684             : 
    2685       59136 :     return 0;
    2686             : }
    2687             : 
    2688             : static bool
    2689       29162 : _SPI_checktuples(void)
    2690             : {
    2691       29162 :     uint64      processed = _SPI_current->processed;
    2692       29162 :     SPITupleTable *tuptable = _SPI_current->tuptable;
    2693       29162 :     bool        failed = false;
    2694             : 
    2695       29162 :     if (tuptable == NULL)       /* spi_dest_startup was not called */
    2696           0 :         failed = true;
    2697       29162 :     else if (processed != tuptable->numvals)
    2698           0 :         failed = true;
    2699             : 
    2700       29162 :     return failed;
    2701             : }
    2702             : 
    2703             : /*
    2704             :  * Convert a "temporary" SPIPlan into an "unsaved" plan.
    2705             :  *
    2706             :  * The passed _SPI_plan struct is on the stack, and all its subsidiary data
    2707             :  * is in or under the current SPI executor context.  Copy the plan into the
    2708             :  * SPI procedure context so it will survive _SPI_end_call().  To minimize
    2709             :  * data copying, this destructively modifies the input plan, by taking the
    2710             :  * plancache entries away from it and reparenting them to the new SPIPlan.
    2711             :  */
    2712             : static SPIPlanPtr
    2713       13458 : _SPI_make_plan_non_temp(SPIPlanPtr plan)
    2714             : {
    2715             :     SPIPlanPtr  newplan;
    2716       13458 :     MemoryContext parentcxt = _SPI_current->procCxt;
    2717             :     MemoryContext plancxt;
    2718             :     MemoryContext oldcxt;
    2719             :     ListCell   *lc;
    2720             : 
    2721             :     /* Assert the input is a temporary SPIPlan */
    2722             :     Assert(plan->magic == _SPI_PLAN_MAGIC);
    2723             :     Assert(plan->plancxt == NULL);
    2724             :     /* One-shot plans can't be saved */
    2725             :     Assert(!plan->oneshot);
    2726             : 
    2727             :     /*
    2728             :      * Create a memory context for the plan, underneath the procedure context.
    2729             :      * We don't expect the plan to be very large.
    2730             :      */
    2731       13458 :     plancxt = AllocSetContextCreate(parentcxt,
    2732             :                                     "SPI Plan",
    2733             :                                     ALLOCSET_SMALL_SIZES);
    2734       13458 :     oldcxt = MemoryContextSwitchTo(plancxt);
    2735             : 
    2736             :     /* Copy the _SPI_plan struct and subsidiary data into the new context */
    2737       13458 :     newplan = (SPIPlanPtr) palloc0(sizeof(_SPI_plan));
    2738       13458 :     newplan->magic = _SPI_PLAN_MAGIC;
    2739       13458 :     newplan->plancxt = plancxt;
    2740       13458 :     newplan->cursor_options = plan->cursor_options;
    2741       13458 :     newplan->nargs = plan->nargs;
    2742       13458 :     if (plan->nargs > 0)
    2743             :     {
    2744        2364 :         newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
    2745        2364 :         memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
    2746             :     }
    2747             :     else
    2748       11094 :         newplan->argtypes = NULL;
    2749       13458 :     newplan->parserSetup = plan->parserSetup;
    2750       13458 :     newplan->parserSetupArg = plan->parserSetupArg;
    2751             : 
    2752             :     /*
    2753             :      * Reparent all the CachedPlanSources into the procedure context.  In
    2754             :      * theory this could fail partway through due to the pallocs, but we don't
    2755             :      * care too much since both the procedure context and the executor context
    2756             :      * would go away on error.
    2757             :      */
    2758       26916 :     foreach(lc, plan->plancache_list)
    2759             :     {
    2760       13458 :         CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
    2761             : 
    2762       13458 :         CachedPlanSetParentContext(plansource, parentcxt);
    2763             : 
    2764             :         /* Build new list, with list cells in plancxt */
    2765       13458 :         newplan->plancache_list = lappend(newplan->plancache_list, plansource);
    2766             :     }
    2767             : 
    2768       13458 :     MemoryContextSwitchTo(oldcxt);
    2769             : 
    2770             :     /* For safety, unlink the CachedPlanSources from the temporary plan */
    2771       13458 :     plan->plancache_list = NIL;
    2772             : 
    2773       13458 :     return newplan;
    2774             : }
    2775             : 
    2776             : /*
    2777             :  * Make a "saved" copy of the given plan.
    2778             :  */
    2779             : static SPIPlanPtr
    2780           0 : _SPI_save_plan(SPIPlanPtr plan)
    2781             : {
    2782             :     SPIPlanPtr  newplan;
    2783             :     MemoryContext plancxt;
    2784             :     MemoryContext oldcxt;
    2785             :     ListCell   *lc;
    2786             : 
    2787             :     /* One-shot plans can't be saved */
    2788             :     Assert(!plan->oneshot);
    2789             : 
    2790             :     /*
    2791             :      * Create a memory context for the plan.  We don't expect the plan to be
    2792             :      * very large, so use smaller-than-default alloc parameters.  It's a
    2793             :      * transient context until we finish copying everything.
    2794             :      */
    2795           0 :     plancxt = AllocSetContextCreate(CurrentMemoryContext,
    2796             :                                     "SPI Plan",
    2797             :                                     ALLOCSET_SMALL_SIZES);
    2798           0 :     oldcxt = MemoryContextSwitchTo(plancxt);
    2799             : 
    2800             :     /* Copy the SPI plan into its own context */
    2801           0 :     newplan = (SPIPlanPtr) palloc0(sizeof(_SPI_plan));
    2802           0 :     newplan->magic = _SPI_PLAN_MAGIC;
    2803           0 :     newplan->plancxt = plancxt;
    2804           0 :     newplan->cursor_options = plan->cursor_options;
    2805           0 :     newplan->nargs = plan->nargs;
    2806           0 :     if (plan->nargs > 0)
    2807             :     {
    2808           0 :         newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
    2809           0 :         memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
    2810             :     }
    2811             :     else
    2812           0 :         newplan->argtypes = NULL;
    2813           0 :     newplan->parserSetup = plan->parserSetup;
    2814           0 :     newplan->parserSetupArg = plan->parserSetupArg;
    2815             : 
    2816             :     /* Copy all the plancache entries */
    2817           0 :     foreach(lc, plan->plancache_list)
    2818             :     {
    2819           0 :         CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
    2820             :         CachedPlanSource *newsource;
    2821             : 
    2822           0 :         newsource = CopyCachedPlan(plansource);
    2823           0 :         newplan->plancache_list = lappend(newplan->plancache_list, newsource);
    2824             :     }
    2825             : 
    2826           0 :     MemoryContextSwitchTo(oldcxt);
    2827             : 
    2828             :     /*
    2829             :      * Mark it saved, reparent it under CacheMemoryContext, and mark all the
    2830             :      * component CachedPlanSources as saved.  This sequence cannot fail
    2831             :      * partway through, so there's no risk of long-term memory leakage.
    2832             :      */
    2833           0 :     newplan->saved = true;
    2834           0 :     MemoryContextSetParent(newplan->plancxt, CacheMemoryContext);
    2835             : 
    2836           0 :     foreach(lc, newplan->plancache_list)
    2837             :     {
    2838           0 :         CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
    2839             : 
    2840           0 :         SaveCachedPlan(plansource);
    2841             :     }
    2842             : 
    2843           0 :     return newplan;
    2844             : }
    2845             : 
    2846             : /*
    2847             :  * Internal lookup of ephemeral named relation by name.
    2848             :  */
    2849             : static EphemeralNamedRelation
    2850         392 : _SPI_find_ENR_by_name(const char *name)
    2851             : {
    2852             :     /* internal static function; any error is bug in SPI itself */
    2853             :     Assert(name != NULL);
    2854             : 
    2855             :     /* fast exit if no tuplestores have been added */
    2856         392 :     if (_SPI_current->queryEnv == NULL)
    2857         306 :         return NULL;
    2858             : 
    2859          86 :     return get_ENR(_SPI_current->queryEnv, name);
    2860             : }
    2861             : 
    2862             : /*
    2863             :  * Register an ephemeral named relation for use by the planner and executor on
    2864             :  * subsequent calls using this SPI connection.
    2865             :  */
    2866             : int
    2867         392 : SPI_register_relation(EphemeralNamedRelation enr)
    2868             : {
    2869             :     EphemeralNamedRelation match;
    2870             :     int         res;
    2871             : 
    2872         392 :     if (enr == NULL || enr->md.name == NULL)
    2873           0 :         return SPI_ERROR_ARGUMENT;
    2874             : 
    2875         392 :     res = _SPI_begin_call(false);   /* keep current memory context */
    2876         392 :     if (res < 0)
    2877           0 :         return res;
    2878             : 
    2879         392 :     match = _SPI_find_ENR_by_name(enr->md.name);
    2880         392 :     if (match)
    2881           0 :         res = SPI_ERROR_REL_DUPLICATE;
    2882             :     else
    2883             :     {
    2884         392 :         if (_SPI_current->queryEnv == NULL)
    2885         306 :             _SPI_current->queryEnv = create_queryEnv();
    2886             : 
    2887         392 :         register_ENR(_SPI_current->queryEnv, enr);
    2888         392 :         res = SPI_OK_REL_REGISTER;
    2889             :     }
    2890             : 
    2891         392 :     _SPI_end_call(false);
    2892             : 
    2893         392 :     return res;
    2894             : }
    2895             : 
    2896             : /*
    2897             :  * Unregister an ephemeral named relation by name.  This will probably be a
    2898             :  * rarely used function, since SPI_finish will clear it automatically.
    2899             :  */
    2900             : int
    2901           0 : SPI_unregister_relation(const char *name)
    2902             : {
    2903             :     EphemeralNamedRelation match;
    2904             :     int         res;
    2905             : 
    2906           0 :     if (name == NULL)
    2907           0 :         return SPI_ERROR_ARGUMENT;
    2908             : 
    2909           0 :     res = _SPI_begin_call(false);   /* keep current memory context */
    2910           0 :     if (res < 0)
    2911           0 :         return res;
    2912             : 
    2913           0 :     match = _SPI_find_ENR_by_name(name);
    2914           0 :     if (match)
    2915             :     {
    2916           0 :         unregister_ENR(_SPI_current->queryEnv, match->md.name);
    2917           0 :         res = SPI_OK_REL_UNREGISTER;
    2918             :     }
    2919             :     else
    2920           0 :         res = SPI_ERROR_REL_NOT_FOUND;
    2921             : 
    2922           0 :     _SPI_end_call(false);
    2923             : 
    2924           0 :     return res;
    2925             : }
    2926             : 
    2927             : /*
    2928             :  * Register the transient relations from 'tdata' using this SPI connection.
    2929             :  * This should be called by PL implementations' trigger handlers after
    2930             :  * connecting, in order to make transition tables visible to any queries run
    2931             :  * in this connection.
    2932             :  */
    2933             : int
    2934        9422 : SPI_register_trigger_data(TriggerData *tdata)
    2935             : {
    2936        9422 :     if (tdata == NULL)
    2937           0 :         return SPI_ERROR_ARGUMENT;
    2938             : 
    2939        9422 :     if (tdata->tg_newtable)
    2940             :     {
    2941         222 :         EphemeralNamedRelation enr =
    2942             :         palloc(sizeof(EphemeralNamedRelationData));
    2943             :         int         rc;
    2944             : 
    2945         222 :         enr->md.name = tdata->tg_trigger->tgnewtable;
    2946         222 :         enr->md.reliddesc = tdata->tg_relation->rd_id;
    2947         222 :         enr->md.tupdesc = NULL;
    2948         222 :         enr->md.enrtype = ENR_NAMED_TUPLESTORE;
    2949         222 :         enr->md.enrtuples = tuplestore_tuple_count(tdata->tg_newtable);
    2950         222 :         enr->reldata = tdata->tg_newtable;
    2951         222 :         rc = SPI_register_relation(enr);
    2952         222 :         if (rc != SPI_OK_REL_REGISTER)
    2953           0 :             return rc;
    2954             :     }
    2955             : 
    2956        9422 :     if (tdata->tg_oldtable)
    2957             :     {
    2958         170 :         EphemeralNamedRelation enr =
    2959             :         palloc(sizeof(EphemeralNamedRelationData));
    2960             :         int         rc;
    2961             : 
    2962         170 :         enr->md.name = tdata->tg_trigger->tgoldtable;
    2963         170 :         enr->md.reliddesc = tdata->tg_relation->rd_id;
    2964         170 :         enr->md.tupdesc = NULL;
    2965         170 :         enr->md.enrtype = ENR_NAMED_TUPLESTORE;
    2966         170 :         enr->md.enrtuples = tuplestore_tuple_count(tdata->tg_oldtable);
    2967         170 :         enr->reldata = tdata->tg_oldtable;
    2968         170 :         rc = SPI_register_relation(enr);
    2969         170 :         if (rc != SPI_OK_REL_REGISTER)
    2970           0 :             return rc;
    2971             :     }
    2972             : 
    2973        9422 :     return SPI_OK_TD_REGISTER;
    2974             : }

Generated by: LCOV version 1.13