1 /*-------------------------------------------------------------------------
4 * Server Programming Interface
6 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.57 2001/08/02 18:08:43 tgl Exp $
13 *-------------------------------------------------------------------------
17 #include "access/printtup.h"
18 #include "commands/command.h"
19 #include "executor/spi_priv.h"
22 uint32 SPI_processed = 0;
23 Oid SPI_lastoid = InvalidOid;
24 SPITupleTable *SPI_tuptable = NULL;
27 static _SPI_connection *_SPI_stack = NULL;
28 static _SPI_connection *_SPI_current = NULL;
29 static int _SPI_connected = -1;
30 static int _SPI_curid = -1;
32 static int _SPI_execute(char *src, int tcount, _SPI_plan *plan);
33 static int _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount);
35 static int _SPI_execute_plan(_SPI_plan *plan,
36 Datum *Values, char *Nulls, int tcount);
38 static void _SPI_cursor_operation(Portal portal, bool forward, int count,
41 static _SPI_plan *_SPI_copy_plan(_SPI_plan *plan, int location);
43 static int _SPI_begin_call(bool execmem);
44 static int _SPI_end_call(bool procmem);
45 static MemoryContext _SPI_execmem(void);
46 static MemoryContext _SPI_procmem(void);
47 static bool _SPI_checktuples(void);
49 #ifdef SPI_EXECUTOR_STATS
50 extern int ShowExecutorStats;
51 extern void ResetUsage(void);
52 extern void ShowUsage(void);
56 /* =================== interface functions =================== */
61 _SPI_connection *new_SPI_stack;
64 * When procedure called by Executor _SPI_curid expected to be equal
67 if (_SPI_curid != _SPI_connected)
68 return SPI_ERROR_CONNECT;
70 if (_SPI_stack == NULL)
72 if (_SPI_connected != -1)
73 elog(FATAL, "SPI_connect: no connection(s) expected");
74 new_SPI_stack = (_SPI_connection *) malloc(sizeof(_SPI_connection));
78 if (_SPI_connected <= -1)
79 elog(FATAL, "SPI_connect: some connection(s) expected");
80 new_SPI_stack = (_SPI_connection *) realloc(_SPI_stack,
81 (_SPI_connected + 2) * sizeof(_SPI_connection));
84 if (new_SPI_stack == NULL)
85 elog(ERROR, "Memory exhausted in SPI_connect");
88 * We' returning to procedure where _SPI_curid == _SPI_connected - 1
90 _SPI_stack = new_SPI_stack;
93 _SPI_current = &(_SPI_stack[_SPI_connected]);
94 _SPI_current->qtlist = NULL;
95 _SPI_current->processed = 0;
96 _SPI_current->tuptable = NULL;
98 /* Create memory contexts for this procedure */
99 _SPI_current->procCxt = AllocSetContextCreate(TopTransactionContext,
101 ALLOCSET_DEFAULT_MINSIZE,
102 ALLOCSET_DEFAULT_INITSIZE,
103 ALLOCSET_DEFAULT_MAXSIZE);
104 _SPI_current->execCxt = AllocSetContextCreate(TopTransactionContext,
106 ALLOCSET_DEFAULT_MINSIZE,
107 ALLOCSET_DEFAULT_INITSIZE,
108 ALLOCSET_DEFAULT_MAXSIZE);
109 /* ... and switch to procedure's context */
110 _SPI_current->savedcxt = MemoryContextSwitchTo(_SPI_current->procCxt);
112 _SPI_current->savedId = GetScanCommandId();
113 SetScanCommandId(GetCurrentCommandId());
115 return SPI_OK_CONNECT;
124 res = _SPI_begin_call(false); /* live in procedure memory */
128 /* Restore memory context as it was before procedure call */
129 MemoryContextSwitchTo(_SPI_current->savedcxt);
131 /* Release memory used in procedure call */
132 MemoryContextDelete(_SPI_current->execCxt);
133 MemoryContextDelete(_SPI_current->procCxt);
135 SetScanCommandId(_SPI_current->savedId);
138 * After _SPI_begin_call _SPI_connected == _SPI_curid. Now we are
139 * closing connection to SPI and returning to upper Executor and so
140 * _SPI_connected must be equal to _SPI_curid.
144 if (_SPI_connected == -1)
152 _SPI_connection *new_SPI_stack;
154 new_SPI_stack = (_SPI_connection *) realloc(_SPI_stack,
155 (_SPI_connected + 1) * sizeof(_SPI_connection));
156 /* This could only fail with a pretty stupid malloc package ... */
157 if (new_SPI_stack == NULL)
158 elog(ERROR, "Memory exhausted in SPI_finish");
159 _SPI_stack = new_SPI_stack;
160 _SPI_current = &(_SPI_stack[_SPI_connected]);
163 return SPI_OK_FINISH;
168 * Clean up SPI state at transaction commit or abort (we don't care which).
174 * Note that memory contexts belonging to SPI stack entries will be
175 * freed automatically, so we can ignore them here. We just need to
176 * restore our static variables to initial state.
178 if (_SPI_stack != NULL) /* there was abort */
180 _SPI_current = _SPI_stack = NULL;
181 _SPI_connected = _SPI_curid = -1;
183 SPI_lastoid = InvalidOid;
200 SPI_exec(char *src, int tcount)
204 if (src == NULL || tcount < 0)
205 return SPI_ERROR_ARGUMENT;
207 res = _SPI_begin_call(true);
211 res = _SPI_execute(src, tcount, NULL);
218 SPI_execp(void *plan, Datum *Values, char *Nulls, int tcount)
222 if (plan == NULL || tcount < 0)
223 return SPI_ERROR_ARGUMENT;
225 if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL)
226 return SPI_ERROR_PARAM;
228 res = _SPI_begin_call(true);
232 /* copy plan to current (executor) context */
233 plan = (void *) _SPI_copy_plan(plan, _SPI_CPLAN_CURCXT);
235 res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, tcount);
242 SPI_prepare(char *src, int nargs, Oid *argtypes)
246 if (src == NULL || nargs < 0 || (nargs > 0 && argtypes == NULL))
248 SPI_result = SPI_ERROR_ARGUMENT;
252 SPI_result = _SPI_begin_call(true);
256 plan = (_SPI_plan *) palloc(sizeof(_SPI_plan)); /* Executor context */
257 plan->argtypes = argtypes;
260 SPI_result = _SPI_execute(src, 0, plan);
262 if (SPI_result >= 0) /* copy plan to procedure context */
263 plan = _SPI_copy_plan(plan, _SPI_CPLAN_PROCXT);
269 return (void *) plan;
274 SPI_saveplan(void *plan)
280 SPI_result = SPI_ERROR_ARGUMENT;
284 SPI_result = _SPI_begin_call(false); /* don't change context */
288 newplan = _SPI_copy_plan((_SPI_plan *) plan, _SPI_CPLAN_TOPCXT);
293 return (void *) newplan;
298 SPI_freeplan(void *plan)
300 _SPI_plan *spiplan = (_SPI_plan *)plan;
303 return SPI_ERROR_ARGUMENT;
305 MemoryContextDelete(spiplan->plancxt);
310 SPI_copytuple(HeapTuple tuple)
312 MemoryContext oldcxt = NULL;
317 SPI_result = SPI_ERROR_ARGUMENT;
321 if (_SPI_curid + 1 == _SPI_connected) /* connected */
323 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
324 elog(FATAL, "SPI: stack corrupted");
325 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
328 ctuple = heap_copytuple(tuple);
331 MemoryContextSwitchTo(oldcxt);
337 SPI_copytupledesc(TupleDesc tupdesc)
339 MemoryContext oldcxt = NULL;
344 SPI_result = SPI_ERROR_ARGUMENT;
348 if (_SPI_curid + 1 == _SPI_connected) /* connected */
350 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
351 elog(FATAL, "SPI: stack corrupted");
352 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
355 ctupdesc = CreateTupleDescCopy(tupdesc);
358 MemoryContextSwitchTo(oldcxt);
364 SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
365 Datum *Values, char *Nulls)
367 MemoryContext oldcxt = NULL;
369 int numberOfAttributes;
376 if (rel == NULL || tuple == NULL || natts <= 0 || attnum == NULL || Values == NULL)
378 SPI_result = SPI_ERROR_ARGUMENT;
382 if (_SPI_curid + 1 == _SPI_connected) /* connected */
384 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
385 elog(FATAL, "SPI: stack corrupted");
386 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
389 numberOfAttributes = rel->rd_att->natts;
390 v = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
391 n = (char *) palloc(numberOfAttributes * sizeof(char));
393 /* fetch old values and nulls */
394 for (i = 0; i < numberOfAttributes; i++)
396 v[i] = heap_getattr(tuple, i + 1, rel->rd_att, &isnull);
397 n[i] = (isnull) ? 'n' : ' ';
400 /* replace values and nulls */
401 for (i = 0; i < natts; i++)
403 if (attnum[i] <= 0 || attnum[i] > numberOfAttributes)
405 v[attnum[i] - 1] = Values[i];
406 n[attnum[i] - 1] = (Nulls && Nulls[i] == 'n') ? 'n' : ' ';
409 if (i == natts) /* no errors in *attnum */
411 mtuple = heap_formtuple(rel->rd_att, v, n);
412 infomask = mtuple->t_data->t_infomask;
413 memmove(&(mtuple->t_data->t_oid), &(tuple->t_data->t_oid),
414 ((char *) &(tuple->t_data->t_hoff) -
415 (char *) &(tuple->t_data->t_oid)));
416 mtuple->t_data->t_infomask = infomask;
417 mtuple->t_data->t_natts = numberOfAttributes;
422 SPI_result = SPI_ERROR_NOATTRIBUTE;
429 MemoryContextSwitchTo(oldcxt);
435 SPI_fnumber(TupleDesc tupdesc, char *fname)
439 for (res = 0; res < tupdesc->natts; res++)
441 if (strcasecmp(NameStr(tupdesc->attrs[res]->attname), fname) == 0)
445 return SPI_ERROR_NOATTRIBUTE;
449 SPI_fname(TupleDesc tupdesc, int fnumber)
453 if (tupdesc->natts < fnumber || fnumber <= 0)
455 SPI_result = SPI_ERROR_NOATTRIBUTE;
459 return pstrdup(NameStr(tupdesc->attrs[fnumber - 1]->attname));
463 SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
474 if (tuple->t_data->t_natts < fnumber || fnumber <= 0)
476 SPI_result = SPI_ERROR_NOATTRIBUTE;
480 origval = heap_getattr(tuple, fnumber, tupdesc, &isnull);
483 if (!getTypeOutputInfo(tupdesc->attrs[fnumber - 1]->atttypid,
484 &foutoid, &typelem, &typisvarlena))
486 SPI_result = SPI_ERROR_NOOUTFUNC;
491 * If we have a toasted datum, forcibly detoast it here to avoid
492 * memory leakage inside the type's output routine.
495 val = PointerGetDatum(PG_DETOAST_DATUM(origval));
499 result = OidFunctionCall3(foutoid,
501 ObjectIdGetDatum(typelem),
502 Int32GetDatum(tupdesc->attrs[fnumber - 1]->atttypmod));
504 /* Clean up detoasted copy, if any */
506 pfree(DatumGetPointer(val));
508 return DatumGetCString(result);
512 SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
518 if (tuple->t_data->t_natts < fnumber || fnumber <= 0)
520 SPI_result = SPI_ERROR_NOATTRIBUTE;
524 val = heap_getattr(tuple, fnumber, tupdesc, isnull);
530 SPI_gettype(TupleDesc tupdesc, int fnumber)
536 if (tupdesc->natts < fnumber || fnumber <= 0)
538 SPI_result = SPI_ERROR_NOATTRIBUTE;
542 typeTuple = SearchSysCache(TYPEOID,
543 ObjectIdGetDatum(tupdesc->attrs[fnumber - 1]->atttypid),
546 if (!HeapTupleIsValid(typeTuple))
548 SPI_result = SPI_ERROR_TYPUNKNOWN;
552 result = pstrdup(NameStr(((Form_pg_type) GETSTRUCT(typeTuple))->typname));
553 ReleaseSysCache(typeTuple);
558 SPI_gettypeid(TupleDesc tupdesc, int fnumber)
562 if (tupdesc->natts < fnumber || fnumber <= 0)
564 SPI_result = SPI_ERROR_NOATTRIBUTE;
568 return tupdesc->attrs[fnumber - 1]->atttypid;
572 SPI_getrelname(Relation rel)
574 return pstrdup(RelationGetRelationName(rel));
578 SPI_palloc(Size size)
580 MemoryContext oldcxt = NULL;
583 if (_SPI_curid + 1 == _SPI_connected) /* connected */
585 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
586 elog(FATAL, "SPI: stack corrupted");
587 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
590 pointer = palloc(size);
593 MemoryContextSwitchTo(oldcxt);
599 SPI_repalloc(void *pointer, Size size)
601 /* No longer need to worry which context chunk was in... */
602 return repalloc(pointer, size);
606 SPI_pfree(void *pointer)
608 /* No longer need to worry which context chunk was in... */
613 SPI_freetuple(HeapTuple tuple)
615 /* No longer need to worry which context tuple was in... */
616 heap_freetuple(tuple);
620 SPI_freetuptable(SPITupleTable *tuptable)
622 if (tuptable != NULL)
623 MemoryContextDelete(tuptable->tuptabcxt);
631 * Open a prepared SPI plan as a portal
634 SPI_cursor_open(char *name, void *plan, Datum *Values, char *Nulls)
636 static int unnamed_portal_count = 0;
638 _SPI_plan *spiplan = (_SPI_plan *)plan;
639 List *qtlist = spiplan->qtlist;
640 List *ptlist = spiplan->ptlist;
643 QueryDesc *queryDesc;
646 MemoryContext oldcontext;
651 /* Ensure that the plan contains only one regular SELECT query */
652 if (length(ptlist) != 1)
653 elog(ERROR, "cannot open multi-query plan as cursor");
654 queryTree = (Query *)lfirst(qtlist);
655 planTree = (Plan *)lfirst(ptlist);
657 if (queryTree->commandType != CMD_SELECT)
658 elog(ERROR, "plan in SPI_cursor_open() is not a SELECT");
659 if (queryTree->isPortal)
660 elog(ERROR, "plan in SPI_cursor_open() must NOT be a DECLARE already");
661 else if (queryTree->into != NULL)
662 elog(ERROR, "plan in SPI_cursor_open() must NOT be a SELECT INTO");
664 /* Reset SPI result */
667 _SPI_current->processed = 0;
668 _SPI_current->tuptable = NULL;
670 /* Make up a portal name if none given */
675 unnamed_portal_count++;
676 if (unnamed_portal_count < 0)
677 unnamed_portal_count = 0;
678 sprintf(portalname, "<unnamed cursor %d>", unnamed_portal_count);
679 if (GetPortalByName(portalname) == NULL)
686 /* Ensure the portal doesn't exist already */
687 portal = GetPortalByName(name);
689 elog(ERROR, "cursor \"%s\" already in use", name);
691 /* Create the portal */
692 portal = CreatePortal(name);
694 elog(ERROR, "failed to create portal \"%s\"", name);
696 /* Switch to portals memory and copy the parsetree and plan to there */
697 oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
698 queryTree = copyObject(queryTree);
699 planTree = copyObject(planTree);
701 /* Modify the parsetree to be a cursor */
702 queryTree->isPortal = true;
703 queryTree->into = pstrdup(name);
704 queryTree->isBinary = false;
706 /* Create the QueryDesc object and the executor state */
707 queryDesc = CreateQueryDesc(queryTree, planTree, SPI);
708 eState = CreateExecutorState();
710 /* If the plan has parameters, put them into the executor state */
711 if (spiplan->nargs > 0)
713 ParamListInfo paramLI = (ParamListInfo) palloc((spiplan->nargs + 1) *
714 sizeof(ParamListInfoData));
715 eState->es_param_list_info = paramLI;
716 for (k = 0; k < spiplan->nargs; paramLI++, k++)
718 paramLI->kind = PARAM_NUM;
720 paramLI->isnull = (Nulls && Nulls[k] == 'n');
721 paramLI->value = Values[k];
723 paramLI->kind = PARAM_INVALID;
726 eState->es_param_list_info = NULL;
728 /* Start the executor */
729 attinfo = ExecutorStart(queryDesc, eState);
731 /* Put all the objects into the portal */
732 PortalSetQuery(portal, queryDesc, attinfo, eState, PortalCleanup);
734 /* Switch back to the callers memory context */
735 MemoryContextSwitchTo(oldcontext);
737 /* Return the created portal */
745 * Find the portal of an existing open cursor
748 SPI_cursor_find(char *name)
750 return GetPortalByName(name);
757 * Fetch rows in a cursor
760 SPI_cursor_fetch(Portal portal, bool forward, int count)
762 _SPI_cursor_operation(portal, forward, count, SPI);
772 SPI_cursor_move(Portal portal, bool forward, int count)
774 _SPI_cursor_operation(portal, forward, count, None);
784 SPI_cursor_close(Portal portal)
786 Portal my_portal = portal;
788 if (!PortalIsValid(my_portal))
789 elog(ERROR, "invalid portal in SPI cursor operation");
791 PortalDrop(&my_portal);
794 /* =================== private functions =================== */
798 * store tuple retrieved by Executor into SPITupleTable
799 * of current SPI procedure
803 spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
805 SPITupleTable *tuptable;
806 MemoryContext oldcxt;
807 MemoryContext tuptabcxt;
810 * When called by Executor _SPI_curid expected to be equal to
813 if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
814 elog(FATAL, "SPI: improper call to spi_printtup");
815 if (_SPI_current != &(_SPI_stack[_SPI_curid]))
816 elog(FATAL, "SPI: stack corrupted in spi_printtup");
818 oldcxt = _SPI_procmem(); /* switch to procedure memory context */
820 tuptable = _SPI_current->tuptable;
821 if (tuptable == NULL)
823 tuptabcxt = AllocSetContextCreate(CurrentMemoryContext,
825 ALLOCSET_DEFAULT_MINSIZE,
826 ALLOCSET_DEFAULT_INITSIZE,
827 ALLOCSET_DEFAULT_MAXSIZE);
828 MemoryContextSwitchTo(tuptabcxt);
830 _SPI_current->tuptable = tuptable = (SPITupleTable *)
831 palloc(sizeof(SPITupleTable));
832 tuptable->tuptabcxt = tuptabcxt;
833 tuptable->alloced = tuptable->free = 128;
834 tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
835 tuptable->tupdesc = CreateTupleDescCopy(tupdesc);
839 MemoryContextSwitchTo(tuptable->tuptabcxt);
841 if (tuptable->free == 0)
843 tuptable->free = 256;
844 tuptable->alloced += tuptable->free;
845 tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
846 tuptable->alloced * sizeof(HeapTuple));
850 tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple);
853 MemoryContextSwitchTo(oldcxt);
862 _SPI_execute(char *src, int tcount, _SPI_plan *plan)
864 List *queryTree_list;
866 List *queryTree_list_item;
872 Oid *argtypes = NULL;
876 /* Increment CommandCounter to see changes made by now */
877 CommandCounterIncrement();
880 SPI_lastoid = InvalidOid;
882 _SPI_current->tuptable = NULL;
883 _SPI_current->qtlist = NULL;
888 argtypes = plan->argtypes;
891 queryTree_list = pg_parse_and_rewrite(src, argtypes, nargs);
893 _SPI_current->qtlist = queryTree_list;
897 foreach(queryTree_list_item, queryTree_list)
899 queryTree = (Query *) lfirst(queryTree_list_item);
900 islastquery = (lnext(queryTree_list_item) == NIL);
902 planTree = pg_plan_query(queryTree);
903 planTree_list = lappend(planTree_list, planTree);
905 if (queryTree->commandType == CMD_UTILITY)
907 if (nodeTag(queryTree->utilityStmt) == T_CopyStmt)
909 CopyStmt *stmt = (CopyStmt *) (queryTree->utilityStmt);
911 if (stmt->filename == NULL)
912 return SPI_ERROR_COPY;
914 else if (nodeTag(queryTree->utilityStmt) == T_ClosePortalStmt ||
915 nodeTag(queryTree->utilityStmt) == T_FetchStmt)
916 return SPI_ERROR_CURSOR;
917 else if (nodeTag(queryTree->utilityStmt) == T_TransactionStmt)
918 return SPI_ERROR_TRANSACTION;
919 res = SPI_OK_UTILITY;
922 ProcessUtility(queryTree->utilityStmt, None);
924 CommandCounterIncrement();
928 else if (islastquery)
931 else if (plan == NULL)
933 qdesc = CreateQueryDesc(queryTree, planTree,
934 islastquery ? SPI : None);
935 state = CreateExecutorState();
936 res = _SPI_pquery(qdesc, state, islastquery ? tcount : 0);
937 if (res < 0 || islastquery)
939 CommandCounterIncrement();
943 qdesc = CreateQueryDesc(queryTree, planTree,
944 islastquery ? SPI : None);
945 res = _SPI_pquery(qdesc, NULL, islastquery ? tcount : 0);
955 plan->qtlist = queryTree_list;
956 plan->ptlist = planTree_list;
963 _SPI_execute_plan(_SPI_plan *plan, Datum *Values, char *Nulls, int tcount)
965 List *queryTree_list = plan->qtlist;
966 List *planTree_list = plan->ptlist;
967 List *queryTree_list_item;
972 int nargs = plan->nargs;
977 /* Increment CommandCounter to see changes made by now */
978 CommandCounterIncrement();
981 SPI_lastoid = InvalidOid;
983 _SPI_current->tuptable = NULL;
984 _SPI_current->qtlist = NULL;
986 foreach(queryTree_list_item, queryTree_list)
988 queryTree = (Query *) lfirst(queryTree_list_item);
989 planTree = lfirst(planTree_list);
990 planTree_list = lnext(planTree_list);
991 islastquery = (planTree_list == NIL); /* assume lists are same
994 if (queryTree->commandType == CMD_UTILITY)
996 ProcessUtility(queryTree->utilityStmt, None);
998 CommandCounterIncrement();
1000 return SPI_OK_UTILITY;
1004 qdesc = CreateQueryDesc(queryTree, planTree,
1005 islastquery ? SPI : None);
1006 state = CreateExecutorState();
1009 ParamListInfo paramLI = (ParamListInfo) palloc((nargs + 1) *
1010 sizeof(ParamListInfoData));
1012 state->es_param_list_info = paramLI;
1013 for (k = 0; k < plan->nargs; paramLI++, k++)
1015 paramLI->kind = PARAM_NUM;
1016 paramLI->id = k + 1;
1017 paramLI->isnull = (Nulls && Nulls[k] == 'n');
1018 paramLI->value = Values[k];
1020 paramLI->kind = PARAM_INVALID;
1023 state->es_param_list_info = NULL;
1024 res = _SPI_pquery(qdesc, state, islastquery ? tcount : 0);
1025 if (res < 0 || islastquery)
1027 CommandCounterIncrement();
1035 _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
1037 Query *parseTree = queryDesc->parsetree;
1038 int operation = queryDesc->operation;
1039 CommandDest dest = queryDesc->dest;
1041 bool isRetrieveIntoPortal = false;
1042 bool isRetrieveIntoRelation = false;
1043 char *intoName = NULL;
1050 res = SPI_OK_SELECT;
1051 if (parseTree->isPortal)
1053 isRetrieveIntoPortal = true;
1054 intoName = parseTree->into;
1055 parseTree->isBinary = false; /* */
1057 return SPI_ERROR_CURSOR;
1060 else if (parseTree->into != NULL) /* select into table */
1062 res = SPI_OK_SELINTO;
1063 isRetrieveIntoRelation = true;
1064 queryDesc->dest = None; /* */
1068 res = SPI_OK_INSERT;
1071 res = SPI_OK_DELETE;
1074 res = SPI_OK_UPDATE;
1077 return SPI_ERROR_OPUNKNOWN;
1080 if (state == NULL) /* plan preparation */
1082 #ifdef SPI_EXECUTOR_STATS
1083 if (ShowExecutorStats)
1086 tupdesc = ExecutorStart(queryDesc, state);
1089 * Don't work currently --- need to rearrange callers so that we
1090 * prepare the portal before doing CreateExecutorState() etc. See
1091 * pquery.c for the correct order of operations.
1093 if (isRetrieveIntoPortal)
1094 elog(FATAL, "SPI_select: retrieve into portal not implemented");
1096 ExecutorRun(queryDesc, state, EXEC_FOR, (long) tcount);
1098 _SPI_current->processed = state->es_processed;
1099 save_lastoid = state->es_lastoid;
1101 if (operation == CMD_SELECT && queryDesc->dest == SPI)
1103 if (_SPI_checktuples())
1104 elog(FATAL, "SPI_select: # of processed tuples check failed");
1107 ExecutorEnd(queryDesc, state);
1109 #ifdef SPI_EXECUTOR_STATS
1110 if (ShowExecutorStats)
1112 fprintf(stderr, "! Executor Stats:\n");
1119 SPI_processed = _SPI_current->processed;
1120 SPI_lastoid = save_lastoid;
1121 SPI_tuptable = _SPI_current->tuptable;
1123 queryDesc->dest = dest;
1130 * _SPI_cursor_operation()
1132 * Do a FETCH or MOVE in a cursor
1135 _SPI_cursor_operation(Portal portal, bool forward, int count,
1138 QueryDesc *querydesc;
1140 MemoryContext oldcontext;
1141 CommandDest olddest;
1143 /* Check that the portal is valid */
1144 if (!PortalIsValid(portal))
1145 elog(ERROR, "invalid portal in SPI cursor operation");
1147 /* Push the SPI stack */
1148 _SPI_begin_call(true);
1150 /* Reset the SPI result */
1152 SPI_tuptable = NULL;
1153 _SPI_current->processed = 0;
1154 _SPI_current->tuptable = NULL;
1156 /* Switch to the portals memory context */
1157 oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
1158 querydesc = PortalGetQueryDesc(portal);
1159 estate = PortalGetState(portal);
1161 /* Save the queries command destination and set it to SPI (for fetch) */
1162 /* or None (for move) */
1163 olddest = querydesc->dest;
1164 querydesc->dest = dest;
1166 /* Run the executor like PerformPortalFetch and remember states */
1171 ExecutorRun(querydesc, estate, EXEC_FOR, (long)count);
1172 _SPI_current->processed = estate->es_processed;
1173 if (estate->es_processed > 0)
1174 portal->atStart = false;
1175 if (count <= 0 || (int) estate->es_processed < count)
1176 portal->atEnd = true;
1181 if (!portal->atStart)
1183 ExecutorRun(querydesc, estate, EXEC_BACK, (long) count);
1184 _SPI_current->processed = estate->es_processed;
1185 if (estate->es_processed > 0)
1186 portal->atEnd = false;
1187 if (count <= 0 || estate->es_processed < count)
1188 portal->atStart = true;
1192 /* Restore the old command destination and switch back to callers */
1193 /* memory context */
1194 querydesc->dest = olddest;
1195 MemoryContextSwitchTo(oldcontext);
1197 if (dest == SPI && _SPI_checktuples())
1198 elog(FATAL, "SPI_fetch: # of processed tuples check failed");
1200 /* Put the result into place for access by caller */
1201 SPI_processed = _SPI_current->processed;
1202 SPI_tuptable = _SPI_current->tuptable;
1204 /* Pop the SPI stack */
1205 _SPI_end_call(true);
1209 static MemoryContext
1212 return MemoryContextSwitchTo(_SPI_current->execCxt);
1215 static MemoryContext
1218 return MemoryContextSwitchTo(_SPI_current->procCxt);
1226 _SPI_begin_call(bool execmem)
1228 if (_SPI_curid + 1 != _SPI_connected)
1229 return SPI_ERROR_UNCONNECTED;
1231 if (_SPI_current != &(_SPI_stack[_SPI_curid]))
1232 elog(FATAL, "SPI: stack corrupted");
1234 if (execmem) /* switch to the Executor memory context */
1241 _SPI_end_call(bool procmem)
1245 * We' returning to procedure where _SPI_curid == _SPI_connected - 1
1249 _SPI_current->qtlist = NULL;
1251 if (procmem) /* switch to the procedure memory context */
1254 /* and free Executor memory */
1255 MemoryContextResetAndDeleteChildren(_SPI_current->execCxt);
1262 _SPI_checktuples(void)
1264 uint32 processed = _SPI_current->processed;
1265 SPITupleTable *tuptable = _SPI_current->tuptable;
1266 bool failed = false;
1270 if (tuptable != NULL)
1275 /* some tuples were processed */
1276 if (tuptable == NULL) /* spi_printtup was not called */
1278 else if (processed != (tuptable->alloced - tuptable->free))
1286 _SPI_copy_plan(_SPI_plan *plan, int location)
1289 MemoryContext oldcxt;
1290 MemoryContext plancxt;
1291 MemoryContext parentcxt;
1293 /* Determine correct parent for the plan's memory context */
1294 if (location == _SPI_CPLAN_PROCXT)
1295 parentcxt = _SPI_current->procCxt;
1296 else if (location == _SPI_CPLAN_TOPCXT)
1297 parentcxt = TopMemoryContext;
1299 parentcxt = CurrentMemoryContext;
1302 * Create a memory context for the plan. We don't expect the plan to
1303 * be very large, so use smaller-than-default alloc parameters.
1305 plancxt = AllocSetContextCreate(parentcxt,
1309 ALLOCSET_DEFAULT_MAXSIZE);
1310 oldcxt = MemoryContextSwitchTo(plancxt);
1312 /* Copy the SPI plan into its own context */
1313 newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan));
1314 newplan->plancxt = plancxt;
1315 newplan->qtlist = (List *) copyObject(plan->qtlist);
1316 newplan->ptlist = (List *) copyObject(plan->ptlist);
1317 newplan->nargs = plan->nargs;
1318 if (plan->nargs > 0)
1320 newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
1321 memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
1324 newplan->argtypes = NULL;
1326 MemoryContextSwitchTo(oldcxt);