]> granicus.if.org Git - postgresql/blob - src/backend/executor/spi.c
Restructure command destination handling so that we pass around
[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.96 2003/05/06 20:26:27 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                                           DestReceiver *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, CreateDestReceiver(SPI));
845         /* we know that the SPI receiver doesn't need a destroy call */
846 }
847
848
849 /*
850  * SPI_cursor_move()
851  *
852  *      Move in a cursor
853  */
854 void
855 SPI_cursor_move(Portal portal, bool forward, int count)
856 {
857         _SPI_cursor_operation(portal, forward, count, None_Receiver);
858 }
859
860
861 /*
862  * SPI_cursor_close()
863  *
864  *      Close a cursor
865  */
866 void
867 SPI_cursor_close(Portal portal)
868 {
869         if (!PortalIsValid(portal))
870                 elog(ERROR, "invalid portal in SPI cursor operation");
871
872         PortalDrop(portal, false);
873 }
874
875 /* =================== private functions =================== */
876
877 /*
878  * spi_dest_startup
879  *              Initialize to receive tuples from Executor into SPITupleTable
880  *              of current SPI procedure
881  */
882 void
883 spi_dest_startup(DestReceiver *self, int operation,
884                                  const char *portalName, TupleDesc typeinfo, List *targetlist)
885 {
886         SPITupleTable *tuptable;
887         MemoryContext oldcxt;
888         MemoryContext tuptabcxt;
889
890         /*
891          * When called by Executor _SPI_curid expected to be equal to
892          * _SPI_connected
893          */
894         if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
895                 elog(FATAL, "SPI: improper call to spi_dest_startup");
896         if (_SPI_current != &(_SPI_stack[_SPI_curid]))
897                 elog(FATAL, "SPI: stack corrupted in spi_dest_startup");
898
899         if (_SPI_current->tuptable != NULL)
900                 elog(FATAL, "SPI: improper call to spi_dest_startup");
901
902         oldcxt = _SPI_procmem();        /* switch to procedure memory context */
903
904         tuptabcxt = AllocSetContextCreate(CurrentMemoryContext,
905                                                                           "SPI TupTable",
906                                                                           ALLOCSET_DEFAULT_MINSIZE,
907                                                                           ALLOCSET_DEFAULT_INITSIZE,
908                                                                           ALLOCSET_DEFAULT_MAXSIZE);
909         MemoryContextSwitchTo(tuptabcxt);
910
911         _SPI_current->tuptable = tuptable = (SPITupleTable *)
912                 palloc(sizeof(SPITupleTable));
913         tuptable->tuptabcxt = tuptabcxt;
914         tuptable->alloced = tuptable->free = 128;
915         tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
916         tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
917
918         MemoryContextSwitchTo(oldcxt);
919 }
920
921 /*
922  * spi_printtup
923  *              store tuple retrieved by Executor into SPITupleTable
924  *              of current SPI procedure
925  */
926 void
927 spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
928 {
929         SPITupleTable *tuptable;
930         MemoryContext oldcxt;
931
932         /*
933          * When called by Executor _SPI_curid expected to be equal to
934          * _SPI_connected
935          */
936         if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
937                 elog(FATAL, "SPI: improper call to spi_printtup");
938         if (_SPI_current != &(_SPI_stack[_SPI_curid]))
939                 elog(FATAL, "SPI: stack corrupted in spi_printtup");
940
941         tuptable = _SPI_current->tuptable;
942         if (tuptable == NULL)
943                 elog(FATAL, "SPI: improper call to spi_printtup");
944
945         oldcxt = MemoryContextSwitchTo(tuptable->tuptabcxt);
946
947         if (tuptable->free == 0)
948         {
949                 tuptable->free = 256;
950                 tuptable->alloced += tuptable->free;
951                 tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
952                                                                   tuptable->alloced * sizeof(HeapTuple));
953         }
954
955         tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple);
956         (tuptable->free)--;
957
958         MemoryContextSwitchTo(oldcxt);
959 }
960
961 /*
962  * Static functions
963  */
964
965 /*
966  * Plan and optionally execute a querystring.
967  *
968  * If plan != NULL, just prepare plan tree, else execute immediately.
969  */
970 static int
971 _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
972 {
973         List       *raw_parsetree_list;
974         List       *query_list_list;
975         List       *plan_list;
976         List       *list_item;
977         int                     nargs = 0;
978         Oid                *argtypes = NULL;
979         int                     res = 0;
980
981         if (plan)
982         {
983                 nargs = plan->nargs;
984                 argtypes = plan->argtypes;
985         }
986
987         /* Increment CommandCounter to see changes made by now */
988         CommandCounterIncrement();
989
990         /* Reset state (only needed in case string is empty) */
991         SPI_processed = 0;
992         SPI_lastoid = InvalidOid;
993         SPI_tuptable = NULL;
994         _SPI_current->tuptable = NULL;
995
996         /*
997          * Parse the request string into a list of raw parse trees.
998          */
999         raw_parsetree_list = pg_parse_query(src);
1000
1001         /*
1002          * Do parse analysis and rule rewrite for each raw parsetree.
1003          *
1004          * We save the querytrees from each raw parsetree as a separate
1005          * sublist.  This allows _SPI_execute_plan() to know where the
1006          * boundaries between original queries fall.
1007          */
1008         query_list_list = NIL;
1009         plan_list = NIL;
1010
1011         foreach(list_item, raw_parsetree_list)
1012         {
1013                 Node       *parsetree = (Node *) lfirst(list_item);
1014                 List       *query_list;
1015                 List       *query_list_item;
1016
1017                 query_list = pg_analyze_and_rewrite(parsetree, argtypes, nargs);
1018
1019                 query_list_list = lappend(query_list_list, query_list);
1020
1021                 /* Reset state for each original parsetree */
1022                 /* (at most one of its querytrees will be marked canSetTag) */
1023                 SPI_processed = 0;
1024                 SPI_lastoid = InvalidOid;
1025                 SPI_tuptable = NULL;
1026                 _SPI_current->tuptable = NULL;
1027
1028                 foreach(query_list_item, query_list)
1029                 {
1030                         Query      *queryTree = (Query *) lfirst(query_list_item);
1031                         Plan       *planTree;
1032                         QueryDesc  *qdesc;
1033                         DestReceiver *dest;
1034
1035                         planTree = pg_plan_query(queryTree);
1036                         plan_list = lappend(plan_list, planTree);
1037
1038                         dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None);
1039                         if (queryTree->commandType == CMD_UTILITY)
1040                         {
1041                                 if (IsA(queryTree->utilityStmt, CopyStmt))
1042                                 {
1043                                         CopyStmt   *stmt = (CopyStmt *) queryTree->utilityStmt;
1044
1045                                         if (stmt->filename == NULL)
1046                                                 return SPI_ERROR_COPY;
1047                                 }
1048                                 else if (IsA(queryTree->utilityStmt, DeclareCursorStmt) ||
1049                                                  IsA(queryTree->utilityStmt, ClosePortalStmt) ||
1050                                                  IsA(queryTree->utilityStmt, FetchStmt))
1051                                         return SPI_ERROR_CURSOR;
1052                                 else if (IsA(queryTree->utilityStmt, TransactionStmt))
1053                                         return SPI_ERROR_TRANSACTION;
1054                                 res = SPI_OK_UTILITY;
1055                                 if (plan == NULL)
1056                                 {
1057                                         ProcessUtility(queryTree->utilityStmt, dest, NULL);
1058                                         CommandCounterIncrement();
1059                                 }
1060                         }
1061                         else if (plan == NULL)
1062                         {
1063                                 qdesc = CreateQueryDesc(queryTree, planTree, dest,
1064                                                                                 NULL, NULL, false);
1065                                 res = _SPI_pquery(qdesc, true,
1066                                                                   queryTree->canSetTag ? tcount : 0);
1067                                 if (res < 0)
1068                                         return res;
1069                                 CommandCounterIncrement();
1070                         }
1071                         else
1072                         {
1073                                 qdesc = CreateQueryDesc(queryTree, planTree, dest,
1074                                                                                 NULL, NULL, false);
1075                                 res = _SPI_pquery(qdesc, false, 0);
1076                                 if (res < 0)
1077                                         return res;
1078                         }
1079                 }
1080         }
1081
1082         if (plan)
1083         {
1084                 plan->qtlist = query_list_list;
1085                 plan->ptlist = plan_list;
1086         }
1087
1088         return res;
1089 }
1090
1091 static int
1092 _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
1093                                   int tcount)
1094 {
1095         List       *query_list_list = plan->qtlist;
1096         List       *plan_list = plan->ptlist;
1097         List       *query_list_list_item;
1098         int                     nargs = plan->nargs;
1099         int                     res = 0;
1100         ParamListInfo paramLI;
1101
1102         /* Increment CommandCounter to see changes made by now */
1103         CommandCounterIncrement();
1104
1105         /* Convert parameters to form wanted by executor */
1106         if (nargs > 0)
1107         {
1108                 int                     k;
1109
1110                 paramLI = (ParamListInfo)
1111                         palloc0((nargs + 1) * sizeof(ParamListInfoData));
1112
1113                 for (k = 0; k < nargs; k++)
1114                 {
1115                         paramLI[k].kind = PARAM_NUM;
1116                         paramLI[k].id = k + 1;
1117                         paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
1118                         paramLI[k].value = Values[k];
1119                 }
1120                 paramLI[k].kind = PARAM_INVALID;
1121         }
1122         else
1123                 paramLI = NULL;
1124
1125         /* Reset state (only needed in case string is empty) */
1126         SPI_processed = 0;
1127         SPI_lastoid = InvalidOid;
1128         SPI_tuptable = NULL;
1129         _SPI_current->tuptable = NULL;
1130
1131         foreach(query_list_list_item, query_list_list)
1132         {
1133                 List   *query_list = lfirst(query_list_list_item);
1134                 List   *query_list_item;
1135
1136                 /* Reset state for each original parsetree */
1137                 /* (at most one of its querytrees will be marked canSetTag) */
1138                 SPI_processed = 0;
1139                 SPI_lastoid = InvalidOid;
1140                 SPI_tuptable = NULL;
1141                 _SPI_current->tuptable = NULL;
1142
1143                 foreach(query_list_item, query_list)
1144                 {
1145                         Query  *queryTree = (Query *) lfirst(query_list_item);
1146                         Plan       *planTree;
1147                         QueryDesc  *qdesc;
1148                         DestReceiver *dest;
1149
1150                         planTree = lfirst(plan_list);
1151                         plan_list = lnext(plan_list);
1152
1153                         dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None);
1154                         if (queryTree->commandType == CMD_UTILITY)
1155                         {
1156                                 ProcessUtility(queryTree->utilityStmt, dest, NULL);
1157                                 res = SPI_OK_UTILITY;
1158                                 CommandCounterIncrement();
1159                         }
1160                         else
1161                         {
1162                                 qdesc = CreateQueryDesc(queryTree, planTree, dest,
1163                                                                                 NULL, paramLI, false);
1164                                 res = _SPI_pquery(qdesc, true,
1165                                                                   queryTree->canSetTag ? tcount : 0);
1166                                 if (res < 0)
1167                                         return res;
1168                                 CommandCounterIncrement();
1169                         }
1170                 }
1171         }
1172
1173         return res;
1174 }
1175
1176 static int
1177 _SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount)
1178 {
1179         int                     operation = queryDesc->operation;
1180         int                     res;
1181         Oid                     save_lastoid;
1182
1183         switch (operation)
1184         {
1185                 case CMD_SELECT:
1186                         res = SPI_OK_SELECT;
1187                         if (queryDesc->parsetree->into != NULL) /* select into table */
1188                         {
1189                                 res = SPI_OK_SELINTO;
1190                                 queryDesc->dest = None_Receiver; /* don't output results */
1191                         }
1192                         break;
1193                 case CMD_INSERT:
1194                         res = SPI_OK_INSERT;
1195                         break;
1196                 case CMD_DELETE:
1197                         res = SPI_OK_DELETE;
1198                         break;
1199                 case CMD_UPDATE:
1200                         res = SPI_OK_UPDATE;
1201                         break;
1202                 default:
1203                         return SPI_ERROR_OPUNKNOWN;
1204         }
1205
1206         if (!runit)                                     /* plan preparation, don't execute */
1207                 return res;
1208
1209 #ifdef SPI_EXECUTOR_STATS
1210         if (ShowExecutorStats)
1211                 ResetUsage();
1212 #endif
1213
1214         ExecutorStart(queryDesc, false);
1215
1216         ExecutorRun(queryDesc, ForwardScanDirection, (long) tcount);
1217
1218         _SPI_current->processed = queryDesc->estate->es_processed;
1219         save_lastoid = queryDesc->estate->es_lastoid;
1220
1221         if (operation == CMD_SELECT && queryDesc->dest->mydest == SPI)
1222         {
1223                 if (_SPI_checktuples())
1224                         elog(FATAL, "SPI_select: # of processed tuples check failed");
1225         }
1226
1227         if (queryDesc->dest->mydest == SPI)
1228         {
1229                 SPI_processed = _SPI_current->processed;
1230                 SPI_lastoid = save_lastoid;
1231                 SPI_tuptable = _SPI_current->tuptable;
1232         }
1233         else if (res == SPI_OK_SELECT)
1234         {
1235                 /* Don't return SPI_OK_SELECT if we discarded the result */
1236                 res = SPI_OK_UTILITY;
1237         }
1238
1239         ExecutorEnd(queryDesc);
1240
1241         FreeQueryDesc(queryDesc);
1242
1243 #ifdef SPI_EXECUTOR_STATS
1244         if (ShowExecutorStats)
1245                 ShowUsage("SPI EXECUTOR STATS");
1246 #endif
1247
1248         return res;
1249 }
1250
1251 /*
1252  * _SPI_cursor_operation()
1253  *
1254  *      Do a FETCH or MOVE in a cursor
1255  */
1256 static void
1257 _SPI_cursor_operation(Portal portal, bool forward, int count,
1258                                           DestReceiver *dest)
1259 {
1260         /* Check that the portal is valid */
1261         if (!PortalIsValid(portal))
1262                 elog(ERROR, "invalid portal in SPI cursor operation");
1263
1264         /* Push the SPI stack */
1265         _SPI_begin_call(true);
1266
1267         /* Reset the SPI result */
1268         SPI_processed = 0;
1269         SPI_tuptable = NULL;
1270         _SPI_current->processed = 0;
1271         _SPI_current->tuptable = NULL;
1272
1273         /* Run the cursor */
1274         _SPI_current->processed =
1275                 PortalRunFetch(portal,
1276                                            forward ? FETCH_FORWARD : FETCH_BACKWARD,
1277                                            (long) count,
1278                                            dest);
1279
1280         if (dest->mydest == SPI && _SPI_checktuples())
1281                 elog(FATAL, "SPI_fetch: # of processed tuples check failed");
1282
1283         /* Put the result into place for access by caller */
1284         SPI_processed = _SPI_current->processed;
1285         SPI_tuptable = _SPI_current->tuptable;
1286
1287         /* Pop the SPI stack */
1288         _SPI_end_call(true);
1289 }
1290
1291
1292 static MemoryContext
1293 _SPI_execmem()
1294 {
1295         return MemoryContextSwitchTo(_SPI_current->execCxt);
1296 }
1297
1298 static MemoryContext
1299 _SPI_procmem()
1300 {
1301         return MemoryContextSwitchTo(_SPI_current->procCxt);
1302 }
1303
1304 /*
1305  * _SPI_begin_call
1306  *
1307  */
1308 static int
1309 _SPI_begin_call(bool execmem)
1310 {
1311         if (_SPI_curid + 1 != _SPI_connected)
1312                 return SPI_ERROR_UNCONNECTED;
1313         _SPI_curid++;
1314         if (_SPI_current != &(_SPI_stack[_SPI_curid]))
1315                 elog(FATAL, "SPI: stack corrupted");
1316
1317         if (execmem)                            /* switch to the Executor memory context */
1318                 _SPI_execmem();
1319
1320         return 0;
1321 }
1322
1323 static int
1324 _SPI_end_call(bool procmem)
1325 {
1326         /*
1327          * We're returning to procedure where _SPI_curid == _SPI_connected - 1
1328          */
1329         _SPI_curid--;
1330
1331         if (procmem)                            /* switch to the procedure memory context */
1332         {
1333                 _SPI_procmem();
1334                 /* and free Executor memory */
1335                 MemoryContextResetAndDeleteChildren(_SPI_current->execCxt);
1336         }
1337
1338         return 0;
1339 }
1340
1341 static bool
1342 _SPI_checktuples(void)
1343 {
1344         uint32          processed = _SPI_current->processed;
1345         SPITupleTable *tuptable = _SPI_current->tuptable;
1346         bool            failed = false;
1347
1348         if (tuptable == NULL)   /* spi_dest_startup was not called */
1349                 failed = true;
1350         else if (processed != (tuptable->alloced - tuptable->free))
1351                 failed = true;
1352
1353         return failed;
1354 }
1355
1356 static _SPI_plan *
1357 _SPI_copy_plan(_SPI_plan *plan, int location)
1358 {
1359         _SPI_plan  *newplan;
1360         MemoryContext oldcxt;
1361         MemoryContext plancxt;
1362         MemoryContext parentcxt;
1363
1364         /* Determine correct parent for the plan's memory context */
1365         if (location == _SPI_CPLAN_PROCXT)
1366                 parentcxt = _SPI_current->procCxt;
1367         else if (location == _SPI_CPLAN_TOPCXT)
1368                 parentcxt = TopMemoryContext;
1369         else                                            /* (this case not currently used) */
1370                 parentcxt = CurrentMemoryContext;
1371
1372         /*
1373          * Create a memory context for the plan.  We don't expect the plan to
1374          * be very large, so use smaller-than-default alloc parameters.
1375          */
1376         plancxt = AllocSetContextCreate(parentcxt,
1377                                                                         "SPI Plan",
1378                                                                         ALLOCSET_SMALL_MINSIZE,
1379                                                                         ALLOCSET_SMALL_INITSIZE,
1380                                                                         ALLOCSET_SMALL_MAXSIZE);
1381         oldcxt = MemoryContextSwitchTo(plancxt);
1382
1383         /* Copy the SPI plan into its own context */
1384         newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan));
1385         newplan->plancxt = plancxt;
1386         newplan->qtlist = (List *) copyObject(plan->qtlist);
1387         newplan->ptlist = (List *) copyObject(plan->ptlist);
1388         newplan->nargs = plan->nargs;
1389         if (plan->nargs > 0)
1390         {
1391                 newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
1392                 memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
1393         }
1394         else
1395                 newplan->argtypes = NULL;
1396
1397         MemoryContextSwitchTo(oldcxt);
1398
1399         return newplan;
1400 }