1 /*-------------------------------------------------------------------------
4 * Server Programming Interface
6 * Portions Copyright (c) 1996-2002, 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.94 2003/05/02 20:54:34 tgl Exp $
13 *-------------------------------------------------------------------------
17 #include "access/printtup.h"
18 #include "catalog/heap.h"
19 #include "executor/spi_priv.h"
20 #include "tcop/tcopprot.h"
21 #include "utils/lsyscache.h"
24 uint32 SPI_processed = 0;
25 Oid SPI_lastoid = InvalidOid;
26 SPITupleTable *SPI_tuptable = NULL;
29 static _SPI_connection *_SPI_stack = NULL;
30 static _SPI_connection *_SPI_current = NULL;
31 static int _SPI_connected = -1;
32 static int _SPI_curid = -1;
34 static int _SPI_execute(const char *src, int tcount, _SPI_plan *plan);
35 static int _SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount);
37 static int _SPI_execute_plan(_SPI_plan *plan,
38 Datum *Values, const char *Nulls, int tcount);
40 static void _SPI_cursor_operation(Portal portal, bool forward, int count,
43 static _SPI_plan *_SPI_copy_plan(_SPI_plan *plan, int location);
45 static int _SPI_begin_call(bool execmem);
46 static int _SPI_end_call(bool procmem);
47 static MemoryContext _SPI_execmem(void);
48 static MemoryContext _SPI_procmem(void);
49 static bool _SPI_checktuples(void);
52 /* =================== interface functions =================== */
57 _SPI_connection *new_SPI_stack;
60 * When procedure called by Executor _SPI_curid expected to be equal
63 if (_SPI_curid != _SPI_connected)
64 return SPI_ERROR_CONNECT;
66 if (_SPI_stack == NULL)
68 if (_SPI_connected != -1)
69 elog(FATAL, "SPI_connect: no connection(s) expected");
70 new_SPI_stack = (_SPI_connection *) malloc(sizeof(_SPI_connection));
74 if (_SPI_connected <= -1)
75 elog(FATAL, "SPI_connect: some connection(s) expected");
76 new_SPI_stack = (_SPI_connection *) realloc(_SPI_stack,
77 (_SPI_connected + 2) * sizeof(_SPI_connection));
80 if (new_SPI_stack == NULL)
81 elog(ERROR, "Memory exhausted in SPI_connect");
84 * We' returning to procedure where _SPI_curid == _SPI_connected - 1
86 _SPI_stack = new_SPI_stack;
89 _SPI_current = &(_SPI_stack[_SPI_connected]);
90 _SPI_current->processed = 0;
91 _SPI_current->tuptable = NULL;
94 * Create memory contexts for this procedure
96 * XXX it would be better to use PortalContext as the parent context,
97 * but we may not be inside a portal (consider deferred-trigger
100 _SPI_current->procCxt = AllocSetContextCreate(TopTransactionContext,
102 ALLOCSET_DEFAULT_MINSIZE,
103 ALLOCSET_DEFAULT_INITSIZE,
104 ALLOCSET_DEFAULT_MAXSIZE);
105 _SPI_current->execCxt = AllocSetContextCreate(TopTransactionContext,
107 ALLOCSET_DEFAULT_MINSIZE,
108 ALLOCSET_DEFAULT_INITSIZE,
109 ALLOCSET_DEFAULT_MAXSIZE);
110 /* ... and switch to procedure's context */
111 _SPI_current->savedcxt = MemoryContextSwitchTo(_SPI_current->procCxt);
113 return SPI_OK_CONNECT;
121 res = _SPI_begin_call(false); /* live in procedure memory */
125 /* Restore memory context as it was before procedure call */
126 MemoryContextSwitchTo(_SPI_current->savedcxt);
128 /* Release memory used in procedure call */
129 MemoryContextDelete(_SPI_current->execCxt);
130 MemoryContextDelete(_SPI_current->procCxt);
133 * Reset result variables, especially SPI_tuptable which is probably
134 * pointing at a just-deleted tuptable
137 SPI_lastoid = InvalidOid;
141 * After _SPI_begin_call _SPI_connected == _SPI_curid. Now we are
142 * closing connection to SPI and returning to upper Executor and so
143 * _SPI_connected must be equal to _SPI_curid.
147 if (_SPI_connected == -1)
155 _SPI_connection *new_SPI_stack;
157 new_SPI_stack = (_SPI_connection *) realloc(_SPI_stack,
158 (_SPI_connected + 1) * sizeof(_SPI_connection));
159 /* This could only fail with a pretty stupid malloc package ... */
160 if (new_SPI_stack == NULL)
161 elog(ERROR, "Memory exhausted in SPI_finish");
162 _SPI_stack = new_SPI_stack;
163 _SPI_current = &(_SPI_stack[_SPI_connected]);
166 return SPI_OK_FINISH;
171 * Clean up SPI state at transaction commit or abort (we don't care which).
177 * Note that memory contexts belonging to SPI stack entries will be
178 * freed automatically, so we can ignore them here. We just need to
179 * restore our static variables to initial state.
181 if (_SPI_stack != NULL) /* there was abort */
183 _SPI_current = _SPI_stack = NULL;
184 _SPI_connected = _SPI_curid = -1;
186 SPI_lastoid = InvalidOid;
203 SPI_exec(const char *src, int tcount)
207 if (src == NULL || tcount < 0)
208 return SPI_ERROR_ARGUMENT;
210 res = _SPI_begin_call(true);
214 res = _SPI_execute(src, tcount, NULL);
221 SPI_execp(void *plan, Datum *Values, const char *Nulls, int tcount)
225 if (plan == NULL || tcount < 0)
226 return SPI_ERROR_ARGUMENT;
228 if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL)
229 return SPI_ERROR_PARAM;
231 res = _SPI_begin_call(true);
235 res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, tcount);
242 SPI_prepare(const 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;
273 SPI_saveplan(void *plan)
279 SPI_result = SPI_ERROR_ARGUMENT;
283 SPI_result = _SPI_begin_call(false); /* don't change context */
287 newplan = _SPI_copy_plan((_SPI_plan *) plan, _SPI_CPLAN_TOPCXT);
292 return (void *) newplan;
297 SPI_freeplan(void *plan)
299 _SPI_plan *spiplan = (_SPI_plan *) plan;
302 return SPI_ERROR_ARGUMENT;
304 MemoryContextDelete(spiplan->plancxt);
309 SPI_copytuple(HeapTuple tuple)
311 MemoryContext oldcxt = NULL;
316 SPI_result = SPI_ERROR_ARGUMENT;
320 if (_SPI_curid + 1 == _SPI_connected) /* connected */
322 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
323 elog(FATAL, "SPI: stack corrupted");
324 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
327 ctuple = heap_copytuple(tuple);
330 MemoryContextSwitchTo(oldcxt);
336 SPI_copytupledesc(TupleDesc tupdesc)
338 MemoryContext oldcxt = NULL;
343 SPI_result = SPI_ERROR_ARGUMENT;
347 if (_SPI_curid + 1 == _SPI_connected) /* connected */
349 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
350 elog(FATAL, "SPI: stack corrupted");
351 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
354 ctupdesc = CreateTupleDescCopy(tupdesc);
357 MemoryContextSwitchTo(oldcxt);
363 SPI_copytupleintoslot(HeapTuple tuple, TupleDesc tupdesc)
365 MemoryContext oldcxt = NULL;
366 TupleTableSlot *cslot;
370 if (tuple == NULL || tupdesc == NULL)
372 SPI_result = SPI_ERROR_ARGUMENT;
376 if (_SPI_curid + 1 == _SPI_connected) /* connected */
378 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
379 elog(FATAL, "SPI: stack corrupted");
380 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
383 ctuple = heap_copytuple(tuple);
384 ctupdesc = CreateTupleDescCopy(tupdesc);
386 cslot = MakeTupleTableSlot();
387 ExecSetSlotDescriptor(cslot, ctupdesc, true);
388 cslot = ExecStoreTuple(ctuple, cslot, InvalidBuffer, true);
391 MemoryContextSwitchTo(oldcxt);
397 SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
398 Datum *Values, const char *Nulls)
400 MemoryContext oldcxt = NULL;
402 int numberOfAttributes;
408 if (rel == NULL || tuple == NULL || natts <= 0 || attnum == NULL || Values == NULL)
410 SPI_result = SPI_ERROR_ARGUMENT;
414 if (_SPI_curid + 1 == _SPI_connected) /* connected */
416 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
417 elog(FATAL, "SPI: stack corrupted");
418 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
421 numberOfAttributes = rel->rd_att->natts;
422 v = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
423 n = (char *) palloc(numberOfAttributes * sizeof(char));
425 /* fetch old values and nulls */
426 for (i = 0; i < numberOfAttributes; i++)
428 v[i] = heap_getattr(tuple, i + 1, rel->rd_att, &isnull);
429 n[i] = (isnull) ? 'n' : ' ';
432 /* replace values and nulls */
433 for (i = 0; i < natts; i++)
435 if (attnum[i] <= 0 || attnum[i] > numberOfAttributes)
437 v[attnum[i] - 1] = Values[i];
438 n[attnum[i] - 1] = (Nulls && Nulls[i] == 'n') ? 'n' : ' ';
441 if (i == natts) /* no errors in *attnum */
443 mtuple = heap_formtuple(rel->rd_att, v, n);
446 * copy the identification info of the old tuple: t_ctid, t_self,
449 mtuple->t_data->t_ctid = tuple->t_data->t_ctid;
450 mtuple->t_self = tuple->t_self;
451 mtuple->t_tableOid = tuple->t_tableOid;
452 if (rel->rd_rel->relhasoids)
453 HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple));
458 SPI_result = SPI_ERROR_NOATTRIBUTE;
465 MemoryContextSwitchTo(oldcxt);
471 SPI_fnumber(TupleDesc tupdesc, const char *fname)
474 Form_pg_attribute sysatt;
476 for (res = 0; res < tupdesc->natts; res++)
478 if (namestrcmp(&tupdesc->attrs[res]->attname, fname) == 0)
482 sysatt = SystemAttributeByName(fname, true /* "oid" will be accepted */ );
484 return sysatt->attnum;
486 /* SPI_ERROR_NOATTRIBUTE is different from all sys column numbers */
487 return SPI_ERROR_NOATTRIBUTE;
491 SPI_fname(TupleDesc tupdesc, int fnumber)
493 Form_pg_attribute att;
497 if (fnumber > tupdesc->natts || fnumber == 0 ||
498 fnumber <= FirstLowInvalidHeapAttributeNumber)
500 SPI_result = SPI_ERROR_NOATTRIBUTE;
505 att = tupdesc->attrs[fnumber - 1];
507 att = SystemAttributeDefinition(fnumber, true);
509 return pstrdup(NameStr(att->attname));
513 SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
527 if (fnumber > tuple->t_data->t_natts || fnumber == 0 ||
528 fnumber <= FirstLowInvalidHeapAttributeNumber)
530 SPI_result = SPI_ERROR_NOATTRIBUTE;
534 origval = heap_getattr(tuple, fnumber, tupdesc, &isnull);
540 typoid = tupdesc->attrs[fnumber - 1]->atttypid;
541 typmod = tupdesc->attrs[fnumber - 1]->atttypmod;
545 typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
549 if (!getTypeOutputInfo(typoid, &foutoid, &typelem, &typisvarlena))
551 SPI_result = SPI_ERROR_NOOUTFUNC;
556 * If we have a toasted datum, forcibly detoast it here to avoid
557 * memory leakage inside the type's output routine.
560 val = PointerGetDatum(PG_DETOAST_DATUM(origval));
564 result = OidFunctionCall3(foutoid,
566 ObjectIdGetDatum(typelem),
567 Int32GetDatum(typmod));
569 /* Clean up detoasted copy, if any */
571 pfree(DatumGetPointer(val));
573 return DatumGetCString(result);
577 SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
581 if (fnumber > tuple->t_data->t_natts || fnumber == 0 ||
582 fnumber <= FirstLowInvalidHeapAttributeNumber)
584 SPI_result = SPI_ERROR_NOATTRIBUTE;
589 return heap_getattr(tuple, fnumber, tupdesc, isnull);
593 SPI_gettype(TupleDesc tupdesc, int fnumber)
601 if (fnumber > tupdesc->natts || fnumber == 0 ||
602 fnumber <= FirstLowInvalidHeapAttributeNumber)
604 SPI_result = SPI_ERROR_NOATTRIBUTE;
609 typoid = tupdesc->attrs[fnumber - 1]->atttypid;
611 typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
613 typeTuple = SearchSysCache(TYPEOID,
614 ObjectIdGetDatum(typoid),
617 if (!HeapTupleIsValid(typeTuple))
619 SPI_result = SPI_ERROR_TYPUNKNOWN;
623 result = pstrdup(NameStr(((Form_pg_type) GETSTRUCT(typeTuple))->typname));
624 ReleaseSysCache(typeTuple);
629 SPI_gettypeid(TupleDesc tupdesc, int fnumber)
633 if (fnumber > tupdesc->natts || fnumber == 0 ||
634 fnumber <= FirstLowInvalidHeapAttributeNumber)
636 SPI_result = SPI_ERROR_NOATTRIBUTE;
641 return tupdesc->attrs[fnumber - 1]->atttypid;
643 return (SystemAttributeDefinition(fnumber, true))->atttypid;
647 SPI_getrelname(Relation rel)
649 return pstrdup(RelationGetRelationName(rel));
653 SPI_palloc(Size size)
655 MemoryContext oldcxt = NULL;
658 if (_SPI_curid + 1 == _SPI_connected) /* connected */
660 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
661 elog(FATAL, "SPI: stack corrupted");
662 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
665 pointer = palloc(size);
668 MemoryContextSwitchTo(oldcxt);
674 SPI_repalloc(void *pointer, Size size)
676 /* No longer need to worry which context chunk was in... */
677 return repalloc(pointer, size);
681 SPI_pfree(void *pointer)
683 /* No longer need to worry which context chunk was in... */
688 SPI_freetuple(HeapTuple tuple)
690 /* No longer need to worry which context tuple was in... */
691 heap_freetuple(tuple);
695 SPI_freetuptable(SPITupleTable *tuptable)
697 if (tuptable != NULL)
698 MemoryContextDelete(tuptable->tuptabcxt);
706 * Open a prepared SPI plan as a portal
709 SPI_cursor_open(const char *name, void *plan, Datum *Values, const char *Nulls)
711 _SPI_plan *spiplan = (_SPI_plan *) plan;
712 List *qtlist = spiplan->qtlist;
713 List *ptlist = spiplan->ptlist;
716 ParamListInfo paramLI;
717 MemoryContext oldcontext;
721 /* Ensure that the plan contains only one regular SELECT query */
722 if (length(ptlist) != 1 || length(qtlist) != 1)
723 elog(ERROR, "cannot open multi-query plan as cursor");
724 queryTree = (Query *) lfirst((List *) lfirst(qtlist));
725 planTree = (Plan *) lfirst(ptlist);
727 if (queryTree->commandType != CMD_SELECT)
728 elog(ERROR, "plan in SPI_cursor_open() is not a SELECT");
729 if (queryTree->into != NULL)
730 elog(ERROR, "plan in SPI_cursor_open() must NOT be a SELECT INTO");
732 /* Increment CommandCounter to see changes made by now */
733 CommandCounterIncrement();
735 /* Reset SPI result */
738 _SPI_current->processed = 0;
739 _SPI_current->tuptable = NULL;
741 /* Create the portal */
742 if (name == NULL || name[0] == '\0')
744 /* Use a random nonconflicting name */
745 portal = CreateNewPortal();
749 /* In this path, error if portal of same name already exists */
750 portal = CreatePortal(name, false, false);
753 /* Switch to portals memory and copy the parsetree and plan to there */
754 oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
755 queryTree = copyObject(queryTree);
756 planTree = copyObject(planTree);
758 /* If the plan has parameters, set them up */
759 if (spiplan->nargs > 0)
761 paramLI = (ParamListInfo) palloc0((spiplan->nargs + 1) *
762 sizeof(ParamListInfoData));
764 for (k = 0; k < spiplan->nargs; k++)
766 paramLI[k].kind = PARAM_NUM;
767 paramLI[k].id = k + 1;
768 paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
769 if (paramLI[k].isnull)
771 /* nulls just copy */
772 paramLI[k].value = Values[k];
776 /* pass-by-ref values must be copied into portal context */
780 get_typlenbyval(spiplan->argtypes[k],
781 ¶mTypLen, ¶mTypByVal);
782 paramLI[k].value = datumCopy(Values[k],
783 paramTypByVal, paramTypLen);
786 paramLI[k].kind = PARAM_INVALID;
794 PortalDefineQuery(portal,
795 NULL, /* unfortunately don't have sourceText */
796 "SELECT", /* cursor's query is always a SELECT */
797 makeList1(queryTree),
799 PortalGetHeapMemory(portal));
801 MemoryContextSwitchTo(oldcontext);
804 * Set up options for portal.
806 portal->cursorOptions &= ~(CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL);
807 if (ExecSupportsBackwardScan(plan))
808 portal->cursorOptions |= CURSOR_OPT_SCROLL;
810 portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
813 * Start portal execution.
815 PortalStart(portal, paramLI);
817 Assert(portal->strategy == PORTAL_ONE_SELECT);
819 /* Return the created portal */
827 * Find the portal of an existing open cursor
830 SPI_cursor_find(const char *name)
832 return GetPortalByName(name);
839 * Fetch rows in a cursor
842 SPI_cursor_fetch(Portal portal, bool forward, int count)
844 _SPI_cursor_operation(portal, forward, count, SPI);
854 SPI_cursor_move(Portal portal, bool forward, int count)
856 _SPI_cursor_operation(portal, forward, count, None);
866 SPI_cursor_close(Portal portal)
868 if (!PortalIsValid(portal))
869 elog(ERROR, "invalid portal in SPI cursor operation");
871 PortalDrop(portal, false);
874 /* =================== private functions =================== */
878 * Initialize to receive tuples from Executor into SPITupleTable
879 * of current SPI procedure
882 spi_dest_setup(DestReceiver *self, int operation,
883 const char *portalName, TupleDesc typeinfo)
885 SPITupleTable *tuptable;
886 MemoryContext oldcxt;
887 MemoryContext tuptabcxt;
890 * When called by Executor _SPI_curid expected to be equal to
893 if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
894 elog(FATAL, "SPI: improper call to spi_dest_setup");
895 if (_SPI_current != &(_SPI_stack[_SPI_curid]))
896 elog(FATAL, "SPI: stack corrupted in spi_dest_setup");
898 if (_SPI_current->tuptable != NULL)
899 elog(FATAL, "SPI: improper call to spi_dest_setup");
901 oldcxt = _SPI_procmem(); /* switch to procedure memory context */
903 tuptabcxt = AllocSetContextCreate(CurrentMemoryContext,
905 ALLOCSET_DEFAULT_MINSIZE,
906 ALLOCSET_DEFAULT_INITSIZE,
907 ALLOCSET_DEFAULT_MAXSIZE);
908 MemoryContextSwitchTo(tuptabcxt);
910 _SPI_current->tuptable = tuptable = (SPITupleTable *)
911 palloc(sizeof(SPITupleTable));
912 tuptable->tuptabcxt = tuptabcxt;
913 tuptable->alloced = tuptable->free = 128;
914 tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
915 tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
917 MemoryContextSwitchTo(oldcxt);
922 * store tuple retrieved by Executor into SPITupleTable
923 * of current SPI procedure
926 spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
928 SPITupleTable *tuptable;
929 MemoryContext oldcxt;
932 * When called by Executor _SPI_curid expected to be equal to
935 if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
936 elog(FATAL, "SPI: improper call to spi_printtup");
937 if (_SPI_current != &(_SPI_stack[_SPI_curid]))
938 elog(FATAL, "SPI: stack corrupted in spi_printtup");
940 tuptable = _SPI_current->tuptable;
941 if (tuptable == NULL)
942 elog(FATAL, "SPI: improper call to spi_printtup");
944 oldcxt = MemoryContextSwitchTo(tuptable->tuptabcxt);
946 if (tuptable->free == 0)
948 tuptable->free = 256;
949 tuptable->alloced += tuptable->free;
950 tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
951 tuptable->alloced * sizeof(HeapTuple));
954 tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple);
957 MemoryContextSwitchTo(oldcxt);
965 * Plan and optionally execute a querystring.
967 * If plan != NULL, just prepare plan tree, else execute immediately.
970 _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
972 List *raw_parsetree_list;
973 List *query_list_list;
977 Oid *argtypes = NULL;
983 argtypes = plan->argtypes;
986 /* Increment CommandCounter to see changes made by now */
987 CommandCounterIncrement();
989 /* Reset state (only needed in case string is empty) */
991 SPI_lastoid = InvalidOid;
993 _SPI_current->tuptable = NULL;
996 * Parse the request string into a list of raw parse trees.
998 raw_parsetree_list = pg_parse_query(src);
1001 * Do parse analysis and rule rewrite for each raw parsetree.
1003 * We save the querytrees from each raw parsetree as a separate
1004 * sublist. This allows _SPI_execute_plan() to know where the
1005 * boundaries between original queries fall.
1007 query_list_list = NIL;
1010 foreach(list_item, raw_parsetree_list)
1012 Node *parsetree = (Node *) lfirst(list_item);
1014 List *query_list_item;
1016 query_list = pg_analyze_and_rewrite(parsetree, argtypes, nargs);
1018 query_list_list = lappend(query_list_list, query_list);
1020 /* Reset state for each original parsetree */
1021 /* (at most one of its querytrees will be marked canSetTag) */
1023 SPI_lastoid = InvalidOid;
1024 SPI_tuptable = NULL;
1025 _SPI_current->tuptable = NULL;
1027 foreach(query_list_item, query_list)
1029 Query *queryTree = (Query *) lfirst(query_list_item);
1033 planTree = pg_plan_query(queryTree);
1034 plan_list = lappend(plan_list, planTree);
1036 if (queryTree->commandType == CMD_UTILITY)
1038 if (IsA(queryTree->utilityStmt, CopyStmt))
1040 CopyStmt *stmt = (CopyStmt *) queryTree->utilityStmt;
1042 if (stmt->filename == NULL)
1043 return SPI_ERROR_COPY;
1045 else if (IsA(queryTree->utilityStmt, DeclareCursorStmt) ||
1046 IsA(queryTree->utilityStmt, ClosePortalStmt) ||
1047 IsA(queryTree->utilityStmt, FetchStmt))
1048 return SPI_ERROR_CURSOR;
1049 else if (IsA(queryTree->utilityStmt, TransactionStmt))
1050 return SPI_ERROR_TRANSACTION;
1051 res = SPI_OK_UTILITY;
1054 ProcessUtility(queryTree->utilityStmt, None, NULL);
1055 CommandCounterIncrement();
1058 else if (plan == NULL)
1060 qdesc = CreateQueryDesc(queryTree, planTree,
1061 queryTree->canSetTag ? SPI : None,
1063 res = _SPI_pquery(qdesc, true,
1064 queryTree->canSetTag ? tcount : 0);
1067 CommandCounterIncrement();
1071 qdesc = CreateQueryDesc(queryTree, planTree,
1072 queryTree->canSetTag ? SPI : None,
1074 res = _SPI_pquery(qdesc, false, 0);
1083 plan->qtlist = query_list_list;
1084 plan->ptlist = plan_list;
1091 _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
1094 List *query_list_list = plan->qtlist;
1095 List *plan_list = plan->ptlist;
1096 List *query_list_list_item;
1097 int nargs = plan->nargs;
1099 ParamListInfo paramLI;
1101 /* Increment CommandCounter to see changes made by now */
1102 CommandCounterIncrement();
1104 /* Convert parameters to form wanted by executor */
1109 paramLI = (ParamListInfo)
1110 palloc0((nargs + 1) * sizeof(ParamListInfoData));
1112 for (k = 0; k < nargs; k++)
1114 paramLI[k].kind = PARAM_NUM;
1115 paramLI[k].id = k + 1;
1116 paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
1117 paramLI[k].value = Values[k];
1119 paramLI[k].kind = PARAM_INVALID;
1124 /* Reset state (only needed in case string is empty) */
1126 SPI_lastoid = InvalidOid;
1127 SPI_tuptable = NULL;
1128 _SPI_current->tuptable = NULL;
1130 foreach(query_list_list_item, query_list_list)
1132 List *query_list = lfirst(query_list_list_item);
1133 List *query_list_item;
1135 /* Reset state for each original parsetree */
1136 /* (at most one of its querytrees will be marked canSetTag) */
1138 SPI_lastoid = InvalidOid;
1139 SPI_tuptable = NULL;
1140 _SPI_current->tuptable = NULL;
1142 foreach(query_list_item, query_list)
1144 Query *queryTree = (Query *) lfirst(query_list_item);
1148 planTree = lfirst(plan_list);
1149 plan_list = lnext(plan_list);
1151 if (queryTree->commandType == CMD_UTILITY)
1153 ProcessUtility(queryTree->utilityStmt, None, NULL);
1154 res = SPI_OK_UTILITY;
1155 CommandCounterIncrement();
1159 qdesc = CreateQueryDesc(queryTree, planTree,
1160 queryTree->canSetTag ? SPI : None,
1161 NULL, paramLI, false);
1162 res = _SPI_pquery(qdesc, true,
1163 queryTree->canSetTag ? tcount : 0);
1166 CommandCounterIncrement();
1175 _SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount)
1177 int operation = queryDesc->operation;
1184 res = SPI_OK_SELECT;
1185 if (queryDesc->parsetree->into != NULL) /* select into table */
1187 res = SPI_OK_SELINTO;
1188 queryDesc->dest = None; /* don't output results anywhere */
1192 res = SPI_OK_INSERT;
1195 res = SPI_OK_DELETE;
1198 res = SPI_OK_UPDATE;
1201 return SPI_ERROR_OPUNKNOWN;
1204 if (!runit) /* plan preparation, don't execute */
1207 #ifdef SPI_EXECUTOR_STATS
1208 if (ShowExecutorStats)
1212 ExecutorStart(queryDesc);
1214 ExecutorRun(queryDesc, ForwardScanDirection, (long) tcount);
1216 _SPI_current->processed = queryDesc->estate->es_processed;
1217 save_lastoid = queryDesc->estate->es_lastoid;
1219 if (operation == CMD_SELECT && queryDesc->dest == SPI)
1221 if (_SPI_checktuples())
1222 elog(FATAL, "SPI_select: # of processed tuples check failed");
1225 if (queryDesc->dest == SPI)
1227 SPI_processed = _SPI_current->processed;
1228 SPI_lastoid = save_lastoid;
1229 SPI_tuptable = _SPI_current->tuptable;
1231 else if (res == SPI_OK_SELECT)
1233 /* Don't return SPI_OK_SELECT if we discarded the result */
1234 res = SPI_OK_UTILITY;
1237 ExecutorEnd(queryDesc);
1239 FreeQueryDesc(queryDesc);
1241 #ifdef SPI_EXECUTOR_STATS
1242 if (ShowExecutorStats)
1243 ShowUsage("SPI EXECUTOR STATS");
1250 * _SPI_cursor_operation()
1252 * Do a FETCH or MOVE in a cursor
1255 _SPI_cursor_operation(Portal portal, bool forward, int count,
1258 /* Check that the portal is valid */
1259 if (!PortalIsValid(portal))
1260 elog(ERROR, "invalid portal in SPI cursor operation");
1262 /* Push the SPI stack */
1263 _SPI_begin_call(true);
1265 /* Reset the SPI result */
1267 SPI_tuptable = NULL;
1268 _SPI_current->processed = 0;
1269 _SPI_current->tuptable = NULL;
1271 /* Run the cursor */
1272 _SPI_current->processed =
1273 PortalRunFetch(portal,
1274 forward ? FETCH_FORWARD : FETCH_BACKWARD,
1278 if (dest == SPI && _SPI_checktuples())
1279 elog(FATAL, "SPI_fetch: # of processed tuples check failed");
1281 /* Put the result into place for access by caller */
1282 SPI_processed = _SPI_current->processed;
1283 SPI_tuptable = _SPI_current->tuptable;
1285 /* Pop the SPI stack */
1286 _SPI_end_call(true);
1290 static MemoryContext
1293 return MemoryContextSwitchTo(_SPI_current->execCxt);
1296 static MemoryContext
1299 return MemoryContextSwitchTo(_SPI_current->procCxt);
1307 _SPI_begin_call(bool execmem)
1309 if (_SPI_curid + 1 != _SPI_connected)
1310 return SPI_ERROR_UNCONNECTED;
1312 if (_SPI_current != &(_SPI_stack[_SPI_curid]))
1313 elog(FATAL, "SPI: stack corrupted");
1315 if (execmem) /* switch to the Executor memory context */
1322 _SPI_end_call(bool procmem)
1325 * We're returning to procedure where _SPI_curid == _SPI_connected - 1
1329 if (procmem) /* switch to the procedure memory context */
1332 /* and free Executor memory */
1333 MemoryContextResetAndDeleteChildren(_SPI_current->execCxt);
1340 _SPI_checktuples(void)
1342 uint32 processed = _SPI_current->processed;
1343 SPITupleTable *tuptable = _SPI_current->tuptable;
1344 bool failed = false;
1346 if (tuptable == NULL) /* spi_dest_setup was not called */
1348 else if (processed != (tuptable->alloced - tuptable->free))
1355 _SPI_copy_plan(_SPI_plan *plan, int location)
1358 MemoryContext oldcxt;
1359 MemoryContext plancxt;
1360 MemoryContext parentcxt;
1362 /* Determine correct parent for the plan's memory context */
1363 if (location == _SPI_CPLAN_PROCXT)
1364 parentcxt = _SPI_current->procCxt;
1365 else if (location == _SPI_CPLAN_TOPCXT)
1366 parentcxt = TopMemoryContext;
1367 else /* (this case not currently used) */
1368 parentcxt = CurrentMemoryContext;
1371 * Create a memory context for the plan. We don't expect the plan to
1372 * be very large, so use smaller-than-default alloc parameters.
1374 plancxt = AllocSetContextCreate(parentcxt,
1376 ALLOCSET_SMALL_MINSIZE,
1377 ALLOCSET_SMALL_INITSIZE,
1378 ALLOCSET_SMALL_MAXSIZE);
1379 oldcxt = MemoryContextSwitchTo(plancxt);
1381 /* Copy the SPI plan into its own context */
1382 newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan));
1383 newplan->plancxt = plancxt;
1384 newplan->qtlist = (List *) copyObject(plan->qtlist);
1385 newplan->ptlist = (List *) copyObject(plan->ptlist);
1386 newplan->nargs = plan->nargs;
1387 if (plan->nargs > 0)
1389 newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
1390 memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
1393 newplan->argtypes = NULL;
1395 MemoryContextSwitchTo(oldcxt);