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