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

Generated by: LCOV version 1.11