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