1 /*-------------------------------------------------------------------------
4 * Server Programming Interface
6 *-------------------------------------------------------------------------
8 #include "executor/spi.h"
9 #include "access/printtup.h"
14 QueryTreeList *qtlist; /* malloced */
15 uint32 processed; /* by Executor */
16 SPITupleTable *tuptable;
17 Portal portal; /* portal per procedure */
18 MemoryContext savedcxt;
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;
28 uint32 SPI_processed = 0;
29 SPITupleTable *SPI_tuptable;
32 void spi_printtup(HeapTuple tuple, TupleDesc tupdesc);
36 QueryTreeList *qtlist;
42 static int _SPI_execute(char *src, int tcount, _SPI_plan * plan);
43 static int _SPI_pquery(QueryDesc * queryDesc, EState * state, int tcount);
46 static void _SPI_fetch(FetchStmt * stmt);
50 _SPI_execute_plan(_SPI_plan * plan,
51 Datum * Values, char *Nulls, int tcount);
53 #define _SPI_CPLAN_CURCXT 0
54 #define _SPI_CPLAN_PROCXT 1
55 #define _SPI_CPLAN_TOPCXT 2
57 static _SPI_plan *_SPI_copy_plan(_SPI_plan * plan, int location);
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);
65 #ifdef SPI_EXECUTOR_STATS
66 extern int ShowExecutorStats;
67 extern void ResetUsage(void);
68 extern void ShowUsage(void);
76 PortalVariableMemory pvmem;
79 * It's possible on startup and after commit/abort. In future we'll
80 * catch commit/abort in some way...
82 strcpy(pname, "<SPI manager>");
83 _SPI_portal = GetPortalByName(pname);
84 if (!PortalIsValid(_SPI_portal))
86 if (_SPI_stack != NULL) /* there was abort */
88 _SPI_current = _SPI_stack = NULL;
89 _SPI_connected = _SPI_curid = -1;
92 _SPI_portal = CreatePortal(pname);
93 if (!PortalIsValid(_SPI_portal))
94 elog(FATAL, "SPI_connect: global initialization failed");
98 * When procedure called by Executor _SPI_curid expected to be equal
101 if (_SPI_curid != _SPI_connected)
102 return (SPI_ERROR_CONNECT);
104 if (_SPI_stack == NULL)
106 if (_SPI_connected != -1)
107 elog(FATAL, "SPI_connect: no connection(s) expected");
108 _SPI_stack = (_SPI_connection *) malloc(sizeof(_SPI_connection));
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));
119 * We' returning to procedure where _SPI_curid == _SPI_connected - 1
123 _SPI_current = &(_SPI_stack[_SPI_connected]);
124 _SPI_current->qtlist = NULL;
125 _SPI_current->processed = 0;
126 _SPI_current->tuptable = NULL;
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");
134 /* ... and switch to Portal' Variable memory - procedure' context */
135 pvmem = PortalGetVariableMemory(_SPI_current->portal);
136 _SPI_current->savedcxt = MemoryContextSwitchTo((MemoryContext) pvmem);
138 _SPI_current->savedId = GetScanCommandId();
139 SetScanCommandId(GetCurrentCommandId());
141 return (SPI_OK_CONNECT);
150 res = _SPI_begin_call(false); /* live in procedure memory */
154 /* Restore memory context as it was before procedure call */
155 MemoryContextSwitchTo(_SPI_current->savedcxt);
156 PortalDestroy(&(_SPI_current->portal));
158 SetScanCommandId(_SPI_current->savedId);
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.
167 if (_SPI_connected == -1)
174 _SPI_stack = (_SPI_connection *) realloc(_SPI_stack,
175 (_SPI_connected + 1) * sizeof(_SPI_connection));
176 _SPI_current = &(_SPI_stack[_SPI_connected]);
179 return (SPI_OK_FINISH);
184 SPI_exec(char *src, int tcount)
188 if (src == NULL || tcount < 0)
189 return (SPI_ERROR_ARGUMENT);
191 res = _SPI_begin_call(true);
195 res = _SPI_execute(src, tcount, NULL);
202 SPI_execp(void *plan, Datum * Values, char *Nulls, int tcount)
206 if (plan == NULL || tcount < 0)
207 return (SPI_ERROR_ARGUMENT);
209 if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL)
210 return (SPI_ERROR_PARAM);
212 res = _SPI_begin_call(true);
216 /* copy plan to current (executor) context */
217 plan = (void *) _SPI_copy_plan(plan, _SPI_CPLAN_CURCXT);
219 res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, tcount);
226 SPI_prepare(char *src, int nargs, Oid * argtypes)
230 if (nargs < 0 || (nargs > 0 && argtypes == NULL))
232 SPI_result = SPI_ERROR_ARGUMENT;
236 SPI_result = _SPI_begin_call(true);
240 plan = (_SPI_plan *) palloc(sizeof(_SPI_plan)); /* Executor context */
241 plan->argtypes = argtypes;
244 SPI_result = _SPI_execute(src, 0, plan);
246 if (SPI_result >= 0) /* copy plan to procedure context */
247 plan = _SPI_copy_plan(plan, _SPI_CPLAN_PROCXT);
253 return ((void *) plan);
258 SPI_saveplan(void *plan)
264 SPI_result = SPI_ERROR_ARGUMENT;
268 SPI_result = _SPI_begin_call(false); /* don't change context */
272 newplan = _SPI_copy_plan((_SPI_plan *) plan, _SPI_CPLAN_TOPCXT);
277 return ((void *) newplan);
282 SPI_copytuple(HeapTuple tuple)
284 MemoryContext oldcxt = NULL;
289 SPI_result = SPI_ERROR_ARGUMENT;
293 if (_SPI_curid + 1 == _SPI_connected) /* connected */
295 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
296 elog(FATAL, "SPI: stack corrupted");
297 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
300 ctuple = heap_copytuple(tuple);
303 MemoryContextSwitchTo(oldcxt);
309 SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
310 Datum * Values, char *Nulls)
312 MemoryContext oldcxt = NULL;
314 int numberOfAttributes;
321 if (rel == NULL || tuple == NULL || natts <= 0 || attnum == NULL || Values == NULL)
323 SPI_result = SPI_ERROR_ARGUMENT;
327 if (_SPI_curid + 1 == _SPI_connected) /* connected */
329 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
330 elog(FATAL, "SPI: stack corrupted");
331 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
334 numberOfAttributes = rel->rd_att->natts;
335 v = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
336 n = (char *) palloc(numberOfAttributes * sizeof(char));
338 /* fetch old values and nulls */
339 for (i = 0; i < numberOfAttributes; i++)
341 v[i] = heap_getattr(tuple, InvalidBuffer, i + 1, rel->rd_att, &isnull);
342 n[i] = (isnull) ? 'n' : ' ';
345 /* replace values and nulls */
346 for (i = 0; i < natts; i++)
348 if (attnum[i] <= 0 || attnum[i] > numberOfAttributes)
350 v[attnum[i] - 1] = Values[i];
351 n[attnum[i] - 1] = (Nulls && Nulls[i] == 'n') ? 'n' : ' ';
354 if (i == natts) /* no errors in attnum[] */
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;
366 SPI_result = SPI_ERROR_NOATTRIBUTE;
373 MemoryContextSwitchTo(oldcxt);
379 SPI_fnumber(TupleDesc tupdesc, char *fname)
383 for (res = 0; res < tupdesc->natts; res++)
385 if (strcasecmp(tupdesc->attrs[res]->attname.data, fname) == 0)
389 return (SPI_ERROR_NOATTRIBUTE);
393 SPI_fname(TupleDesc tupdesc, int fnumber)
397 if (tupdesc->natts < fnumber || fnumber <= 0)
399 SPI_result = SPI_ERROR_NOATTRIBUTE;
403 return (nameout(&(tupdesc->attrs[fnumber - 1]->attname)));
407 SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
414 if (tuple->t_natts < fnumber || fnumber <= 0)
417 val = heap_getattr(tuple, InvalidBuffer, fnumber, tupdesc, &isnull);
420 foutoid = typtoout((Oid) tupdesc->attrs[fnumber - 1]->atttypid);
421 if (!OidIsValid(foutoid))
423 SPI_result = SPI_ERROR_NOOUTFUNC;
427 return (fmgr(foutoid, val, gettypelem(tupdesc->attrs[fnumber - 1]->atttypid)));
431 SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool * isnull)
437 if (tuple->t_natts < fnumber || fnumber <= 0)
440 val = heap_getattr(tuple, InvalidBuffer, fnumber, tupdesc, isnull);
446 SPI_gettype(TupleDesc tupdesc, int fnumber)
451 if (tupdesc->natts < fnumber || fnumber <= 0)
453 SPI_result = SPI_ERROR_NOATTRIBUTE;
457 typeTuple = SearchSysCacheTuple(TYPOID,
458 ObjectIdGetDatum(tupdesc->attrs[fnumber - 1]->atttypid),
461 if (!HeapTupleIsValid(typeTuple))
463 SPI_result = SPI_ERROR_TYPUNKNOWN;
467 return (pstrdup(((TypeTupleForm) GETSTRUCT(typeTuple))->typname.data));
471 SPI_gettypeid(TupleDesc tupdesc, int fnumber)
475 if (tupdesc->natts < fnumber || fnumber <= 0)
477 SPI_result = SPI_ERROR_NOATTRIBUTE;
481 return (tupdesc->attrs[fnumber - 1]->atttypid);
485 SPI_getrelname(Relation rel)
487 return (pstrdup(rel->rd_rel->relname.data));
492 * store tuple retrieved by Executor into SPITupleTable
493 * of current SPI procedure
497 spi_printtup(HeapTuple tuple, TupleDesc tupdesc)
499 SPITupleTable *tuptable;
500 MemoryContext oldcxt;
503 * When called by Executor _SPI_curid expected to be equal to
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");
511 oldcxt = _SPI_procmem(); /* switch to procedure memory context */
513 tuptable = _SPI_current->tuptable;
514 if (tuptable == NULL)
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);
522 else if (tuptable->free == 0)
524 tuptable->free = 256;
525 tuptable->alloced += tuptable->free;
526 tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
527 tuptable->alloced * sizeof(HeapTuple));
530 tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple);
533 MemoryContextSwitchTo(oldcxt);
542 _SPI_execute(char *src, int tcount, _SPI_plan * plan)
544 QueryTreeList *queryTree_list;
553 Oid *argtypes = NULL;
557 /* Increment CommandCounter to see changes made by now */
558 CommandCounterIncrement();
562 _SPI_current->tuptable = NULL;
563 _SPI_current->qtlist = NULL;
568 argtypes = plan->argtypes;
570 ptlist = planTree_list = (List *)
571 pg_plan(src, argtypes, nargs, &queryTree_list, None);
573 _SPI_current->qtlist = queryTree_list;
575 qlen = queryTree_list->len;
578 queryTree = (Query *) (queryTree_list->qtrees[i]);
579 planTree = lfirst(planTree_list);
581 planTree_list = lnext(planTree_list);
583 if (queryTree->commandType == CMD_UTILITY)
585 if (nodeTag(queryTree->utilityStmt) == T_CopyStmt)
587 CopyStmt *stmt = (CopyStmt *) (queryTree->utilityStmt);
589 if (stmt->filename == NULL)
590 return (SPI_ERROR_COPY);
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;
600 ProcessUtility(queryTree->utilityStmt, None);
602 CommandCounterIncrement();
606 else if (i >= qlen - 1)
609 else if (plan == NULL)
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)
617 CommandCounterIncrement();
621 qdesc = CreateQueryDesc(queryTree, planTree,
622 (i < qlen - 1) ? None : SPI);
623 res = _SPI_pquery(qdesc, NULL, (i < qlen - 1) ? 0 : tcount);
631 plan->qtlist = queryTree_list;
632 plan->ptlist = ptlist;
639 _SPI_execute_plan(_SPI_plan * plan, Datum * Values, char *Nulls, int tcount)
641 QueryTreeList *queryTree_list = plan->qtlist;
642 List *planTree_list = plan->ptlist;
647 int nargs = plan->nargs;
648 int qlen = queryTree_list->len;
653 /* Increment CommandCounter to see changes made by now */
654 CommandCounterIncrement();
658 _SPI_current->tuptable = NULL;
659 _SPI_current->qtlist = NULL;
663 queryTree = (Query *) (queryTree_list->qtrees[i]);
664 planTree = lfirst(planTree_list);
666 planTree_list = lnext(planTree_list);
668 if (queryTree->commandType == CMD_UTILITY)
670 ProcessUtility(queryTree->utilityStmt, None);
672 CommandCounterIncrement();
674 return (SPI_OK_UTILITY);
678 qdesc = CreateQueryDesc(queryTree, planTree,
679 (i < qlen - 1) ? None : SPI);
680 state = CreateExecutorState();
683 ParamListInfo paramLI = (ParamListInfo) palloc((nargs + 1) *
684 sizeof(ParamListInfoData));
686 state->es_param_list_info = paramLI;
687 for (k = 0; k < plan->nargs; paramLI++, k++)
689 paramLI->kind = PARAM_NUM;
691 paramLI->isnull = (Nulls && Nulls[k] == 'n');
692 paramLI->value = Values[k];
694 paramLI->kind = PARAM_INVALID;
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)
701 CommandCounterIncrement();
710 _SPI_pquery(QueryDesc * queryDesc, EState * state, int tcount)
716 bool isRetrieveIntoPortal = false;
717 bool isRetrieveIntoRelation = false;
718 char *intoName = NULL;
721 parseTree = queryDesc->parsetree;
722 plan = queryDesc->plantree;
723 operation = queryDesc->operation;
729 if (parseTree->isPortal)
731 isRetrieveIntoPortal = true;
732 intoName = parseTree->into;
733 parseTree->isBinary = false; /* */
735 return (SPI_ERROR_CURSOR);
738 else if (parseTree->into != NULL) /* select into table */
740 res = SPI_OK_SELINTO;
741 isRetrieveIntoRelation = true;
754 return (SPI_ERROR_OPUNKNOWN);
757 if (state == NULL) /* plan preparation */
759 #ifdef SPI_EXECUTOR_STATS
760 if (ShowExecutorStats)
763 tupdesc = ExecutorStart(queryDesc, state);
765 /* Don't work currently */
766 if (isRetrieveIntoPortal)
768 ProcessPortal(intoName,
774 return (SPI_OK_CURSOR);
777 ExecutorRun(queryDesc, state, EXEC_FOR, tcount);
779 _SPI_current->processed = state->es_processed;
780 if (operation == CMD_SELECT && queryDesc->dest == SPI)
782 if (_SPI_checktuples(isRetrieveIntoRelation))
783 elog(FATAL, "SPI_select: # of processed tuples check failed");
786 ExecutorEnd(queryDesc, state);
788 #ifdef SPI_EXECUTOR_STATS
789 if (ShowExecutorStats)
791 fprintf(stderr, "! Executor Stats:\n");
796 if (queryDesc->dest == SPI)
798 SPI_processed = _SPI_current->processed;
799 SPI_tuptable = _SPI_current->tuptable;
808 _SPI_fetch(FetchStmt * stmt)
810 char *name = stmt->portalname;
811 int feature = (stmt->direction == FORWARD) ? EXEC_FOR : EXEC_BACK;
812 int count = stmt->howMany;
814 QueryDesc *queryDesc;
816 MemoryContext context;
819 elog(FATAL, "SPI_fetch from blank portal unsupported");
821 portal = GetPortalByName(name);
822 if (!PortalIsValid(portal))
823 elog(FATAL, "SPI_fetch: portal \"%s\" not found", name);
825 context = MemoryContextSwitchTo((MemoryContext) PortalGetHeapMemory(portal));
827 queryDesc = PortalGetQueryDesc(portal);
828 state = PortalGetState(portal);
830 ExecutorRun(queryDesc, state, feature, count);
832 MemoryContextSwitchTo(context); /* switch to the normal Executor
835 _SPI_current->processed = state->es_processed;
836 if (_SPI_checktuples(false))
837 elog(FATAL, "SPI_fetch: # of processed tuples check failed");
839 SPI_processed = _SPI_current->processed;
840 SPI_tuptable = _SPI_current->tuptable;
849 MemoryContext oldcxt;
850 PortalHeapMemory phmem;
852 phmem = PortalGetHeapMemory(_SPI_current->portal);
853 oldcxt = MemoryContextSwitchTo((MemoryContext) phmem);
862 MemoryContext oldcxt;
863 PortalVariableMemory pvmem;
865 pvmem = PortalGetVariableMemory(_SPI_current->portal);
866 oldcxt = MemoryContextSwitchTo((MemoryContext) pvmem);
877 _SPI_begin_call(bool execmem)
879 if (_SPI_curid + 1 != _SPI_connected)
880 return (SPI_ERROR_UNCONNECTED);
882 if (_SPI_current != &(_SPI_stack[_SPI_curid]))
883 elog(FATAL, "SPI: stack corrupted");
885 if (execmem) /* switch to the Executor memory context */
888 StartPortalAllocMode(DefaultAllocMode, 0);
895 _SPI_end_call(bool procmem)
899 * We' returning to procedure where _SPI_curid == _SPI_connected - 1
903 if (_SPI_current->qtlist) /* free _SPI_plan allocations */
905 free(_SPI_current->qtlist->qtrees);
906 free(_SPI_current->qtlist);
907 _SPI_current->qtlist = NULL;
910 if (procmem) /* switch to the procedure memory context */
911 { /* but free Executor memory before */
912 EndPortalAllocMode();
920 _SPI_checktuples(bool isRetrieveIntoRelation)
922 uint32 processed = _SPI_current->processed;
923 SPITupleTable *tuptable = _SPI_current->tuptable;
928 if (tuptable != NULL)
932 /* some tuples were processed */
934 if (tuptable == NULL) /* spi_printtup was not called */
936 if (!isRetrieveIntoRelation)
939 else if (isRetrieveIntoRelation)
941 else if (processed != (tuptable->alloced - tuptable->free))
949 _SPI_copy_plan(_SPI_plan * plan, int location)
952 MemoryContext oldcxt = NULL;
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);
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 *
966 for (i = 0; i < plan->qtlist->len; i++)
967 newplan->qtlist->qtrees[i] = (Query *)
968 copyObject(plan->qtlist->qtrees[i]);
970 newplan->ptlist = (List *) copyObject(plan->ptlist);
971 newplan->nargs = plan->nargs;
974 newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
975 memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
978 newplan->argtypes = NULL;
980 if (location != _SPI_CPLAN_CURCXT)
981 MemoryContextSwitchTo(oldcxt);