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