]> granicus.if.org Git - postgresql/blob - src/backend/executor/spi.c
Replace nested-BEGIN syntax for subtransactions with spec-compliant
[postgresql] / src / backend / executor / spi.c
1 /*-------------------------------------------------------------------------
2  *
3  * spi.c
4  *                              Server Programming Interface
5  *
6  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.121 2004/07/27 05:10:51 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include "access/printtup.h"
18 #include "catalog/heap.h"
19 #include "executor/spi_priv.h"
20 #include "tcop/tcopprot.h"
21 #include "utils/lsyscache.h"
22 #include "utils/typcache.h"
23
24
25 uint32          SPI_processed = 0;
26 Oid                     SPI_lastoid = InvalidOid;
27 SPITupleTable *SPI_tuptable = NULL;
28 int                     SPI_result;
29
30 static _SPI_connection *_SPI_stack = NULL;
31 static _SPI_connection *_SPI_current = NULL;
32 static int      _SPI_stack_depth = 0; /* allocated size of _SPI_stack */
33 static int      _SPI_connected = -1;
34 static int      _SPI_curid = -1;
35
36 static int      _SPI_execute(const char *src, int tcount, _SPI_plan *plan);
37 static int      _SPI_pquery(QueryDesc *queryDesc, bool runit,
38                                                 bool useCurrentSnapshot, int tcount);
39
40 static int _SPI_execute_plan(_SPI_plan *plan,
41                                                          Datum *Values, const char *Nulls,
42                                                          bool useCurrentSnapshot, int tcount);
43
44 static void _SPI_error_callback(void *arg);
45
46 static void _SPI_cursor_operation(Portal portal, bool forward, int count,
47                                           DestReceiver *dest);
48
49 static _SPI_plan *_SPI_copy_plan(_SPI_plan *plan, int location);
50
51 static int      _SPI_begin_call(bool execmem);
52 static int      _SPI_end_call(bool procmem);
53 static MemoryContext _SPI_execmem(void);
54 static MemoryContext _SPI_procmem(void);
55 static bool _SPI_checktuples(void);
56
57
58 /* =================== interface functions =================== */
59
60 int
61 SPI_connect(void)
62 {
63         int             newdepth;
64
65         /*
66          * When procedure called by Executor _SPI_curid expected to be equal
67          * to _SPI_connected
68          */
69         if (_SPI_curid != _SPI_connected)
70                 return SPI_ERROR_CONNECT;
71
72         if (_SPI_stack == NULL)
73         {
74                 if (_SPI_connected != -1 || _SPI_stack_depth != 0)
75                         elog(ERROR, "SPI stack corrupted");
76                 newdepth = 16;
77                 _SPI_stack = (_SPI_connection *)
78                         MemoryContextAlloc(TopTransactionContext,
79                                                            newdepth * sizeof(_SPI_connection));
80                 _SPI_stack_depth = newdepth;
81         }
82         else
83         {
84                 if (_SPI_stack_depth <= 0 || _SPI_stack_depth <= _SPI_connected)
85                         elog(ERROR, "SPI stack corrupted");
86                 if (_SPI_stack_depth == _SPI_connected + 1)
87                 {
88                         newdepth = _SPI_stack_depth * 2;
89                         _SPI_stack = (_SPI_connection *)
90                                 repalloc(_SPI_stack,
91                                                  newdepth * sizeof(_SPI_connection));
92                         _SPI_stack_depth = newdepth;
93                 }
94         }
95
96         /*
97          * We're entering procedure where _SPI_curid == _SPI_connected - 1
98          */
99         _SPI_connected++;
100         Assert(_SPI_connected >= 0 && _SPI_connected < _SPI_stack_depth);
101
102         _SPI_current = &(_SPI_stack[_SPI_connected]);
103         _SPI_current->processed = 0;
104         _SPI_current->tuptable = NULL;
105         _SPI_current->connectXid = GetCurrentTransactionId();
106
107         /*
108          * Create memory contexts for this procedure
109          *
110          * XXX it would be better to use PortalContext as the parent context,
111          * but we may not be inside a portal (consider deferred-trigger
112          * execution).  Perhaps CurTransactionContext would do?  For now it
113          * doesn't matter because we clean up explicitly in AtEOSubXact_SPI().
114          */
115         _SPI_current->procCxt = AllocSetContextCreate(TopTransactionContext,
116                                                                                                   "SPI Proc",
117                                                                                                 ALLOCSET_DEFAULT_MINSIZE,
118                                                                                            ALLOCSET_DEFAULT_INITSIZE,
119                                                                                            ALLOCSET_DEFAULT_MAXSIZE);
120         _SPI_current->execCxt = AllocSetContextCreate(TopTransactionContext,
121                                                                                                   "SPI Exec",
122                                                                                                 ALLOCSET_DEFAULT_MINSIZE,
123                                                                                            ALLOCSET_DEFAULT_INITSIZE,
124                                                                                            ALLOCSET_DEFAULT_MAXSIZE);
125         /* ... and switch to procedure's context */
126         _SPI_current->savedcxt = MemoryContextSwitchTo(_SPI_current->procCxt);
127
128         return SPI_OK_CONNECT;
129 }
130
131 int
132 SPI_finish(void)
133 {
134         int                     res;
135
136         res = _SPI_begin_call(false);           /* live in procedure memory */
137         if (res < 0)
138                 return res;
139
140         /* Restore memory context as it was before procedure call */
141         MemoryContextSwitchTo(_SPI_current->savedcxt);
142
143         /* Release memory used in procedure call */
144         MemoryContextDelete(_SPI_current->execCxt);
145         MemoryContextDelete(_SPI_current->procCxt);
146
147         /*
148          * Reset result variables, especially SPI_tuptable which is probably
149          * pointing at a just-deleted tuptable
150          */
151         SPI_processed = 0;
152         SPI_lastoid = InvalidOid;
153         SPI_tuptable = NULL;
154
155         /*
156          * After _SPI_begin_call _SPI_connected == _SPI_curid. Now we are
157          * closing connection to SPI and returning to upper Executor and so
158          * _SPI_connected must be equal to _SPI_curid.
159          */
160         _SPI_connected--;
161         _SPI_curid--;
162         if (_SPI_connected == -1)
163                 _SPI_current = NULL;
164         else
165                 _SPI_current = &(_SPI_stack[_SPI_connected]);
166
167         return SPI_OK_FINISH;
168 }
169
170 /*
171  * Clean up SPI state at transaction commit or abort.
172  */
173 void
174 AtEOXact_SPI(bool isCommit)
175 {
176         /*
177          * Note that memory contexts belonging to SPI stack entries will be
178          * freed automatically, so we can ignore them here.  We just need to
179          * restore our static variables to initial state.
180          */
181         if (isCommit && _SPI_connected != -1)
182                 ereport(WARNING,
183                                 (errcode(ERRCODE_WARNING),
184                                  errmsg("transaction left non-empty SPI stack"),
185                                  errhint("Check for missing \"SPI_finish\" calls")));
186
187         _SPI_current = _SPI_stack = NULL;
188         _SPI_stack_depth = 0;
189         _SPI_connected = _SPI_curid = -1;
190         SPI_processed = 0;
191         SPI_lastoid = InvalidOid;
192         SPI_tuptable = NULL;
193 }
194
195 /*
196  * Clean up SPI state at subtransaction commit or abort.
197  *
198  * During commit, there shouldn't be any unclosed entries remaining from
199  * the current transaction; we throw them away if found.
200  */
201 void
202 AtEOSubXact_SPI(bool isCommit, TransactionId childXid)
203 {
204         bool    found = false;
205
206         while (_SPI_connected >= 0)
207         {
208                 _SPI_connection *connection = &(_SPI_stack[_SPI_connected]);
209
210                 if (connection->connectXid != childXid)
211                         break;                          /* couldn't be any underneath it either */
212
213                 found = true;
214
215                 /*
216                  * Pop the stack entry and reset global variables.  Unlike
217                  * SPI_finish(), we don't risk switching to memory contexts that
218                  * might be already gone, or deleting memory contexts that have been
219                  * or will be thrown away anyway.
220                  */
221                 _SPI_connected--;
222                 _SPI_curid = _SPI_connected;
223                 if (_SPI_connected == -1)
224                         _SPI_current = NULL;
225                 else
226                         _SPI_current = &(_SPI_stack[_SPI_connected]);
227                 SPI_processed = 0;
228                 SPI_lastoid = InvalidOid;
229                 SPI_tuptable = NULL;
230         }
231
232         if (found && isCommit)
233                 ereport(WARNING,
234                                 (errcode(ERRCODE_WARNING),
235                                  errmsg("subtransaction left non-empty SPI stack"),
236                                  errhint("Check for missing \"SPI_finish\" calls")));
237 }
238
239
240 /* Pushes SPI stack to allow recursive SPI calls */
241 void
242 SPI_push(void)
243 {
244         _SPI_curid++;
245 }
246
247 /* Pops SPI stack to allow recursive SPI calls */
248 void
249 SPI_pop(void)
250 {
251         _SPI_curid--;
252 }
253
254 int
255 SPI_exec(const char *src, int tcount)
256 {
257         int                     res;
258
259         if (src == NULL || tcount < 0)
260                 return SPI_ERROR_ARGUMENT;
261
262         res = _SPI_begin_call(true);
263         if (res < 0)
264                 return res;
265
266         res = _SPI_execute(src, tcount, NULL);
267
268         _SPI_end_call(true);
269         return res;
270 }
271
272 int
273 SPI_execp(void *plan, Datum *Values, const char *Nulls, int tcount)
274 {
275         int                     res;
276
277         if (plan == NULL || tcount < 0)
278                 return SPI_ERROR_ARGUMENT;
279
280         if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL)
281                 return SPI_ERROR_PARAM;
282
283         res = _SPI_begin_call(true);
284         if (res < 0)
285                 return res;
286
287         res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, false, tcount);
288
289         _SPI_end_call(true);
290         return res;
291 }
292
293 /*
294  * SPI_execp_current -- identical to SPI_execp, except that we expose the
295  * Executor option to use a current snapshot instead of the normal
296  * QuerySnapshot.  This is currently not documented in spi.sgml because
297  * it is only intended for use by RI triggers.
298  */
299 int
300 SPI_execp_current(void *plan, Datum *Values, const char *Nulls,
301                                   bool useCurrentSnapshot, int tcount)
302 {
303         int                     res;
304
305         if (plan == NULL || tcount < 0)
306                 return SPI_ERROR_ARGUMENT;
307
308         if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL)
309                 return SPI_ERROR_PARAM;
310
311         res = _SPI_begin_call(true);
312         if (res < 0)
313                 return res;
314
315         res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls,
316                                                         useCurrentSnapshot, tcount);
317
318         _SPI_end_call(true);
319         return res;
320 }
321
322 void *
323 SPI_prepare(const char *src, int nargs, Oid *argtypes)
324 {
325         _SPI_plan       plan;
326         _SPI_plan  *result;
327
328         if (src == NULL || nargs < 0 || (nargs > 0 && argtypes == NULL))
329         {
330                 SPI_result = SPI_ERROR_ARGUMENT;
331                 return NULL;
332         }
333
334         SPI_result = _SPI_begin_call(true);
335         if (SPI_result < 0)
336                 return NULL;
337
338         plan.plancxt = NULL;            /* doesn't have own context */
339         plan.query = src;
340         plan.nargs = nargs;
341         plan.argtypes = argtypes;
342
343         SPI_result = _SPI_execute(src, 0, &plan);
344
345         if (SPI_result >= 0)            /* copy plan to procedure context */
346                 result = _SPI_copy_plan(&plan, _SPI_CPLAN_PROCXT);
347         else
348                 result = NULL;
349
350         _SPI_end_call(true);
351
352         return (void *) result;
353 }
354
355 void *
356 SPI_saveplan(void *plan)
357 {
358         _SPI_plan  *newplan;
359
360         if (plan == NULL)
361         {
362                 SPI_result = SPI_ERROR_ARGUMENT;
363                 return NULL;
364         }
365
366         SPI_result = _SPI_begin_call(false);            /* don't change context */
367         if (SPI_result < 0)
368                 return NULL;
369
370         newplan = _SPI_copy_plan((_SPI_plan *) plan, _SPI_CPLAN_TOPCXT);
371
372         _SPI_curid--;
373         SPI_result = 0;
374
375         return (void *) newplan;
376 }
377
378 int
379 SPI_freeplan(void *plan)
380 {
381         _SPI_plan  *spiplan = (_SPI_plan *) plan;
382
383         if (plan == NULL)
384                 return SPI_ERROR_ARGUMENT;
385
386         MemoryContextDelete(spiplan->plancxt);
387         return 0;
388 }
389
390 HeapTuple
391 SPI_copytuple(HeapTuple tuple)
392 {
393         MemoryContext oldcxt = NULL;
394         HeapTuple       ctuple;
395
396         if (tuple == NULL)
397         {
398                 SPI_result = SPI_ERROR_ARGUMENT;
399                 return NULL;
400         }
401
402         if (_SPI_curid + 1 == _SPI_connected)           /* connected */
403         {
404                 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
405                         elog(ERROR, "SPI stack corrupted");
406                 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
407         }
408
409         ctuple = heap_copytuple(tuple);
410
411         if (oldcxt)
412                 MemoryContextSwitchTo(oldcxt);
413
414         return ctuple;
415 }
416
417 HeapTupleHeader
418 SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
419 {
420         MemoryContext oldcxt = NULL;
421         HeapTupleHeader dtup;
422
423         if (tuple == NULL || tupdesc == NULL)
424         {
425                 SPI_result = SPI_ERROR_ARGUMENT;
426                 return NULL;
427         }
428
429         /* For RECORD results, make sure a typmod has been assigned */
430         if (tupdesc->tdtypeid == RECORDOID &&
431                 tupdesc->tdtypmod < 0)
432                 assign_record_type_typmod(tupdesc);
433
434         if (_SPI_curid + 1 == _SPI_connected)           /* connected */
435         {
436                 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
437                         elog(ERROR, "SPI stack corrupted");
438                 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
439         }
440
441         dtup = (HeapTupleHeader) palloc(tuple->t_len);
442         memcpy((char *) dtup, (char *) tuple->t_data, tuple->t_len);
443
444         HeapTupleHeaderSetDatumLength(dtup, tuple->t_len);
445         HeapTupleHeaderSetTypeId(dtup, tupdesc->tdtypeid);
446         HeapTupleHeaderSetTypMod(dtup, tupdesc->tdtypmod);
447
448         if (oldcxt)
449                 MemoryContextSwitchTo(oldcxt);
450
451         return dtup;
452 }
453
454 HeapTuple
455 SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
456                                 Datum *Values, const char *Nulls)
457 {
458         MemoryContext oldcxt = NULL;
459         HeapTuple       mtuple;
460         int                     numberOfAttributes;
461         Datum      *v;
462         char       *n;
463         int                     i;
464
465         if (rel == NULL || tuple == NULL || natts < 0 || attnum == NULL || Values == NULL)
466         {
467                 SPI_result = SPI_ERROR_ARGUMENT;
468                 return NULL;
469         }
470
471         if (_SPI_curid + 1 == _SPI_connected)           /* connected */
472         {
473                 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
474                         elog(ERROR, "SPI stack corrupted");
475                 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
476         }
477         SPI_result = 0;
478         numberOfAttributes = rel->rd_att->natts;
479         v = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
480         n = (char *) palloc(numberOfAttributes * sizeof(char));
481
482         /* fetch old values and nulls */
483         heap_deformtuple(tuple, rel->rd_att, v, n);
484
485         /* replace values and nulls */
486         for (i = 0; i < natts; i++)
487         {
488                 if (attnum[i] <= 0 || attnum[i] > numberOfAttributes)
489                         break;
490                 v[attnum[i] - 1] = Values[i];
491                 n[attnum[i] - 1] = (Nulls && Nulls[i] == 'n') ? 'n' : ' ';
492         }
493
494         if (i == natts)                         /* no errors in *attnum */
495         {
496                 mtuple = heap_formtuple(rel->rd_att, v, n);
497
498                 /*
499                  * copy the identification info of the old tuple: t_ctid, t_self,
500                  * and OID (if any)
501                  */
502                 mtuple->t_data->t_ctid = tuple->t_data->t_ctid;
503                 mtuple->t_self = tuple->t_self;
504                 mtuple->t_tableOid = tuple->t_tableOid;
505                 if (rel->rd_att->tdhasoid)
506                         HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple));
507         }
508         else
509         {
510                 mtuple = NULL;
511                 SPI_result = SPI_ERROR_NOATTRIBUTE;
512         }
513
514         pfree(v);
515         pfree(n);
516
517         if (oldcxt)
518                 MemoryContextSwitchTo(oldcxt);
519
520         return mtuple;
521 }
522
523 int
524 SPI_fnumber(TupleDesc tupdesc, const char *fname)
525 {
526         int                     res;
527         Form_pg_attribute sysatt;
528
529         for (res = 0; res < tupdesc->natts; res++)
530         {
531                 if (namestrcmp(&tupdesc->attrs[res]->attname, fname) == 0)
532                         return res + 1;
533         }
534
535         sysatt = SystemAttributeByName(fname, true /* "oid" will be accepted */ );
536         if (sysatt != NULL)
537                 return sysatt->attnum;
538
539         /* SPI_ERROR_NOATTRIBUTE is different from all sys column numbers */
540         return SPI_ERROR_NOATTRIBUTE;
541 }
542
543 char *
544 SPI_fname(TupleDesc tupdesc, int fnumber)
545 {
546         Form_pg_attribute att;
547
548         SPI_result = 0;
549
550         if (fnumber > tupdesc->natts || fnumber == 0 ||
551                 fnumber <= FirstLowInvalidHeapAttributeNumber)
552         {
553                 SPI_result = SPI_ERROR_NOATTRIBUTE;
554                 return NULL;
555         }
556
557         if (fnumber > 0)
558                 att = tupdesc->attrs[fnumber - 1];
559         else
560                 att = SystemAttributeDefinition(fnumber, true);
561
562         return pstrdup(NameStr(att->attname));
563 }
564
565 char *
566 SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
567 {
568         Datum           origval,
569                                 val,
570                                 result;
571         bool            isnull;
572         Oid                     typoid,
573                                 foutoid,
574                                 typioparam;
575         int32           typmod;
576         bool            typisvarlena;
577
578         SPI_result = 0;
579
580         if (fnumber > tuple->t_data->t_natts || fnumber == 0 ||
581                 fnumber <= FirstLowInvalidHeapAttributeNumber)
582         {
583                 SPI_result = SPI_ERROR_NOATTRIBUTE;
584                 return NULL;
585         }
586
587         origval = heap_getattr(tuple, fnumber, tupdesc, &isnull);
588         if (isnull)
589                 return NULL;
590
591         if (fnumber > 0)
592         {
593                 typoid = tupdesc->attrs[fnumber - 1]->atttypid;
594                 typmod = tupdesc->attrs[fnumber - 1]->atttypmod;
595         }
596         else
597         {
598                 typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
599                 typmod = -1;
600         }
601
602         getTypeOutputInfo(typoid, &foutoid, &typioparam, &typisvarlena);
603
604         /*
605          * If we have a toasted datum, forcibly detoast it here to avoid
606          * memory leakage inside the type's output routine.
607          */
608         if (typisvarlena)
609                 val = PointerGetDatum(PG_DETOAST_DATUM(origval));
610         else
611                 val = origval;
612
613         result = OidFunctionCall3(foutoid,
614                                                           val,
615                                                           ObjectIdGetDatum(typioparam),
616                                                           Int32GetDatum(typmod));
617
618         /* Clean up detoasted copy, if any */
619         if (val != origval)
620                 pfree(DatumGetPointer(val));
621
622         return DatumGetCString(result);
623 }
624
625 Datum
626 SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
627 {
628         SPI_result = 0;
629
630         if (fnumber > tuple->t_data->t_natts || fnumber == 0 ||
631                 fnumber <= FirstLowInvalidHeapAttributeNumber)
632         {
633                 SPI_result = SPI_ERROR_NOATTRIBUTE;
634                 *isnull = true;
635                 return (Datum) NULL;
636         }
637
638         return heap_getattr(tuple, fnumber, tupdesc, isnull);
639 }
640
641 char *
642 SPI_gettype(TupleDesc tupdesc, int fnumber)
643 {
644         Oid                     typoid;
645         HeapTuple       typeTuple;
646         char       *result;
647
648         SPI_result = 0;
649
650         if (fnumber > tupdesc->natts || fnumber == 0 ||
651                 fnumber <= FirstLowInvalidHeapAttributeNumber)
652         {
653                 SPI_result = SPI_ERROR_NOATTRIBUTE;
654                 return NULL;
655         }
656
657         if (fnumber > 0)
658                 typoid = tupdesc->attrs[fnumber - 1]->atttypid;
659         else
660                 typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
661
662         typeTuple = SearchSysCache(TYPEOID,
663                                                            ObjectIdGetDatum(typoid),
664                                                            0, 0, 0);
665
666         if (!HeapTupleIsValid(typeTuple))
667         {
668                 SPI_result = SPI_ERROR_TYPUNKNOWN;
669                 return NULL;
670         }
671
672         result = pstrdup(NameStr(((Form_pg_type) GETSTRUCT(typeTuple))->typname));
673         ReleaseSysCache(typeTuple);
674         return result;
675 }
676
677 Oid
678 SPI_gettypeid(TupleDesc tupdesc, int fnumber)
679 {
680         SPI_result = 0;
681
682         if (fnumber > tupdesc->natts || fnumber == 0 ||
683                 fnumber <= FirstLowInvalidHeapAttributeNumber)
684         {
685                 SPI_result = SPI_ERROR_NOATTRIBUTE;
686                 return InvalidOid;
687         }
688
689         if (fnumber > 0)
690                 return tupdesc->attrs[fnumber - 1]->atttypid;
691         else
692                 return (SystemAttributeDefinition(fnumber, true))->atttypid;
693 }
694
695 char *
696 SPI_getrelname(Relation rel)
697 {
698         return pstrdup(RelationGetRelationName(rel));
699 }
700
701 void *
702 SPI_palloc(Size size)
703 {
704         MemoryContext oldcxt = NULL;
705         void       *pointer;
706
707         if (_SPI_curid + 1 == _SPI_connected)           /* connected */
708         {
709                 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
710                         elog(ERROR, "SPI stack corrupted");
711                 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
712         }
713
714         pointer = palloc(size);
715
716         if (oldcxt)
717                 MemoryContextSwitchTo(oldcxt);
718
719         return pointer;
720 }
721
722 void *
723 SPI_repalloc(void *pointer, Size size)
724 {
725         /* No longer need to worry which context chunk was in... */
726         return repalloc(pointer, size);
727 }
728
729 void
730 SPI_pfree(void *pointer)
731 {
732         /* No longer need to worry which context chunk was in... */
733         pfree(pointer);
734 }
735
736 void
737 SPI_freetuple(HeapTuple tuple)
738 {
739         /* No longer need to worry which context tuple was in... */
740         heap_freetuple(tuple);
741 }
742
743 void
744 SPI_freetuptable(SPITupleTable *tuptable)
745 {
746         if (tuptable != NULL)
747                 MemoryContextDelete(tuptable->tuptabcxt);
748 }
749
750
751
752 /*
753  * SPI_cursor_open()
754  *
755  *      Open a prepared SPI plan as a portal
756  */
757 Portal
758 SPI_cursor_open(const char *name, void *plan, Datum *Values, const char *Nulls)
759 {
760         _SPI_plan  *spiplan = (_SPI_plan *) plan;
761         List       *qtlist = spiplan->qtlist;
762         List       *ptlist = spiplan->ptlist;
763         Query      *queryTree;
764         Plan       *planTree;
765         ParamListInfo paramLI;
766         MemoryContext oldcontext;
767         Portal          portal;
768         int                     k;
769
770         /* Ensure that the plan contains only one regular SELECT query */
771         if (list_length(ptlist) != 1 || list_length(qtlist) != 1)
772                 ereport(ERROR,
773                                 (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
774                                  errmsg("cannot open multi-query plan as cursor")));
775         queryTree = (Query *) linitial((List *) linitial(qtlist));
776         planTree = (Plan *) linitial(ptlist);
777
778         if (queryTree->commandType != CMD_SELECT)
779                 ereport(ERROR,
780                                 (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
781                                  errmsg("cannot open non-SELECT query as cursor")));
782         if (queryTree->into != NULL)
783                 ereport(ERROR,
784                                 (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
785                                  errmsg("cannot open SELECT INTO query as cursor")));
786
787         /* Increment CommandCounter to see changes made by now */
788         CommandCounterIncrement();
789
790         /* Reset SPI result */
791         SPI_processed = 0;
792         SPI_tuptable = NULL;
793         _SPI_current->processed = 0;
794         _SPI_current->tuptable = NULL;
795
796         /* Create the portal */
797         if (name == NULL || name[0] == '\0')
798         {
799                 /* Use a random nonconflicting name */
800                 portal = CreateNewPortal();
801         }
802         else
803         {
804                 /* In this path, error if portal of same name already exists */
805                 portal = CreatePortal(name, false, false);
806         }
807
808         /* Switch to portals memory and copy the parsetree and plan to there */
809         oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
810         queryTree = copyObject(queryTree);
811         planTree = copyObject(planTree);
812
813         /* If the plan has parameters, set them up */
814         if (spiplan->nargs > 0)
815         {
816                 paramLI = (ParamListInfo) palloc0((spiplan->nargs + 1) *
817                                                                                   sizeof(ParamListInfoData));
818
819                 for (k = 0; k < spiplan->nargs; k++)
820                 {
821                         paramLI[k].kind = PARAM_NUM;
822                         paramLI[k].id = k + 1;
823                         paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
824                         if (paramLI[k].isnull)
825                         {
826                                 /* nulls just copy */
827                                 paramLI[k].value = Values[k];
828                         }
829                         else
830                         {
831                                 /* pass-by-ref values must be copied into portal context */
832                                 int16           paramTypLen;
833                                 bool            paramTypByVal;
834
835                                 get_typlenbyval(spiplan->argtypes[k],
836                                                                 &paramTypLen, &paramTypByVal);
837                                 paramLI[k].value = datumCopy(Values[k],
838                                                                                          paramTypByVal, paramTypLen);
839                         }
840                 }
841                 paramLI[k].kind = PARAM_INVALID;
842         }
843         else
844                 paramLI = NULL;
845
846         /*
847          * Set up the portal.
848          */
849         PortalDefineQuery(portal,
850                                           NULL,         /* unfortunately don't have sourceText */
851                                           "SELECT", /* cursor's query is always a SELECT */
852                                           list_make1(queryTree),
853                                           list_make1(planTree),
854                                           PortalGetHeapMemory(portal));
855
856         MemoryContextSwitchTo(oldcontext);
857
858         /*
859          * Set up options for portal.
860          */
861         portal->cursorOptions &= ~(CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL);
862         if (ExecSupportsBackwardScan(plan))
863                 portal->cursorOptions |= CURSOR_OPT_SCROLL;
864         else
865                 portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
866
867         /*
868          * Start portal execution.
869          */
870         PortalStart(portal, paramLI);
871
872         Assert(portal->strategy == PORTAL_ONE_SELECT);
873
874         /* Return the created portal */
875         return portal;
876 }
877
878
879 /*
880  * SPI_cursor_find()
881  *
882  *      Find the portal of an existing open cursor
883  */
884 Portal
885 SPI_cursor_find(const char *name)
886 {
887         return GetPortalByName(name);
888 }
889
890
891 /*
892  * SPI_cursor_fetch()
893  *
894  *      Fetch rows in a cursor
895  */
896 void
897 SPI_cursor_fetch(Portal portal, bool forward, int count)
898 {
899         _SPI_cursor_operation(portal, forward, count,
900                                                   CreateDestReceiver(SPI, NULL));
901         /* we know that the SPI receiver doesn't need a destroy call */
902 }
903
904
905 /*
906  * SPI_cursor_move()
907  *
908  *      Move in a cursor
909  */
910 void
911 SPI_cursor_move(Portal portal, bool forward, int count)
912 {
913         _SPI_cursor_operation(portal, forward, count, None_Receiver);
914 }
915
916
917 /*
918  * SPI_cursor_close()
919  *
920  *      Close a cursor
921  */
922 void
923 SPI_cursor_close(Portal portal)
924 {
925         if (!PortalIsValid(portal))
926                 elog(ERROR, "invalid portal in SPI cursor operation");
927
928         PortalDrop(portal, false);
929 }
930
931 /*
932  * Returns the Oid representing the type id for argument at argIndex. First
933  * parameter is at index zero.
934  */
935 Oid
936 SPI_getargtypeid(void *plan, int argIndex)
937 {
938         if (plan == NULL || argIndex < 0 || argIndex >= ((_SPI_plan*)plan)->nargs)
939         {
940                 SPI_result = SPI_ERROR_ARGUMENT;
941                 return InvalidOid;
942         }
943         return ((_SPI_plan *) plan)->argtypes[argIndex];
944 }
945
946 /*
947  * Returns the number of arguments for the prepared plan.
948  */
949 int
950 SPI_getargcount(void *plan)
951 {
952         if (plan == NULL)
953         {
954                 SPI_result = SPI_ERROR_ARGUMENT;
955                 return -1;
956         }
957         return ((_SPI_plan *) plan)->nargs;
958 }
959
960 /*
961  * Returns true if the plan contains exactly one command
962  * and that command originates from normal SELECT (i.e.
963  * *not* a SELECT ... INTO). In essence, the result indicates
964  * if the command can be used with SPI_cursor_open
965  *
966  * Parameters
967  *    plan A plan previously prepared using SPI_prepare
968  */
969 bool
970 SPI_is_cursor_plan(void *plan)
971 {
972         _SPI_plan *spiplan = (_SPI_plan *) plan;
973         List *qtlist;
974
975         if (spiplan == NULL)
976         {
977                 SPI_result = SPI_ERROR_ARGUMENT;
978                 return false;
979         }
980
981         qtlist = spiplan->qtlist;
982         if (list_length(spiplan->ptlist) == 1 && list_length(qtlist) == 1)
983         {
984                 Query *queryTree = (Query *) linitial((List *) linitial(qtlist));
985
986                 if (queryTree->commandType == CMD_SELECT && queryTree->into == NULL)
987                         return true;
988         }
989         return false;
990 }
991
992 /* =================== private functions =================== */
993
994 /*
995  * spi_dest_startup
996  *              Initialize to receive tuples from Executor into SPITupleTable
997  *              of current SPI procedure
998  */
999 void
1000 spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
1001 {
1002         SPITupleTable *tuptable;
1003         MemoryContext oldcxt;
1004         MemoryContext tuptabcxt;
1005
1006         /*
1007          * When called by Executor _SPI_curid expected to be equal to
1008          * _SPI_connected
1009          */
1010         if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
1011                 elog(ERROR, "improper call to spi_dest_startup");
1012         if (_SPI_current != &(_SPI_stack[_SPI_curid]))
1013                 elog(ERROR, "SPI stack corrupted");
1014
1015         if (_SPI_current->tuptable != NULL)
1016                 elog(ERROR, "improper call to spi_dest_startup");
1017
1018         oldcxt = _SPI_procmem();        /* switch to procedure memory context */
1019
1020         tuptabcxt = AllocSetContextCreate(CurrentMemoryContext,
1021                                                                           "SPI TupTable",
1022                                                                           ALLOCSET_DEFAULT_MINSIZE,
1023                                                                           ALLOCSET_DEFAULT_INITSIZE,
1024                                                                           ALLOCSET_DEFAULT_MAXSIZE);
1025         MemoryContextSwitchTo(tuptabcxt);
1026
1027         _SPI_current->tuptable = tuptable = (SPITupleTable *)
1028                 palloc(sizeof(SPITupleTable));
1029         tuptable->tuptabcxt = tuptabcxt;
1030         tuptable->alloced = tuptable->free = 128;
1031         tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
1032         tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
1033
1034         MemoryContextSwitchTo(oldcxt);
1035 }
1036
1037 /*
1038  * spi_printtup
1039  *              store tuple retrieved by Executor into SPITupleTable
1040  *              of current SPI procedure
1041  */
1042 void
1043 spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
1044 {
1045         SPITupleTable *tuptable;
1046         MemoryContext oldcxt;
1047
1048         /*
1049          * When called by Executor _SPI_curid expected to be equal to
1050          * _SPI_connected
1051          */
1052         if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
1053                 elog(ERROR, "improper call to spi_printtup");
1054         if (_SPI_current != &(_SPI_stack[_SPI_curid]))
1055                 elog(ERROR, "SPI stack corrupted");
1056
1057         tuptable = _SPI_current->tuptable;
1058         if (tuptable == NULL)
1059                 elog(ERROR, "improper call to spi_printtup");
1060
1061         oldcxt = MemoryContextSwitchTo(tuptable->tuptabcxt);
1062
1063         if (tuptable->free == 0)
1064         {
1065                 tuptable->free = 256;
1066                 tuptable->alloced += tuptable->free;
1067                 tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
1068                                                                   tuptable->alloced * sizeof(HeapTuple));
1069         }
1070
1071         tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple);
1072         (tuptable->free)--;
1073
1074         MemoryContextSwitchTo(oldcxt);
1075 }
1076
1077 /*
1078  * Static functions
1079  */
1080
1081 /*
1082  * Plan and optionally execute a querystring.
1083  *
1084  * If plan != NULL, just prepare plan trees and save them in *plan;
1085  * else execute immediately.
1086  */
1087 static int
1088 _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
1089 {
1090         List       *raw_parsetree_list;
1091         List       *query_list_list;
1092         List       *plan_list;
1093         ListCell   *list_item;
1094         ErrorContextCallback spierrcontext;
1095         int                     nargs = 0;
1096         Oid                *argtypes = NULL;
1097         int                     res = 0;
1098
1099         if (plan)
1100         {
1101                 nargs = plan->nargs;
1102                 argtypes = plan->argtypes;
1103         }
1104
1105         /* Increment CommandCounter to see changes made by now */
1106         CommandCounterIncrement();
1107
1108         /* Reset state (only needed in case string is empty) */
1109         SPI_processed = 0;
1110         SPI_lastoid = InvalidOid;
1111         SPI_tuptable = NULL;
1112         _SPI_current->tuptable = NULL;
1113
1114         /*
1115          * Setup error traceback support for ereport()
1116          */
1117         spierrcontext.callback = _SPI_error_callback;
1118         spierrcontext.arg = (void *) src;
1119         spierrcontext.previous = error_context_stack;
1120         error_context_stack = &spierrcontext;
1121
1122         /*
1123          * Parse the request string into a list of raw parse trees.
1124          */
1125         raw_parsetree_list = pg_parse_query(src);
1126
1127         /*
1128          * Do parse analysis and rule rewrite for each raw parsetree.
1129          *
1130          * We save the querytrees from each raw parsetree as a separate sublist.
1131          * This allows _SPI_execute_plan() to know where the boundaries
1132          * between original queries fall.
1133          */
1134         query_list_list = NIL;
1135         plan_list = NIL;
1136
1137         foreach(list_item, raw_parsetree_list)
1138         {
1139                 Node       *parsetree = (Node *) lfirst(list_item);
1140                 List       *query_list;
1141                 ListCell   *query_list_item;
1142
1143                 query_list = pg_analyze_and_rewrite(parsetree, argtypes, nargs);
1144
1145                 query_list_list = lappend(query_list_list, query_list);
1146
1147                 /* Reset state for each original parsetree */
1148                 /* (at most one of its querytrees will be marked canSetTag) */
1149                 SPI_processed = 0;
1150                 SPI_lastoid = InvalidOid;
1151                 SPI_tuptable = NULL;
1152                 _SPI_current->tuptable = NULL;
1153
1154                 foreach(query_list_item, query_list)
1155                 {
1156                         Query      *queryTree = (Query *) lfirst(query_list_item);
1157                         Plan       *planTree;
1158                         QueryDesc  *qdesc;
1159                         DestReceiver *dest;
1160
1161                         planTree = pg_plan_query(queryTree, NULL);
1162                         plan_list = lappend(plan_list, planTree);
1163
1164                         dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL);
1165                         if (queryTree->commandType == CMD_UTILITY)
1166                         {
1167                                 if (IsA(queryTree->utilityStmt, CopyStmt))
1168                                 {
1169                                         CopyStmt   *stmt = (CopyStmt *) queryTree->utilityStmt;
1170
1171                                         if (stmt->filename == NULL)
1172                                         {
1173                                                 res = SPI_ERROR_COPY;
1174                                                 goto fail;
1175                                         }
1176                                 }
1177                                 else if (IsA(queryTree->utilityStmt, DeclareCursorStmt) ||
1178                                                  IsA(queryTree->utilityStmt, ClosePortalStmt) ||
1179                                                  IsA(queryTree->utilityStmt, FetchStmt))
1180                                 {
1181                                         res = SPI_ERROR_CURSOR;
1182                                         goto fail;
1183                                 }
1184                                 else if (IsA(queryTree->utilityStmt, TransactionStmt))
1185                                 {
1186                                         res = SPI_ERROR_TRANSACTION;
1187                                         goto fail;
1188                                 }
1189                                 res = SPI_OK_UTILITY;
1190                                 if (plan == NULL)
1191                                 {
1192                                         ProcessUtility(queryTree->utilityStmt, dest, NULL);
1193                                         CommandCounterIncrement();
1194                                 }
1195                         }
1196                         else if (plan == NULL)
1197                         {
1198                                 qdesc = CreateQueryDesc(queryTree, planTree, dest,
1199                                                                                 NULL, false);
1200                                 res = _SPI_pquery(qdesc, true, false,
1201                                                                   queryTree->canSetTag ? tcount : 0);
1202                                 if (res < 0)
1203                                         goto fail;
1204                                 CommandCounterIncrement();
1205                         }
1206                         else
1207                         {
1208                                 qdesc = CreateQueryDesc(queryTree, planTree, dest,
1209                                                                                 NULL, false);
1210                                 res = _SPI_pquery(qdesc, false, false, 0);
1211                                 if (res < 0)
1212                                         goto fail;
1213                         }
1214                 }
1215         }
1216
1217         if (plan)
1218         {
1219                 plan->qtlist = query_list_list;
1220                 plan->ptlist = plan_list;
1221         }
1222
1223 fail:
1224
1225         /*
1226          * Pop the error context stack
1227          */
1228         error_context_stack = spierrcontext.previous;
1229
1230         return res;
1231 }
1232
1233 static int
1234 _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
1235                                   bool useCurrentSnapshot, int tcount)
1236 {
1237         List       *query_list_list = plan->qtlist;
1238         ListCell   *plan_list_item = list_head(plan->ptlist);
1239         ListCell   *query_list_list_item;
1240         ErrorContextCallback spierrcontext;
1241         int                     nargs = plan->nargs;
1242         int                     res = 0;
1243         ParamListInfo paramLI;
1244
1245         /* Increment CommandCounter to see changes made by now */
1246         CommandCounterIncrement();
1247
1248         /* Convert parameters to form wanted by executor */
1249         if (nargs > 0)
1250         {
1251                 int                     k;
1252
1253                 paramLI = (ParamListInfo)
1254                         palloc0((nargs + 1) * sizeof(ParamListInfoData));
1255
1256                 for (k = 0; k < nargs; k++)
1257                 {
1258                         paramLI[k].kind = PARAM_NUM;
1259                         paramLI[k].id = k + 1;
1260                         paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
1261                         paramLI[k].value = Values[k];
1262                 }
1263                 paramLI[k].kind = PARAM_INVALID;
1264         }
1265         else
1266                 paramLI = NULL;
1267
1268         /* Reset state (only needed in case string is empty) */
1269         SPI_processed = 0;
1270         SPI_lastoid = InvalidOid;
1271         SPI_tuptable = NULL;
1272         _SPI_current->tuptable = NULL;
1273
1274         /*
1275          * Setup error traceback support for ereport()
1276          */
1277         spierrcontext.callback = _SPI_error_callback;
1278         spierrcontext.arg = (void *) plan->query;
1279         spierrcontext.previous = error_context_stack;
1280         error_context_stack = &spierrcontext;
1281
1282         foreach(query_list_list_item, query_list_list)
1283         {
1284                 List       *query_list = lfirst(query_list_list_item);
1285                 ListCell   *query_list_item;
1286
1287                 /* Reset state for each original parsetree */
1288                 /* (at most one of its querytrees will be marked canSetTag) */
1289                 SPI_processed = 0;
1290                 SPI_lastoid = InvalidOid;
1291                 SPI_tuptable = NULL;
1292                 _SPI_current->tuptable = NULL;
1293
1294                 foreach(query_list_item, query_list)
1295                 {
1296                         Query      *queryTree = (Query *) lfirst(query_list_item);
1297                         Plan       *planTree;
1298                         QueryDesc  *qdesc;
1299                         DestReceiver *dest;
1300
1301                         planTree = lfirst(plan_list_item);
1302                         plan_list_item = lnext(plan_list_item);
1303
1304                         dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL);
1305                         if (queryTree->commandType == CMD_UTILITY)
1306                         {
1307                                 ProcessUtility(queryTree->utilityStmt, dest, NULL);
1308                                 res = SPI_OK_UTILITY;
1309                                 CommandCounterIncrement();
1310                         }
1311                         else
1312                         {
1313                                 qdesc = CreateQueryDesc(queryTree, planTree, dest,
1314                                                                                 paramLI, false);
1315                                 res = _SPI_pquery(qdesc, true, useCurrentSnapshot,
1316                                                                   queryTree->canSetTag ? tcount : 0);
1317                                 if (res < 0)
1318                                         goto fail;
1319                                 CommandCounterIncrement();
1320                         }
1321                 }
1322         }
1323
1324 fail:
1325
1326         /*
1327          * Pop the error context stack
1328          */
1329         error_context_stack = spierrcontext.previous;
1330
1331         return res;
1332 }
1333
1334 static int
1335 _SPI_pquery(QueryDesc *queryDesc, bool runit,
1336                         bool useCurrentSnapshot, int tcount)
1337 {
1338         int                     operation = queryDesc->operation;
1339         int                     res;
1340         Oid                     save_lastoid;
1341
1342         switch (operation)
1343         {
1344                 case CMD_SELECT:
1345                         res = SPI_OK_SELECT;
1346                         if (queryDesc->parsetree->into != NULL)         /* select into table */
1347                         {
1348                                 res = SPI_OK_SELINTO;
1349                                 queryDesc->dest = None_Receiver;                /* don't output results */
1350                         }
1351                         break;
1352                 case CMD_INSERT:
1353                         res = SPI_OK_INSERT;
1354                         break;
1355                 case CMD_DELETE:
1356                         res = SPI_OK_DELETE;
1357                         break;
1358                 case CMD_UPDATE:
1359                         res = SPI_OK_UPDATE;
1360                         break;
1361                 default:
1362                         return SPI_ERROR_OPUNKNOWN;
1363         }
1364
1365         if (!runit)                                     /* plan preparation, don't execute */
1366                 return res;
1367
1368 #ifdef SPI_EXECUTOR_STATS
1369         if (ShowExecutorStats)
1370                 ResetUsage();
1371 #endif
1372
1373         ExecutorStart(queryDesc, useCurrentSnapshot, false);
1374
1375         ExecutorRun(queryDesc, ForwardScanDirection, (long) tcount);
1376
1377         _SPI_current->processed = queryDesc->estate->es_processed;
1378         save_lastoid = queryDesc->estate->es_lastoid;
1379
1380         if (operation == CMD_SELECT && queryDesc->dest->mydest == SPI)
1381         {
1382                 if (_SPI_checktuples())
1383                         elog(ERROR, "consistency check on SPI tuple count failed");
1384         }
1385
1386         if (queryDesc->dest->mydest == SPI)
1387         {
1388                 SPI_processed = _SPI_current->processed;
1389                 SPI_lastoid = save_lastoid;
1390                 SPI_tuptable = _SPI_current->tuptable;
1391         }
1392         else if (res == SPI_OK_SELECT)
1393         {
1394                 /* Don't return SPI_OK_SELECT if we discarded the result */
1395                 res = SPI_OK_UTILITY;
1396         }
1397
1398         ExecutorEnd(queryDesc);
1399
1400         FreeQueryDesc(queryDesc);
1401
1402 #ifdef SPI_EXECUTOR_STATS
1403         if (ShowExecutorStats)
1404                 ShowUsage("SPI EXECUTOR STATS");
1405 #endif
1406
1407         return res;
1408 }
1409
1410 /*
1411  * _SPI_error_callback
1412  *
1413  * Add context information when a query invoked via SPI fails
1414  */
1415 static void
1416 _SPI_error_callback(void *arg)
1417 {
1418         const char *query = (const char *) arg;
1419         int                     syntaxerrposition;
1420
1421         /*
1422          * If there is a syntax error position, convert to internal syntax error;
1423          * otherwise treat the query as an item of context stack
1424          */
1425         syntaxerrposition = geterrposition();
1426         if (syntaxerrposition > 0)
1427         {
1428                 errposition(0);
1429                 internalerrposition(syntaxerrposition);
1430                 internalerrquery(query);
1431         }
1432         else
1433                 errcontext("SQL query \"%s\"", query);
1434 }
1435
1436 /*
1437  * _SPI_cursor_operation()
1438  *
1439  *      Do a FETCH or MOVE in a cursor
1440  */
1441 static void
1442 _SPI_cursor_operation(Portal portal, bool forward, int count,
1443                                           DestReceiver *dest)
1444 {
1445         long            nfetched;
1446
1447         /* Check that the portal is valid */
1448         if (!PortalIsValid(portal))
1449                 elog(ERROR, "invalid portal in SPI cursor operation");
1450
1451         /* Push the SPI stack */
1452         if (_SPI_begin_call(true) < 0)
1453                 elog(ERROR, "SPI cursor operation called while not connected");
1454
1455         /* Reset the SPI result */
1456         SPI_processed = 0;
1457         SPI_tuptable = NULL;
1458         _SPI_current->processed = 0;
1459         _SPI_current->tuptable = NULL;
1460
1461         /* Run the cursor */
1462         nfetched = PortalRunFetch(portal,
1463                                                           forward ? FETCH_FORWARD : FETCH_BACKWARD,
1464                                                           (long) count,
1465                                                           dest);
1466
1467         /*
1468          * Think not to combine this store with the preceding function call.
1469          * If the portal contains calls to functions that use SPI, then
1470          * SPI_stack is likely to move around while the portal runs.  When
1471          * control returns, _SPI_current will point to the correct stack
1472          * entry... but the pointer may be different than it was beforehand.
1473          * So we must be sure to re-fetch the pointer after the function call
1474          * completes.
1475          */
1476         _SPI_current->processed = nfetched;
1477
1478         if (dest->mydest == SPI && _SPI_checktuples())
1479                 elog(ERROR, "consistency check on SPI tuple count failed");
1480
1481         /* Put the result into place for access by caller */
1482         SPI_processed = _SPI_current->processed;
1483         SPI_tuptable = _SPI_current->tuptable;
1484
1485         /* Pop the SPI stack */
1486         _SPI_end_call(true);
1487 }
1488
1489
1490 static MemoryContext
1491 _SPI_execmem()
1492 {
1493         return MemoryContextSwitchTo(_SPI_current->execCxt);
1494 }
1495
1496 static MemoryContext
1497 _SPI_procmem()
1498 {
1499         return MemoryContextSwitchTo(_SPI_current->procCxt);
1500 }
1501
1502 /*
1503  * _SPI_begin_call: begin a SPI operation within a connected procedure
1504  */
1505 static int
1506 _SPI_begin_call(bool execmem)
1507 {
1508         if (_SPI_curid + 1 != _SPI_connected)
1509                 return SPI_ERROR_UNCONNECTED;
1510         _SPI_curid++;
1511         if (_SPI_current != &(_SPI_stack[_SPI_curid]))
1512                 elog(ERROR, "SPI stack corrupted");
1513
1514         if (execmem)                            /* switch to the Executor memory context */
1515                 _SPI_execmem();
1516
1517         return 0;
1518 }
1519
1520 /*
1521  * _SPI_end_call: end a SPI operation within a connected procedure
1522  *
1523  * Note: this currently has no failure return cases, so callers don't check
1524  */
1525 static int
1526 _SPI_end_call(bool procmem)
1527 {
1528         /*
1529          * We're returning to procedure where _SPI_curid == _SPI_connected - 1
1530          */
1531         _SPI_curid--;
1532
1533         if (procmem)                            /* switch to the procedure memory context */
1534         {
1535                 _SPI_procmem();
1536                 /* and free Executor memory */
1537                 MemoryContextResetAndDeleteChildren(_SPI_current->execCxt);
1538         }
1539
1540         return 0;
1541 }
1542
1543 static bool
1544 _SPI_checktuples(void)
1545 {
1546         uint32          processed = _SPI_current->processed;
1547         SPITupleTable *tuptable = _SPI_current->tuptable;
1548         bool            failed = false;
1549
1550         if (tuptable == NULL)           /* spi_dest_startup was not called */
1551                 failed = true;
1552         else if (processed != (tuptable->alloced - tuptable->free))
1553                 failed = true;
1554
1555         return failed;
1556 }
1557
1558 static _SPI_plan *
1559 _SPI_copy_plan(_SPI_plan *plan, int location)
1560 {
1561         _SPI_plan  *newplan;
1562         MemoryContext oldcxt;
1563         MemoryContext plancxt;
1564         MemoryContext parentcxt;
1565
1566         /* Determine correct parent for the plan's memory context */
1567         if (location == _SPI_CPLAN_PROCXT)
1568                 parentcxt = _SPI_current->procCxt;
1569         else if (location == _SPI_CPLAN_TOPCXT)
1570                 parentcxt = TopMemoryContext;
1571         else                            /* (this case not currently used) */
1572                 parentcxt = CurrentMemoryContext;
1573
1574         /*
1575          * Create a memory context for the plan.  We don't expect the plan to
1576          * be very large, so use smaller-than-default alloc parameters.
1577          */
1578         plancxt = AllocSetContextCreate(parentcxt,
1579                                                                         "SPI Plan",
1580                                                                         ALLOCSET_SMALL_MINSIZE,
1581                                                                         ALLOCSET_SMALL_INITSIZE,
1582                                                                         ALLOCSET_SMALL_MAXSIZE);
1583         oldcxt = MemoryContextSwitchTo(plancxt);
1584
1585         /* Copy the SPI plan into its own context */
1586         newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan));
1587         newplan->plancxt = plancxt;
1588         newplan->query = pstrdup(plan->query);
1589         newplan->qtlist = (List *) copyObject(plan->qtlist);
1590         newplan->ptlist = (List *) copyObject(plan->ptlist);
1591         newplan->nargs = plan->nargs;
1592         if (plan->nargs > 0)
1593         {
1594                 newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
1595                 memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
1596         }
1597         else
1598                 newplan->argtypes = NULL;
1599
1600         MemoryContextSwitchTo(oldcxt);
1601
1602         return newplan;
1603 }