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.96 2003/05/06 20:26:27 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, CreateDestReceiver(SPI));
845 /* we know that the SPI receiver doesn't need a destroy call */
855 SPI_cursor_move(Portal portal, bool forward, int count)
857 _SPI_cursor_operation(portal, forward, count, None_Receiver);
867 SPI_cursor_close(Portal portal)
869 if (!PortalIsValid(portal))
870 elog(ERROR, "invalid portal in SPI cursor operation");
872 PortalDrop(portal, false);
875 /* =================== private functions =================== */
879 * Initialize to receive tuples from Executor into SPITupleTable
880 * of current SPI procedure
883 spi_dest_startup(DestReceiver *self, int operation,
884 const char *portalName, TupleDesc typeinfo, List *targetlist)
886 SPITupleTable *tuptable;
887 MemoryContext oldcxt;
888 MemoryContext tuptabcxt;
891 * When called by Executor _SPI_curid expected to be equal to
894 if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
895 elog(FATAL, "SPI: improper call to spi_dest_startup");
896 if (_SPI_current != &(_SPI_stack[_SPI_curid]))
897 elog(FATAL, "SPI: stack corrupted in spi_dest_startup");
899 if (_SPI_current->tuptable != NULL)
900 elog(FATAL, "SPI: improper call to spi_dest_startup");
902 oldcxt = _SPI_procmem(); /* switch to procedure memory context */
904 tuptabcxt = AllocSetContextCreate(CurrentMemoryContext,
906 ALLOCSET_DEFAULT_MINSIZE,
907 ALLOCSET_DEFAULT_INITSIZE,
908 ALLOCSET_DEFAULT_MAXSIZE);
909 MemoryContextSwitchTo(tuptabcxt);
911 _SPI_current->tuptable = tuptable = (SPITupleTable *)
912 palloc(sizeof(SPITupleTable));
913 tuptable->tuptabcxt = tuptabcxt;
914 tuptable->alloced = tuptable->free = 128;
915 tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
916 tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
918 MemoryContextSwitchTo(oldcxt);
923 * store tuple retrieved by Executor into SPITupleTable
924 * of current SPI procedure
927 spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
929 SPITupleTable *tuptable;
930 MemoryContext oldcxt;
933 * When called by Executor _SPI_curid expected to be equal to
936 if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
937 elog(FATAL, "SPI: improper call to spi_printtup");
938 if (_SPI_current != &(_SPI_stack[_SPI_curid]))
939 elog(FATAL, "SPI: stack corrupted in spi_printtup");
941 tuptable = _SPI_current->tuptable;
942 if (tuptable == NULL)
943 elog(FATAL, "SPI: improper call to spi_printtup");
945 oldcxt = MemoryContextSwitchTo(tuptable->tuptabcxt);
947 if (tuptable->free == 0)
949 tuptable->free = 256;
950 tuptable->alloced += tuptable->free;
951 tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
952 tuptable->alloced * sizeof(HeapTuple));
955 tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple);
958 MemoryContextSwitchTo(oldcxt);
966 * Plan and optionally execute a querystring.
968 * If plan != NULL, just prepare plan tree, else execute immediately.
971 _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
973 List *raw_parsetree_list;
974 List *query_list_list;
978 Oid *argtypes = NULL;
984 argtypes = plan->argtypes;
987 /* Increment CommandCounter to see changes made by now */
988 CommandCounterIncrement();
990 /* Reset state (only needed in case string is empty) */
992 SPI_lastoid = InvalidOid;
994 _SPI_current->tuptable = NULL;
997 * Parse the request string into a list of raw parse trees.
999 raw_parsetree_list = pg_parse_query(src);
1002 * Do parse analysis and rule rewrite for each raw parsetree.
1004 * We save the querytrees from each raw parsetree as a separate
1005 * sublist. This allows _SPI_execute_plan() to know where the
1006 * boundaries between original queries fall.
1008 query_list_list = NIL;
1011 foreach(list_item, raw_parsetree_list)
1013 Node *parsetree = (Node *) lfirst(list_item);
1015 List *query_list_item;
1017 query_list = pg_analyze_and_rewrite(parsetree, argtypes, nargs);
1019 query_list_list = lappend(query_list_list, query_list);
1021 /* Reset state for each original parsetree */
1022 /* (at most one of its querytrees will be marked canSetTag) */
1024 SPI_lastoid = InvalidOid;
1025 SPI_tuptable = NULL;
1026 _SPI_current->tuptable = NULL;
1028 foreach(query_list_item, query_list)
1030 Query *queryTree = (Query *) lfirst(query_list_item);
1035 planTree = pg_plan_query(queryTree);
1036 plan_list = lappend(plan_list, planTree);
1038 dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None);
1039 if (queryTree->commandType == CMD_UTILITY)
1041 if (IsA(queryTree->utilityStmt, CopyStmt))
1043 CopyStmt *stmt = (CopyStmt *) queryTree->utilityStmt;
1045 if (stmt->filename == NULL)
1046 return SPI_ERROR_COPY;
1048 else if (IsA(queryTree->utilityStmt, DeclareCursorStmt) ||
1049 IsA(queryTree->utilityStmt, ClosePortalStmt) ||
1050 IsA(queryTree->utilityStmt, FetchStmt))
1051 return SPI_ERROR_CURSOR;
1052 else if (IsA(queryTree->utilityStmt, TransactionStmt))
1053 return SPI_ERROR_TRANSACTION;
1054 res = SPI_OK_UTILITY;
1057 ProcessUtility(queryTree->utilityStmt, dest, NULL);
1058 CommandCounterIncrement();
1061 else if (plan == NULL)
1063 qdesc = CreateQueryDesc(queryTree, planTree, dest,
1065 res = _SPI_pquery(qdesc, true,
1066 queryTree->canSetTag ? tcount : 0);
1069 CommandCounterIncrement();
1073 qdesc = CreateQueryDesc(queryTree, planTree, dest,
1075 res = _SPI_pquery(qdesc, false, 0);
1084 plan->qtlist = query_list_list;
1085 plan->ptlist = plan_list;
1092 _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
1095 List *query_list_list = plan->qtlist;
1096 List *plan_list = plan->ptlist;
1097 List *query_list_list_item;
1098 int nargs = plan->nargs;
1100 ParamListInfo paramLI;
1102 /* Increment CommandCounter to see changes made by now */
1103 CommandCounterIncrement();
1105 /* Convert parameters to form wanted by executor */
1110 paramLI = (ParamListInfo)
1111 palloc0((nargs + 1) * sizeof(ParamListInfoData));
1113 for (k = 0; k < nargs; k++)
1115 paramLI[k].kind = PARAM_NUM;
1116 paramLI[k].id = k + 1;
1117 paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
1118 paramLI[k].value = Values[k];
1120 paramLI[k].kind = PARAM_INVALID;
1125 /* Reset state (only needed in case string is empty) */
1127 SPI_lastoid = InvalidOid;
1128 SPI_tuptable = NULL;
1129 _SPI_current->tuptable = NULL;
1131 foreach(query_list_list_item, query_list_list)
1133 List *query_list = lfirst(query_list_list_item);
1134 List *query_list_item;
1136 /* Reset state for each original parsetree */
1137 /* (at most one of its querytrees will be marked canSetTag) */
1139 SPI_lastoid = InvalidOid;
1140 SPI_tuptable = NULL;
1141 _SPI_current->tuptable = NULL;
1143 foreach(query_list_item, query_list)
1145 Query *queryTree = (Query *) lfirst(query_list_item);
1150 planTree = lfirst(plan_list);
1151 plan_list = lnext(plan_list);
1153 dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None);
1154 if (queryTree->commandType == CMD_UTILITY)
1156 ProcessUtility(queryTree->utilityStmt, dest, NULL);
1157 res = SPI_OK_UTILITY;
1158 CommandCounterIncrement();
1162 qdesc = CreateQueryDesc(queryTree, planTree, dest,
1163 NULL, paramLI, false);
1164 res = _SPI_pquery(qdesc, true,
1165 queryTree->canSetTag ? tcount : 0);
1168 CommandCounterIncrement();
1177 _SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount)
1179 int operation = queryDesc->operation;
1186 res = SPI_OK_SELECT;
1187 if (queryDesc->parsetree->into != NULL) /* select into table */
1189 res = SPI_OK_SELINTO;
1190 queryDesc->dest = None_Receiver; /* don't output results */
1194 res = SPI_OK_INSERT;
1197 res = SPI_OK_DELETE;
1200 res = SPI_OK_UPDATE;
1203 return SPI_ERROR_OPUNKNOWN;
1206 if (!runit) /* plan preparation, don't execute */
1209 #ifdef SPI_EXECUTOR_STATS
1210 if (ShowExecutorStats)
1214 ExecutorStart(queryDesc, false);
1216 ExecutorRun(queryDesc, ForwardScanDirection, (long) tcount);
1218 _SPI_current->processed = queryDesc->estate->es_processed;
1219 save_lastoid = queryDesc->estate->es_lastoid;
1221 if (operation == CMD_SELECT && queryDesc->dest->mydest == SPI)
1223 if (_SPI_checktuples())
1224 elog(FATAL, "SPI_select: # of processed tuples check failed");
1227 if (queryDesc->dest->mydest == SPI)
1229 SPI_processed = _SPI_current->processed;
1230 SPI_lastoid = save_lastoid;
1231 SPI_tuptable = _SPI_current->tuptable;
1233 else if (res == SPI_OK_SELECT)
1235 /* Don't return SPI_OK_SELECT if we discarded the result */
1236 res = SPI_OK_UTILITY;
1239 ExecutorEnd(queryDesc);
1241 FreeQueryDesc(queryDesc);
1243 #ifdef SPI_EXECUTOR_STATS
1244 if (ShowExecutorStats)
1245 ShowUsage("SPI EXECUTOR STATS");
1252 * _SPI_cursor_operation()
1254 * Do a FETCH or MOVE in a cursor
1257 _SPI_cursor_operation(Portal portal, bool forward, int count,
1260 /* Check that the portal is valid */
1261 if (!PortalIsValid(portal))
1262 elog(ERROR, "invalid portal in SPI cursor operation");
1264 /* Push the SPI stack */
1265 _SPI_begin_call(true);
1267 /* Reset the SPI result */
1269 SPI_tuptable = NULL;
1270 _SPI_current->processed = 0;
1271 _SPI_current->tuptable = NULL;
1273 /* Run the cursor */
1274 _SPI_current->processed =
1275 PortalRunFetch(portal,
1276 forward ? FETCH_FORWARD : FETCH_BACKWARD,
1280 if (dest->mydest == SPI && _SPI_checktuples())
1281 elog(FATAL, "SPI_fetch: # of processed tuples check failed");
1283 /* Put the result into place for access by caller */
1284 SPI_processed = _SPI_current->processed;
1285 SPI_tuptable = _SPI_current->tuptable;
1287 /* Pop the SPI stack */
1288 _SPI_end_call(true);
1292 static MemoryContext
1295 return MemoryContextSwitchTo(_SPI_current->execCxt);
1298 static MemoryContext
1301 return MemoryContextSwitchTo(_SPI_current->procCxt);
1309 _SPI_begin_call(bool execmem)
1311 if (_SPI_curid + 1 != _SPI_connected)
1312 return SPI_ERROR_UNCONNECTED;
1314 if (_SPI_current != &(_SPI_stack[_SPI_curid]))
1315 elog(FATAL, "SPI: stack corrupted");
1317 if (execmem) /* switch to the Executor memory context */
1324 _SPI_end_call(bool procmem)
1327 * We're returning to procedure where _SPI_curid == _SPI_connected - 1
1331 if (procmem) /* switch to the procedure memory context */
1334 /* and free Executor memory */
1335 MemoryContextResetAndDeleteChildren(_SPI_current->execCxt);
1342 _SPI_checktuples(void)
1344 uint32 processed = _SPI_current->processed;
1345 SPITupleTable *tuptable = _SPI_current->tuptable;
1346 bool failed = false;
1348 if (tuptable == NULL) /* spi_dest_startup was not called */
1350 else if (processed != (tuptable->alloced - tuptable->free))
1357 _SPI_copy_plan(_SPI_plan *plan, int location)
1360 MemoryContext oldcxt;
1361 MemoryContext plancxt;
1362 MemoryContext parentcxt;
1364 /* Determine correct parent for the plan's memory context */
1365 if (location == _SPI_CPLAN_PROCXT)
1366 parentcxt = _SPI_current->procCxt;
1367 else if (location == _SPI_CPLAN_TOPCXT)
1368 parentcxt = TopMemoryContext;
1369 else /* (this case not currently used) */
1370 parentcxt = CurrentMemoryContext;
1373 * Create a memory context for the plan. We don't expect the plan to
1374 * be very large, so use smaller-than-default alloc parameters.
1376 plancxt = AllocSetContextCreate(parentcxt,
1378 ALLOCSET_SMALL_MINSIZE,
1379 ALLOCSET_SMALL_INITSIZE,
1380 ALLOCSET_SMALL_MAXSIZE);
1381 oldcxt = MemoryContextSwitchTo(plancxt);
1383 /* Copy the SPI plan into its own context */
1384 newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan));
1385 newplan->plancxt = plancxt;
1386 newplan->qtlist = (List *) copyObject(plan->qtlist);
1387 newplan->ptlist = (List *) copyObject(plan->ptlist);
1388 newplan->nargs = plan->nargs;
1389 if (plan->nargs > 0)
1391 newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
1392 memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
1395 newplan->argtypes = NULL;
1397 MemoryContextSwitchTo(oldcxt);