]> granicus.if.org Git - postgresql/blob - src/backend/executor/spi.c
Goodbye register keyword. Compiler knows better.
[postgresql] / src / backend / executor / spi.c
1 /*-------------------------------------------------------------------------
2  *
3  * spi.c--
4  *                              Server Programming Interface
5  *
6  *-------------------------------------------------------------------------
7  */
8 #include "executor/spi.h"
9 #include "catalog/pg_type.h"
10 #include "access/printtup.h"
11 #include "fmgr.h"
12
13 typedef struct
14 {
15         QueryTreeList *qtlist;          /* malloced */
16         uint32          processed;              /* by Executor */
17         SPITupleTable *tuptable;
18         Portal          portal;                 /* portal per procedure */
19         MemoryContext savedcxt;
20         CommandId       savedId;
21 }                       _SPI_connection;
22
23 static Portal _SPI_portal = (Portal) NULL;
24 static _SPI_connection *_SPI_stack = NULL;
25 static _SPI_connection *_SPI_current = NULL;
26 static int      _SPI_connected = -1;
27 static int      _SPI_curid = -1;
28
29 uint32          SPI_processed = 0;
30 SPITupleTable *SPI_tuptable;
31 int                     SPI_result;
32
33 void            spi_printtup(HeapTuple tuple, TupleDesc tupdesc);
34
35 typedef struct
36 {
37         QueryTreeList *qtlist;
38         List       *ptlist;
39         int                     nargs;
40         Oid                *argtypes;
41 }                       _SPI_plan;
42
43 static int      _SPI_execute(char *src, int tcount, _SPI_plan * plan);
44 static int      _SPI_pquery(QueryDesc * queryDesc, EState * state, int tcount);
45
46 #if 0
47 static void _SPI_fetch(FetchStmt * stmt);
48
49 #endif
50 static int
51 _SPI_execute_plan(_SPI_plan * plan,
52                                   Datum * Values, char *Nulls, int tcount);
53
54 #define _SPI_CPLAN_CURCXT       0
55 #define _SPI_CPLAN_PROCXT       1
56 #define _SPI_CPLAN_TOPCXT       2
57
58 static _SPI_plan *_SPI_copy_plan(_SPI_plan * plan, int location);
59
60 static int      _SPI_begin_call(bool execmem);
61 static int      _SPI_end_call(bool procmem);
62 static MemoryContext _SPI_execmem(void);
63 static MemoryContext _SPI_procmem(void);
64 static bool _SPI_checktuples(void);
65
66 #ifdef SPI_EXECUTOR_STATS
67 extern int      ShowExecutorStats;
68 extern void ResetUsage(void);
69 extern void ShowUsage(void);
70
71 #endif
72
73 /* =================== interface functions =================== */
74
75 int
76 SPI_connect()
77 {
78         char            pname[64];
79         PortalVariableMemory pvmem;
80
81         /*
82          * It's possible on startup and after commit/abort. In future we'll
83          * catch commit/abort in some way...
84          */
85         strcpy(pname, "<SPI manager>");
86         _SPI_portal = GetPortalByName(pname);
87         if (!PortalIsValid(_SPI_portal))
88         {
89                 if (_SPI_stack != NULL) /* there was abort */
90                         free(_SPI_stack);
91                 _SPI_current = _SPI_stack = NULL;
92                 _SPI_connected = _SPI_curid = -1;
93                 SPI_processed = 0;
94                 SPI_tuptable = NULL;
95                 _SPI_portal = CreatePortal(pname);
96                 if (!PortalIsValid(_SPI_portal))
97                         elog(FATAL, "SPI_connect: global initialization failed");
98         }
99
100         /*
101          * When procedure called by Executor _SPI_curid expected to be equal
102          * to _SPI_connected
103          */
104         if (_SPI_curid != _SPI_connected)
105                 return (SPI_ERROR_CONNECT);
106
107         if (_SPI_stack == NULL)
108         {
109                 if (_SPI_connected != -1)
110                         elog(FATAL, "SPI_connect: no connection(s) expected");
111                 _SPI_stack = (_SPI_connection *) malloc(sizeof(_SPI_connection));
112         }
113         else
114         {
115                 if (_SPI_connected <= -1)
116                         elog(FATAL, "SPI_connect: some connection(s) expected");
117                 _SPI_stack = (_SPI_connection *) realloc(_SPI_stack,
118                                                  (_SPI_connected + 2) * sizeof(_SPI_connection));
119         }
120
121         /*
122          * We' returning to procedure where _SPI_curid == _SPI_connected - 1
123          */
124         _SPI_connected++;
125
126         _SPI_current = &(_SPI_stack[_SPI_connected]);
127         _SPI_current->qtlist = NULL;
128         _SPI_current->processed = 0;
129         _SPI_current->tuptable = NULL;
130
131         /* Create Portal for this procedure ... */
132         sprintf(pname, "<SPI %d>", _SPI_connected);
133         _SPI_current->portal = CreatePortal(pname);
134         if (!PortalIsValid(_SPI_current->portal))
135                 elog(FATAL, "SPI_connect: initialization failed");
136
137         /* ... and switch to Portal' Variable memory - procedure' context */
138         pvmem = PortalGetVariableMemory(_SPI_current->portal);
139         _SPI_current->savedcxt = MemoryContextSwitchTo((MemoryContext) pvmem);
140
141         _SPI_current->savedId = GetScanCommandId();
142         SetScanCommandId(GetCurrentCommandId());
143
144         return (SPI_OK_CONNECT);
145
146 }
147
148 int
149 SPI_finish()
150 {
151         int                     res;
152
153         res = _SPI_begin_call(false);           /* live in procedure memory */
154         if (res < 0)
155                 return (res);
156
157         /* Restore memory context as it was before procedure call */
158         MemoryContextSwitchTo(_SPI_current->savedcxt);
159         PortalDestroy(&(_SPI_current->portal));
160
161         SetScanCommandId(_SPI_current->savedId);
162
163         /*
164          * After _SPI_begin_call _SPI_connected == _SPI_curid. Now we are
165          * closing connection to SPI and returning to upper Executor and so
166          * _SPI_connected must be equal to _SPI_curid.
167          */
168         _SPI_connected--;
169         _SPI_curid--;
170         if (_SPI_connected == -1)
171         {
172                 free(_SPI_stack);
173                 _SPI_stack = NULL;
174         }
175         else
176         {
177                 _SPI_stack = (_SPI_connection *) realloc(_SPI_stack,
178                                                  (_SPI_connected + 1) * sizeof(_SPI_connection));
179                 _SPI_current = &(_SPI_stack[_SPI_connected]);
180         }
181
182         return (SPI_OK_FINISH);
183
184 }
185
186 int
187 SPI_exec(char *src, int tcount)
188 {
189         int                     res;
190
191         if (src == NULL || tcount < 0)
192                 return (SPI_ERROR_ARGUMENT);
193
194         res = _SPI_begin_call(true);
195         if (res < 0)
196                 return (res);
197
198         res = _SPI_execute(src, tcount, NULL);
199
200         _SPI_end_call(true);
201         return (res);
202 }
203
204 int
205 SPI_execp(void *plan, Datum * Values, char *Nulls, int tcount)
206 {
207         int                     res;
208
209         if (plan == NULL || tcount < 0)
210                 return (SPI_ERROR_ARGUMENT);
211
212         if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL)
213                 return (SPI_ERROR_PARAM);
214
215         res = _SPI_begin_call(true);
216         if (res < 0)
217                 return (res);
218
219         /* copy plan to current (executor) context */
220         plan = (void *) _SPI_copy_plan(plan, _SPI_CPLAN_CURCXT);
221
222         res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, tcount);
223
224         _SPI_end_call(true);
225         return (res);
226 }
227
228 void       *
229 SPI_prepare(char *src, int nargs, Oid * argtypes)
230 {
231         _SPI_plan  *plan;
232
233         if (src == NULL || nargs < 0 || (nargs > 0 && argtypes == NULL))
234         {
235                 SPI_result = SPI_ERROR_ARGUMENT;
236                 return (NULL);
237         }
238
239         SPI_result = _SPI_begin_call(true);
240         if (SPI_result < 0)
241                 return (NULL);
242
243         plan = (_SPI_plan *) palloc(sizeof(_SPI_plan));         /* Executor context */
244         plan->argtypes = argtypes;
245         plan->nargs = nargs;
246
247         SPI_result = _SPI_execute(src, 0, plan);
248
249         if (SPI_result >= 0)            /* copy plan to procedure context */
250                 plan = _SPI_copy_plan(plan, _SPI_CPLAN_PROCXT);
251         else
252                 plan = NULL;
253
254         _SPI_end_call(true);
255
256         return ((void *) plan);
257
258 }
259
260 void       *
261 SPI_saveplan(void *plan)
262 {
263         _SPI_plan  *newplan;
264
265         if (plan == NULL)
266         {
267                 SPI_result = SPI_ERROR_ARGUMENT;
268                 return (NULL);
269         }
270
271         SPI_result = _SPI_begin_call(false);            /* don't change context */
272         if (SPI_result < 0)
273                 return (NULL);
274
275         newplan = _SPI_copy_plan((_SPI_plan *) plan, _SPI_CPLAN_TOPCXT);
276
277         _SPI_curid--;
278         SPI_result = 0;
279
280         return ((void *) newplan);
281
282 }
283
284 HeapTuple
285 SPI_copytuple(HeapTuple tuple)
286 {
287         MemoryContext oldcxt = NULL;
288         HeapTuple       ctuple;
289
290         if (tuple == NULL)
291         {
292                 SPI_result = SPI_ERROR_ARGUMENT;
293                 return (NULL);
294         }
295
296         if (_SPI_curid + 1 == _SPI_connected)           /* connected */
297         {
298                 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
299                         elog(FATAL, "SPI: stack corrupted");
300                 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
301         }
302
303         ctuple = heap_copytuple(tuple);
304
305         if (oldcxt)
306                 MemoryContextSwitchTo(oldcxt);
307
308         return (ctuple);
309 }
310
311 HeapTuple
312 SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
313                                 Datum * Values, char *Nulls)
314 {
315         MemoryContext oldcxt = NULL;
316         HeapTuple       mtuple;
317         int                     numberOfAttributes;
318         uint8           infomask;
319         Datum      *v;
320         char       *n;
321         bool            isnull;
322         int                     i;
323
324         if (rel == NULL || tuple == NULL || natts <= 0 || attnum == NULL || Values == NULL)
325         {
326                 SPI_result = SPI_ERROR_ARGUMENT;
327                 return (NULL);
328         }
329
330         if (_SPI_curid + 1 == _SPI_connected)           /* connected */
331         {
332                 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
333                         elog(FATAL, "SPI: stack corrupted");
334                 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
335         }
336         SPI_result = 0;
337         numberOfAttributes = rel->rd_att->natts;
338         v = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
339         n = (char *) palloc(numberOfAttributes * sizeof(char));
340
341         /* fetch old values and nulls */
342         for (i = 0; i < numberOfAttributes; i++)
343         {
344                 v[i] = heap_getattr(tuple, i + 1, rel->rd_att, &isnull);
345                 n[i] = (isnull) ? 'n' : ' ';
346         }
347
348         /* replace values and nulls */
349         for (i = 0; i < natts; i++)
350         {
351                 if (attnum[i] <= 0 || attnum[i] > numberOfAttributes)
352                         break;
353                 v[attnum[i] - 1] = Values[i];
354                 n[attnum[i] - 1] = (Nulls && Nulls[i] == 'n') ? 'n' : ' ';
355         }
356
357         if (i == natts)                         /* no errors in attnum[] */
358         {
359                 mtuple = heap_formtuple(rel->rd_att, v, n);
360                 infomask = mtuple->t_infomask;
361                 memmove(&(mtuple->t_ctid), &(tuple->t_ctid),
362                                 ((char *) &(tuple->t_hoff) - (char *) &(tuple->t_ctid)));
363                 mtuple->t_infomask = infomask;
364                 mtuple->t_natts = numberOfAttributes;
365         }
366         else
367         {
368                 mtuple = NULL;
369                 SPI_result = SPI_ERROR_NOATTRIBUTE;
370         }
371
372         pfree(v);
373         pfree(n);
374
375         if (oldcxt)
376                 MemoryContextSwitchTo(oldcxt);
377
378         return (mtuple);
379 }
380
381 int
382 SPI_fnumber(TupleDesc tupdesc, char *fname)
383 {
384         int                     res;
385
386         for (res = 0; res < tupdesc->natts; res++)
387         {
388                 if (strcasecmp(tupdesc->attrs[res]->attname.data, fname) == 0)
389                         return (res + 1);
390         }
391
392         return (SPI_ERROR_NOATTRIBUTE);
393 }
394
395 char       *
396 SPI_fname(TupleDesc tupdesc, int fnumber)
397 {
398
399         SPI_result = 0;
400         if (tupdesc->natts < fnumber || fnumber <= 0)
401         {
402                 SPI_result = SPI_ERROR_NOATTRIBUTE;
403                 return (NULL);
404         }
405
406         return (nameout(&(tupdesc->attrs[fnumber - 1]->attname)));
407 }
408
409 char       *
410 SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
411 {
412         Datum           val;
413         bool            isnull;
414         Oid                     foutoid;
415
416         SPI_result = 0;
417         if (tuple->t_natts < fnumber || fnumber <= 0)
418         {
419                 SPI_result = SPI_ERROR_NOATTRIBUTE;
420                 return (NULL);
421         }
422
423         val = heap_getattr(tuple, fnumber, tupdesc, &isnull);
424         if (isnull)
425                 return (NULL);
426         foutoid = typtoout((Oid) tupdesc->attrs[fnumber - 1]->atttypid);
427         if (!OidIsValid(foutoid))
428         {
429                 SPI_result = SPI_ERROR_NOOUTFUNC;
430                 return (NULL);
431         }
432
433         return (fmgr(foutoid, val,
434                         gettypelem(tupdesc->attrs[fnumber - 1]->atttypid),
435                                            tupdesc->attrs[fnumber - 1]->atttypmod));
436 }
437
438 Datum
439 SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool * isnull)
440 {
441         Datum           val;
442
443         *isnull = true;
444         SPI_result = 0;
445         if (tuple->t_natts < fnumber || fnumber <= 0)
446         {
447                 SPI_result = SPI_ERROR_NOATTRIBUTE;
448                 return ((Datum) NULL);
449         }
450
451         val = heap_getattr(tuple, fnumber, tupdesc, isnull);
452
453         return (val);
454 }
455
456 char       *
457 SPI_gettype(TupleDesc tupdesc, int fnumber)
458 {
459         HeapTuple       typeTuple;
460
461         SPI_result = 0;
462         if (tupdesc->natts < fnumber || fnumber <= 0)
463         {
464                 SPI_result = SPI_ERROR_NOATTRIBUTE;
465                 return (NULL);
466         }
467
468         typeTuple = SearchSysCacheTuple(TYPOID,
469                                  ObjectIdGetDatum(tupdesc->attrs[fnumber - 1]->atttypid),
470                                                                         0, 0, 0);
471
472         if (!HeapTupleIsValid(typeTuple))
473         {
474                 SPI_result = SPI_ERROR_TYPUNKNOWN;
475                 return (NULL);
476         }
477
478         return (pstrdup(((TypeTupleForm) GETSTRUCT(typeTuple))->typname.data));
479 }
480
481 Oid
482 SPI_gettypeid(TupleDesc tupdesc, int fnumber)
483 {
484
485         SPI_result = 0;
486         if (tupdesc->natts < fnumber || fnumber <= 0)
487         {
488                 SPI_result = SPI_ERROR_NOATTRIBUTE;
489                 return (InvalidOid);
490         }
491
492         return (tupdesc->attrs[fnumber - 1]->atttypid);
493 }
494
495 char       *
496 SPI_getrelname(Relation rel)
497 {
498         return (pstrdup(rel->rd_rel->relname.data));
499 }
500
501 void *
502 SPI_palloc (Size size)
503 {
504         MemoryContext   oldcxt = NULL;
505         void               *pointer;
506         
507         if (_SPI_curid + 1 == _SPI_connected)           /* connected */
508         {
509                 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
510                         elog(FATAL, "SPI: stack corrupted");
511                 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
512         }
513         
514         pointer = palloc (size);
515         
516         if (oldcxt)
517                 MemoryContextSwitchTo(oldcxt);
518         
519         return (pointer);
520 }
521
522 void *
523 SPI_repalloc (void *pointer, Size size)
524 {
525         MemoryContext   oldcxt = NULL;
526         
527         if (_SPI_curid + 1 == _SPI_connected)           /* connected */
528         {
529                 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
530                         elog(FATAL, "SPI: stack corrupted");
531                 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
532         }
533         
534         pointer = repalloc (pointer, size);
535         
536         if (oldcxt)
537                 MemoryContextSwitchTo(oldcxt);
538         
539         return (pointer);
540 }
541
542 void 
543 SPI_pfree (void *pointer)
544 {
545         MemoryContext   oldcxt = NULL;
546         
547         if (_SPI_curid + 1 == _SPI_connected)           /* connected */
548         {
549                 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
550                         elog(FATAL, "SPI: stack corrupted");
551                 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
552         }
553         
554         pfree (pointer);
555         
556         if (oldcxt)
557                 MemoryContextSwitchTo(oldcxt);
558         
559         return;
560 }
561
562 /* =================== private functions =================== */
563
564 /*
565  * spi_printtup --
566  *              store tuple retrieved by Executor into SPITupleTable
567  *              of current SPI procedure
568  *
569  */
570 void
571 spi_printtup(HeapTuple tuple, TupleDesc tupdesc)
572 {
573         SPITupleTable *tuptable;
574         MemoryContext oldcxt;
575
576         /*
577          * When called by Executor _SPI_curid expected to be equal to
578          * _SPI_connected
579          */
580         if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
581                 elog(FATAL, "SPI: improper call to spi_printtup");
582         if (_SPI_current != &(_SPI_stack[_SPI_curid]))
583                 elog(FATAL, "SPI: stack corrupted in spi_printtup");
584
585         oldcxt = _SPI_procmem();        /* switch to procedure memory context */
586
587         tuptable = _SPI_current->tuptable;
588         if (tuptable == NULL)
589         {
590                 _SPI_current->tuptable = tuptable = (SPITupleTable *)
591                         palloc(sizeof(SPITupleTable));
592                 tuptable->alloced = tuptable->free = 128;
593                 tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
594                 tuptable->tupdesc = CreateTupleDescCopy(tupdesc);
595         }
596         else if (tuptable->free == 0)
597         {
598                 tuptable->free = 256;
599                 tuptable->alloced += tuptable->free;
600                 tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
601                                                                   tuptable->alloced * sizeof(HeapTuple));
602         }
603
604         tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple);
605         (tuptable->free)--;
606
607         MemoryContextSwitchTo(oldcxt);
608         return;
609 }
610
611 /*
612  * Static functions
613  */
614
615 static int
616 _SPI_execute(char *src, int tcount, _SPI_plan * plan)
617 {
618         QueryTreeList *queryTree_list;
619         List       *planTree_list;
620         List       *ptlist;
621         QueryDesc  *qdesc;
622         Query      *queryTree;
623         Plan       *planTree;
624         EState     *state;
625         int                     qlen;
626         int                     nargs = 0;
627         Oid                *argtypes = NULL;
628         int                     res;
629         int                     i;
630
631         /* Increment CommandCounter to see changes made by now */
632         CommandCounterIncrement();
633
634         SPI_processed = 0;
635         SPI_tuptable = NULL;
636         _SPI_current->tuptable = NULL;
637         _SPI_current->qtlist = NULL;
638
639         if (plan)
640         {
641                 nargs = plan->nargs;
642                 argtypes = plan->argtypes;
643         }
644         ptlist = planTree_list = (List *)
645                 pg_parse_and_plan(src, argtypes, nargs, &queryTree_list, None);
646
647         _SPI_current->qtlist = queryTree_list;
648
649         qlen = queryTree_list->len;
650         for (i = 0;; i++)
651         {
652                 queryTree = (Query *) (queryTree_list->qtrees[i]);
653                 planTree = lfirst(planTree_list);
654
655                 planTree_list = lnext(planTree_list);
656
657                 if (queryTree->commandType == CMD_UTILITY)
658                 {
659                         if (nodeTag(queryTree->utilityStmt) == T_CopyStmt)
660                         {
661                                 CopyStmt   *stmt = (CopyStmt *) (queryTree->utilityStmt);
662
663                                 if (stmt->filename == NULL)
664                                         return (SPI_ERROR_COPY);
665                         }
666                         else if (nodeTag(queryTree->utilityStmt) == T_ClosePortalStmt ||
667                                          nodeTag(queryTree->utilityStmt) == T_FetchStmt)
668                                 return (SPI_ERROR_CURSOR);
669                         else if (nodeTag(queryTree->utilityStmt) == T_TransactionStmt)
670                                 return (SPI_ERROR_TRANSACTION);
671                         res = SPI_OK_UTILITY;
672                         if (plan == NULL)
673                         {
674                                 ProcessUtility(queryTree->utilityStmt, None);
675                                 if (i < qlen - 1)
676                                         CommandCounterIncrement();
677                                 else
678                                         return (res);
679                         }
680                         else if (i >= qlen - 1)
681                                 break;
682                 }
683                 else if (plan == NULL)
684                 {
685                         qdesc = CreateQueryDesc(queryTree, planTree,
686                                                                         (i < qlen - 1) ? None : SPI);
687                         state = CreateExecutorState();
688                         res = _SPI_pquery(qdesc, state, (i < qlen - 1) ? 0 : tcount);
689                         if (res < 0 || i >= qlen - 1)
690                                 return (res);
691                         CommandCounterIncrement();
692                 }
693                 else
694                 {
695                         qdesc = CreateQueryDesc(queryTree, planTree,
696                                                                         (i < qlen - 1) ? None : SPI);
697                         res = _SPI_pquery(qdesc, NULL, (i < qlen - 1) ? 0 : tcount);
698                         if (res < 0)
699                                 return (res);
700                         if (i >= qlen - 1)
701                                 break;
702                 }
703         }
704
705         plan->qtlist = queryTree_list;
706         plan->ptlist = ptlist;
707
708         return (res);
709
710 }
711
712 static int
713 _SPI_execute_plan(_SPI_plan * plan, Datum * Values, char *Nulls, int tcount)
714 {
715         QueryTreeList *queryTree_list = plan->qtlist;
716         List       *planTree_list = plan->ptlist;
717         QueryDesc  *qdesc;
718         Query      *queryTree;
719         Plan       *planTree;
720         EState     *state;
721         int                     nargs = plan->nargs;
722         int                     qlen = queryTree_list->len;
723         int                     res;
724         int                     i,
725                                 k;
726
727         /* Increment CommandCounter to see changes made by now */
728         CommandCounterIncrement();
729
730         SPI_processed = 0;
731         SPI_tuptable = NULL;
732         _SPI_current->tuptable = NULL;
733         _SPI_current->qtlist = NULL;
734
735         for (i = 0;; i++)
736         {
737                 queryTree = (Query *) (queryTree_list->qtrees[i]);
738                 planTree = lfirst(planTree_list);
739
740                 planTree_list = lnext(planTree_list);
741
742                 if (queryTree->commandType == CMD_UTILITY)
743                 {
744                         ProcessUtility(queryTree->utilityStmt, None);
745                         if (i < qlen - 1)
746                                 CommandCounterIncrement();
747                         else
748                                 return (SPI_OK_UTILITY);
749                 }
750                 else
751                 {
752                         qdesc = CreateQueryDesc(queryTree, planTree,
753                                                                         (i < qlen - 1) ? None : SPI);
754                         state = CreateExecutorState();
755                         if (nargs > 0)
756                         {
757                                 ParamListInfo paramLI = (ParamListInfo) palloc((nargs + 1) *
758                                                                                           sizeof(ParamListInfoData));
759
760                                 state->es_param_list_info = paramLI;
761                                 for (k = 0; k < plan->nargs; paramLI++, k++)
762                                 {
763                                         paramLI->kind = PARAM_NUM;
764                                         paramLI->id = k + 1;
765                                         paramLI->isnull = (Nulls && Nulls[k] == 'n');
766                                         paramLI->value = Values[k];
767                                 }
768                                 paramLI->kind = PARAM_INVALID;
769                         }
770                         else
771                                 state->es_param_list_info = NULL;
772                         res = _SPI_pquery(qdesc, state, (i < qlen - 1) ? 0 : tcount);
773                         if (res < 0 || i >= qlen - 1)
774                                 return (res);
775                         CommandCounterIncrement();
776                 }
777         }
778
779         return (res);
780
781 }
782
783 static int
784 _SPI_pquery(QueryDesc * queryDesc, EState * state, int tcount)
785 {
786         Query      *parseTree = queryDesc->parsetree;
787         Plan       *plan = queryDesc->plantree;
788         int                     operation = queryDesc->operation;
789         CommandDest     dest = queryDesc->dest;
790         TupleDesc       tupdesc;
791         bool            isRetrieveIntoPortal = false;
792         bool            isRetrieveIntoRelation = false;
793         char       *intoName = NULL;
794         int                     res;
795
796         switch (operation)
797         {
798                 case CMD_SELECT:
799                         res = SPI_OK_SELECT;
800                         if (parseTree->isPortal)
801                         {
802                                 isRetrieveIntoPortal = true;
803                                 intoName = parseTree->into;
804                                 parseTree->isBinary = false;    /* */
805
806                                 return (SPI_ERROR_CURSOR);
807
808                         }
809                         else if (parseTree->into != NULL)       /* select into table */
810                         {
811                                 res = SPI_OK_SELINTO;
812                                 isRetrieveIntoRelation = true;
813                                 queryDesc->dest = None;                 /* */
814                         }
815                         break;
816                 case CMD_INSERT:
817                         res = SPI_OK_INSERT;
818                         break;
819                 case CMD_DELETE:
820                         res = SPI_OK_DELETE;
821                         break;
822                 case CMD_UPDATE:
823                         res = SPI_OK_UPDATE;
824                         break;
825                 default:
826                         return (SPI_ERROR_OPUNKNOWN);
827         }
828
829         if (state == NULL)                      /* plan preparation */
830                 return (res);
831 #ifdef SPI_EXECUTOR_STATS
832         if (ShowExecutorStats)
833                 ResetUsage();
834 #endif
835         tupdesc = ExecutorStart(queryDesc, state);
836
837         /* Don't work currently */
838         if (isRetrieveIntoPortal)
839         {
840                 ProcessPortal(intoName,
841                                           parseTree,
842                                           plan,
843                                           state,
844                                           tupdesc,
845                                           None);
846                 return (SPI_OK_CURSOR);
847         }
848
849         ExecutorRun(queryDesc, state, EXEC_FOR, tcount);
850
851         _SPI_current->processed = state->es_processed;
852         if (operation == CMD_SELECT && queryDesc->dest == SPI)
853         {
854                 if (_SPI_checktuples())
855                         elog(FATAL, "SPI_select: # of processed tuples check failed");
856         }
857
858         ExecutorEnd(queryDesc, state);
859
860 #ifdef SPI_EXECUTOR_STATS
861         if (ShowExecutorStats)
862         {
863                 fprintf(stderr, "! Executor Stats:\n");
864                 ShowUsage();
865         }
866 #endif
867
868         if (dest == SPI)
869         {
870                 SPI_processed = _SPI_current->processed;
871                 SPI_tuptable = _SPI_current->tuptable;
872         }
873         queryDesc->dest = dest;
874
875         return (res);
876
877 }
878
879 #if 0
880 static void
881 _SPI_fetch(FetchStmt * stmt)
882 {
883         char       *name = stmt->portalname;
884         int                     feature = (stmt->direction == FORWARD) ? EXEC_FOR : EXEC_BACK;
885         int                     count = stmt->howMany;
886         Portal          portal;
887         QueryDesc  *queryDesc;
888         EState     *state;
889         MemoryContext context;
890
891         if (name == NULL)
892                 elog(FATAL, "SPI_fetch from blank portal unsupported");
893
894         portal = GetPortalByName(name);
895         if (!PortalIsValid(portal))
896                 elog(FATAL, "SPI_fetch: portal \"%s\" not found", name);
897
898         context = MemoryContextSwitchTo((MemoryContext) PortalGetHeapMemory(portal));
899
900         queryDesc = PortalGetQueryDesc(portal);
901         state = PortalGetState(portal);
902
903         ExecutorRun(queryDesc, state, feature, count);
904
905         MemoryContextSwitchTo(context);         /* switch to the normal Executor
906                                                                                  * context */
907
908         _SPI_current->processed = state->es_processed;
909         if (_SPI_checktuples())
910                 elog(FATAL, "SPI_fetch: # of processed tuples check failed");
911
912         SPI_processed = _SPI_current->processed;
913         SPI_tuptable = _SPI_current->tuptable;
914
915 }
916
917 #endif
918
919 static MemoryContext
920 _SPI_execmem()
921 {
922         MemoryContext oldcxt;
923         PortalHeapMemory phmem;
924
925         phmem = PortalGetHeapMemory(_SPI_current->portal);
926         oldcxt = MemoryContextSwitchTo((MemoryContext) phmem);
927
928         return (oldcxt);
929
930 }
931
932 static MemoryContext
933 _SPI_procmem()
934 {
935         MemoryContext oldcxt;
936         PortalVariableMemory pvmem;
937
938         pvmem = PortalGetVariableMemory(_SPI_current->portal);
939         oldcxt = MemoryContextSwitchTo((MemoryContext) pvmem);
940
941         return (oldcxt);
942
943 }
944
945 /*
946  * _SPI_begin_call --
947  *
948  */
949 static int
950 _SPI_begin_call(bool execmem)
951 {
952         if (_SPI_curid + 1 != _SPI_connected)
953                 return (SPI_ERROR_UNCONNECTED);
954         _SPI_curid++;
955         if (_SPI_current != &(_SPI_stack[_SPI_curid]))
956                 elog(FATAL, "SPI: stack corrupted");
957
958         if (execmem)                            /* switch to the Executor memory context */
959         {
960                 _SPI_execmem();
961                 StartPortalAllocMode(DefaultAllocMode, 0);
962         }
963
964         return (0);
965 }
966
967 static int
968 _SPI_end_call(bool procmem)
969 {
970
971         /*
972          * We' returning to procedure where _SPI_curid == _SPI_connected - 1
973          */
974         _SPI_curid--;
975
976         if (_SPI_current->qtlist)       /* free _SPI_plan allocations */
977         {
978                 free(_SPI_current->qtlist->qtrees);
979                 free(_SPI_current->qtlist);
980                 _SPI_current->qtlist = NULL;
981         }
982
983         if (procmem)                            /* switch to the procedure memory context */
984         {                                                       /* but free Executor memory before */
985                 EndPortalAllocMode();
986                 _SPI_procmem();
987         }
988
989         return (0);
990 }
991
992 static bool
993 _SPI_checktuples()
994 {
995         uint32          processed = _SPI_current->processed;
996         SPITupleTable *tuptable = _SPI_current->tuptable;
997         bool            failed = false;
998
999         if (processed == 0)
1000         {
1001                 if (tuptable != NULL)
1002                         failed = true;
1003         }
1004         else    /* some tuples were processed */
1005         {
1006                 if (tuptable == NULL)   /* spi_printtup was not called */
1007                         failed = true;
1008                 else if (processed != (tuptable->alloced - tuptable->free))
1009                         failed = true;
1010         }
1011
1012         return (failed);
1013 }
1014
1015 static _SPI_plan *
1016 _SPI_copy_plan(_SPI_plan * plan, int location)
1017 {
1018         _SPI_plan  *newplan;
1019         MemoryContext oldcxt = NULL;
1020         int                     i;
1021
1022         if (location == _SPI_CPLAN_PROCXT)
1023                 oldcxt = MemoryContextSwitchTo((MemoryContext)
1024                                                   PortalGetVariableMemory(_SPI_current->portal));
1025         else if (location == _SPI_CPLAN_TOPCXT)
1026                 oldcxt = MemoryContextSwitchTo(TopMemoryContext);
1027
1028         newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan));
1029         newplan->qtlist = (QueryTreeList *) palloc(sizeof(QueryTreeList));
1030         newplan->qtlist->len = plan->qtlist->len;
1031         newplan->qtlist->qtrees = (Query **) palloc(plan->qtlist->len *
1032                                                                                                 sizeof(Query *));
1033         for (i = 0; i < plan->qtlist->len; i++)
1034                 newplan->qtlist->qtrees[i] = (Query *)
1035                         copyObject(plan->qtlist->qtrees[i]);
1036
1037         newplan->ptlist = (List *) copyObject(plan->ptlist);
1038         newplan->nargs = plan->nargs;
1039         if (plan->nargs > 0)
1040         {
1041                 newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
1042                 memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
1043         }
1044         else
1045                 newplan->argtypes = NULL;
1046
1047         if (location != _SPI_CPLAN_CURCXT)
1048                 MemoryContextSwitchTo(oldcxt);
1049
1050         return (newplan);
1051 }