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