1 /*-------------------------------------------------------------------------
4 * Server Programming Interface
6 * Portions Copyright (c) 1996-2003, 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.106 2003/09/25 18:58:35 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,
36 bool useSnapshotNow, int tcount);
38 static int _SPI_execute_plan(_SPI_plan *plan,
39 Datum *Values, const char *Nulls,
40 bool useSnapshotNow, int tcount);
42 static void _SPI_cursor_operation(Portal portal, bool forward, int count,
45 static _SPI_plan *_SPI_copy_plan(_SPI_plan *plan, int location);
47 static int _SPI_begin_call(bool execmem);
48 static int _SPI_end_call(bool procmem);
49 static MemoryContext _SPI_execmem(void);
50 static MemoryContext _SPI_procmem(void);
51 static bool _SPI_checktuples(void);
54 /* =================== interface functions =================== */
59 _SPI_connection *new_SPI_stack;
62 * When procedure called by Executor _SPI_curid expected to be equal
65 if (_SPI_curid != _SPI_connected)
66 return SPI_ERROR_CONNECT;
68 if (_SPI_stack == NULL)
70 if (_SPI_connected != -1)
71 elog(ERROR, "SPI stack corrupted");
72 new_SPI_stack = (_SPI_connection *) malloc(sizeof(_SPI_connection));
76 if (_SPI_connected < 0)
77 elog(ERROR, "SPI stack corrupted");
78 new_SPI_stack = (_SPI_connection *) realloc(_SPI_stack,
79 (_SPI_connected + 2) * sizeof(_SPI_connection));
82 if (new_SPI_stack == NULL)
84 (errcode(ERRCODE_OUT_OF_MEMORY),
85 errmsg("out of memory")));
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->processed = 0;
95 _SPI_current->tuptable = NULL;
98 * Create memory contexts for this procedure
100 * XXX it would be better to use PortalContext as the parent context, but
101 * we may not be inside a portal (consider deferred-trigger
104 _SPI_current->procCxt = AllocSetContextCreate(TopTransactionContext,
106 ALLOCSET_DEFAULT_MINSIZE,
107 ALLOCSET_DEFAULT_INITSIZE,
108 ALLOCSET_DEFAULT_MAXSIZE);
109 _SPI_current->execCxt = AllocSetContextCreate(TopTransactionContext,
111 ALLOCSET_DEFAULT_MINSIZE,
112 ALLOCSET_DEFAULT_INITSIZE,
113 ALLOCSET_DEFAULT_MAXSIZE);
114 /* ... and switch to procedure's context */
115 _SPI_current->savedcxt = MemoryContextSwitchTo(_SPI_current->procCxt);
117 return SPI_OK_CONNECT;
125 res = _SPI_begin_call(false); /* live in procedure memory */
129 /* Restore memory context as it was before procedure call */
130 MemoryContextSwitchTo(_SPI_current->savedcxt);
132 /* Release memory used in procedure call */
133 MemoryContextDelete(_SPI_current->execCxt);
134 MemoryContextDelete(_SPI_current->procCxt);
137 * Reset result variables, especially SPI_tuptable which is probably
138 * pointing at a just-deleted tuptable
141 SPI_lastoid = InvalidOid;
145 * After _SPI_begin_call _SPI_connected == _SPI_curid. Now we are
146 * closing connection to SPI and returning to upper Executor and so
147 * _SPI_connected must be equal to _SPI_curid.
151 if (_SPI_connected == -1)
159 _SPI_connection *new_SPI_stack;
161 new_SPI_stack = (_SPI_connection *) realloc(_SPI_stack,
162 (_SPI_connected + 1) * sizeof(_SPI_connection));
163 /* This could only fail with a pretty stupid malloc package ... */
164 if (new_SPI_stack == NULL)
166 (errcode(ERRCODE_OUT_OF_MEMORY),
167 errmsg("out of memory")));
168 _SPI_stack = new_SPI_stack;
169 _SPI_current = &(_SPI_stack[_SPI_connected]);
172 return SPI_OK_FINISH;
177 * Clean up SPI state at transaction commit or abort (we don't care which).
183 * Note that memory contexts belonging to SPI stack entries will be
184 * freed automatically, so we can ignore them here. We just need to
185 * restore our static variables to initial state.
187 if (_SPI_stack != NULL) /* there was abort */
189 _SPI_current = _SPI_stack = NULL;
190 _SPI_connected = _SPI_curid = -1;
192 SPI_lastoid = InvalidOid;
209 SPI_exec(const char *src, int tcount)
213 if (src == NULL || tcount < 0)
214 return SPI_ERROR_ARGUMENT;
216 res = _SPI_begin_call(true);
220 res = _SPI_execute(src, tcount, NULL);
227 SPI_execp(void *plan, Datum *Values, const char *Nulls, int tcount)
231 if (plan == NULL || tcount < 0)
232 return SPI_ERROR_ARGUMENT;
234 if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL)
235 return SPI_ERROR_PARAM;
237 res = _SPI_begin_call(true);
241 res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, false, tcount);
248 * SPI_execp_now -- identical to SPI_execp, except that we use SnapshotNow
249 * instead of the normal QuerySnapshot. This is currently not documented
250 * in spi.sgml because it is only intended for use by RI triggers.
253 SPI_execp_now(void *plan, Datum *Values, const char *Nulls, int tcount)
257 if (plan == NULL || tcount < 0)
258 return SPI_ERROR_ARGUMENT;
260 if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL)
261 return SPI_ERROR_PARAM;
263 res = _SPI_begin_call(true);
267 res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, true, tcount);
274 SPI_prepare(const char *src, int nargs, Oid *argtypes)
278 if (src == NULL || nargs < 0 || (nargs > 0 && argtypes == NULL))
280 SPI_result = SPI_ERROR_ARGUMENT;
284 SPI_result = _SPI_begin_call(true);
288 plan = (_SPI_plan *) palloc(sizeof(_SPI_plan)); /* Executor context */
289 plan->argtypes = argtypes;
292 SPI_result = _SPI_execute(src, 0, plan);
294 if (SPI_result >= 0) /* copy plan to procedure context */
295 plan = _SPI_copy_plan(plan, _SPI_CPLAN_PROCXT);
301 return (void *) plan;
305 SPI_saveplan(void *plan)
311 SPI_result = SPI_ERROR_ARGUMENT;
315 SPI_result = _SPI_begin_call(false); /* don't change context */
319 newplan = _SPI_copy_plan((_SPI_plan *) plan, _SPI_CPLAN_TOPCXT);
324 return (void *) newplan;
329 SPI_freeplan(void *plan)
331 _SPI_plan *spiplan = (_SPI_plan *) plan;
334 return SPI_ERROR_ARGUMENT;
336 MemoryContextDelete(spiplan->plancxt);
341 SPI_copytuple(HeapTuple tuple)
343 MemoryContext oldcxt = NULL;
348 SPI_result = SPI_ERROR_ARGUMENT;
352 if (_SPI_curid + 1 == _SPI_connected) /* connected */
354 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
355 elog(ERROR, "SPI stack corrupted");
356 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
359 ctuple = heap_copytuple(tuple);
362 MemoryContextSwitchTo(oldcxt);
368 SPI_copytupledesc(TupleDesc tupdesc)
370 MemoryContext oldcxt = NULL;
375 SPI_result = SPI_ERROR_ARGUMENT;
379 if (_SPI_curid + 1 == _SPI_connected) /* connected */
381 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
382 elog(ERROR, "SPI stack corrupted");
383 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
386 ctupdesc = CreateTupleDescCopy(tupdesc);
389 MemoryContextSwitchTo(oldcxt);
395 SPI_copytupleintoslot(HeapTuple tuple, TupleDesc tupdesc)
397 MemoryContext oldcxt = NULL;
398 TupleTableSlot *cslot;
402 if (tuple == NULL || tupdesc == NULL)
404 SPI_result = SPI_ERROR_ARGUMENT;
408 if (_SPI_curid + 1 == _SPI_connected) /* connected */
410 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
411 elog(ERROR, "SPI stack corrupted");
412 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
415 ctuple = heap_copytuple(tuple);
416 ctupdesc = CreateTupleDescCopy(tupdesc);
418 cslot = MakeTupleTableSlot();
419 ExecSetSlotDescriptor(cslot, ctupdesc, true);
420 cslot = ExecStoreTuple(ctuple, cslot, InvalidBuffer, true);
423 MemoryContextSwitchTo(oldcxt);
429 SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
430 Datum *Values, const char *Nulls)
432 MemoryContext oldcxt = NULL;
434 int numberOfAttributes;
440 if (rel == NULL || tuple == NULL || natts < 0 || attnum == NULL || Values == NULL)
442 SPI_result = SPI_ERROR_ARGUMENT;
446 if (_SPI_curid + 1 == _SPI_connected) /* connected */
448 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
449 elog(ERROR, "SPI stack corrupted");
450 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
453 numberOfAttributes = rel->rd_att->natts;
454 v = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
455 n = (char *) palloc(numberOfAttributes * sizeof(char));
457 /* fetch old values and nulls */
458 for (i = 0; i < numberOfAttributes; i++)
460 v[i] = heap_getattr(tuple, i + 1, rel->rd_att, &isnull);
461 n[i] = (isnull) ? 'n' : ' ';
464 /* replace values and nulls */
465 for (i = 0; i < natts; i++)
467 if (attnum[i] <= 0 || attnum[i] > numberOfAttributes)
469 v[attnum[i] - 1] = Values[i];
470 n[attnum[i] - 1] = (Nulls && Nulls[i] == 'n') ? 'n' : ' ';
473 if (i == natts) /* no errors in *attnum */
475 mtuple = heap_formtuple(rel->rd_att, v, n);
478 * copy the identification info of the old tuple: t_ctid, t_self,
481 mtuple->t_data->t_ctid = tuple->t_data->t_ctid;
482 mtuple->t_self = tuple->t_self;
483 mtuple->t_tableOid = tuple->t_tableOid;
484 if (rel->rd_rel->relhasoids)
485 HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple));
490 SPI_result = SPI_ERROR_NOATTRIBUTE;
497 MemoryContextSwitchTo(oldcxt);
503 SPI_fnumber(TupleDesc tupdesc, const char *fname)
506 Form_pg_attribute sysatt;
508 for (res = 0; res < tupdesc->natts; res++)
510 if (namestrcmp(&tupdesc->attrs[res]->attname, fname) == 0)
514 sysatt = SystemAttributeByName(fname, true /* "oid" will be accepted */ );
516 return sysatt->attnum;
518 /* SPI_ERROR_NOATTRIBUTE is different from all sys column numbers */
519 return SPI_ERROR_NOATTRIBUTE;
523 SPI_fname(TupleDesc tupdesc, int fnumber)
525 Form_pg_attribute att;
529 if (fnumber > tupdesc->natts || fnumber == 0 ||
530 fnumber <= FirstLowInvalidHeapAttributeNumber)
532 SPI_result = SPI_ERROR_NOATTRIBUTE;
537 att = tupdesc->attrs[fnumber - 1];
539 att = SystemAttributeDefinition(fnumber, true);
541 return pstrdup(NameStr(att->attname));
545 SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
559 if (fnumber > tuple->t_data->t_natts || fnumber == 0 ||
560 fnumber <= FirstLowInvalidHeapAttributeNumber)
562 SPI_result = SPI_ERROR_NOATTRIBUTE;
566 origval = heap_getattr(tuple, fnumber, tupdesc, &isnull);
572 typoid = tupdesc->attrs[fnumber - 1]->atttypid;
573 typmod = tupdesc->attrs[fnumber - 1]->atttypmod;
577 typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
581 getTypeOutputInfo(typoid, &foutoid, &typelem, &typisvarlena);
584 * If we have a toasted datum, forcibly detoast it here to avoid
585 * memory leakage inside the type's output routine.
588 val = PointerGetDatum(PG_DETOAST_DATUM(origval));
592 result = OidFunctionCall3(foutoid,
594 ObjectIdGetDatum(typelem),
595 Int32GetDatum(typmod));
597 /* Clean up detoasted copy, if any */
599 pfree(DatumGetPointer(val));
601 return DatumGetCString(result);
605 SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
609 if (fnumber > tuple->t_data->t_natts || fnumber == 0 ||
610 fnumber <= FirstLowInvalidHeapAttributeNumber)
612 SPI_result = SPI_ERROR_NOATTRIBUTE;
617 return heap_getattr(tuple, fnumber, tupdesc, isnull);
621 SPI_gettype(TupleDesc tupdesc, int fnumber)
629 if (fnumber > tupdesc->natts || fnumber == 0 ||
630 fnumber <= FirstLowInvalidHeapAttributeNumber)
632 SPI_result = SPI_ERROR_NOATTRIBUTE;
637 typoid = tupdesc->attrs[fnumber - 1]->atttypid;
639 typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
641 typeTuple = SearchSysCache(TYPEOID,
642 ObjectIdGetDatum(typoid),
645 if (!HeapTupleIsValid(typeTuple))
647 SPI_result = SPI_ERROR_TYPUNKNOWN;
651 result = pstrdup(NameStr(((Form_pg_type) GETSTRUCT(typeTuple))->typname));
652 ReleaseSysCache(typeTuple);
657 SPI_gettypeid(TupleDesc tupdesc, int fnumber)
661 if (fnumber > tupdesc->natts || fnumber == 0 ||
662 fnumber <= FirstLowInvalidHeapAttributeNumber)
664 SPI_result = SPI_ERROR_NOATTRIBUTE;
669 return tupdesc->attrs[fnumber - 1]->atttypid;
671 return (SystemAttributeDefinition(fnumber, true))->atttypid;
675 SPI_getrelname(Relation rel)
677 return pstrdup(RelationGetRelationName(rel));
681 SPI_palloc(Size size)
683 MemoryContext oldcxt = NULL;
686 if (_SPI_curid + 1 == _SPI_connected) /* connected */
688 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
689 elog(ERROR, "SPI stack corrupted");
690 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
693 pointer = palloc(size);
696 MemoryContextSwitchTo(oldcxt);
702 SPI_repalloc(void *pointer, Size size)
704 /* No longer need to worry which context chunk was in... */
705 return repalloc(pointer, size);
709 SPI_pfree(void *pointer)
711 /* No longer need to worry which context chunk was in... */
716 SPI_freetuple(HeapTuple tuple)
718 /* No longer need to worry which context tuple was in... */
719 heap_freetuple(tuple);
723 SPI_freetuptable(SPITupleTable *tuptable)
725 if (tuptable != NULL)
726 MemoryContextDelete(tuptable->tuptabcxt);
734 * Open a prepared SPI plan as a portal
737 SPI_cursor_open(const char *name, void *plan, Datum *Values, const char *Nulls)
739 _SPI_plan *spiplan = (_SPI_plan *) plan;
740 List *qtlist = spiplan->qtlist;
741 List *ptlist = spiplan->ptlist;
744 ParamListInfo paramLI;
745 MemoryContext oldcontext;
749 /* Ensure that the plan contains only one regular SELECT query */
750 if (length(ptlist) != 1 || length(qtlist) != 1)
752 (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
753 errmsg("cannot open multi-query plan as cursor")));
754 queryTree = (Query *) lfirst((List *) lfirst(qtlist));
755 planTree = (Plan *) lfirst(ptlist);
757 if (queryTree->commandType != CMD_SELECT)
759 (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
760 errmsg("cannot open non-SELECT query as cursor")));
761 if (queryTree->into != NULL)
763 (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
764 errmsg("cannot open SELECT INTO query as cursor")));
766 /* Increment CommandCounter to see changes made by now */
767 CommandCounterIncrement();
769 /* Reset SPI result */
772 _SPI_current->processed = 0;
773 _SPI_current->tuptable = NULL;
775 /* Create the portal */
776 if (name == NULL || name[0] == '\0')
778 /* Use a random nonconflicting name */
779 portal = CreateNewPortal();
783 /* In this path, error if portal of same name already exists */
784 portal = CreatePortal(name, false, false);
787 /* Switch to portals memory and copy the parsetree and plan to there */
788 oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
789 queryTree = copyObject(queryTree);
790 planTree = copyObject(planTree);
792 /* If the plan has parameters, set them up */
793 if (spiplan->nargs > 0)
795 paramLI = (ParamListInfo) palloc0((spiplan->nargs + 1) *
796 sizeof(ParamListInfoData));
798 for (k = 0; k < spiplan->nargs; k++)
800 paramLI[k].kind = PARAM_NUM;
801 paramLI[k].id = k + 1;
802 paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
803 if (paramLI[k].isnull)
805 /* nulls just copy */
806 paramLI[k].value = Values[k];
810 /* pass-by-ref values must be copied into portal context */
814 get_typlenbyval(spiplan->argtypes[k],
815 ¶mTypLen, ¶mTypByVal);
816 paramLI[k].value = datumCopy(Values[k],
817 paramTypByVal, paramTypLen);
820 paramLI[k].kind = PARAM_INVALID;
828 PortalDefineQuery(portal,
829 NULL, /* unfortunately don't have sourceText */
830 "SELECT", /* cursor's query is always a SELECT */
831 makeList1(queryTree),
833 PortalGetHeapMemory(portal));
835 MemoryContextSwitchTo(oldcontext);
838 * Set up options for portal.
840 portal->cursorOptions &= ~(CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL);
841 if (ExecSupportsBackwardScan(plan))
842 portal->cursorOptions |= CURSOR_OPT_SCROLL;
844 portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
847 * Start portal execution.
849 PortalStart(portal, paramLI);
851 Assert(portal->strategy == PORTAL_ONE_SELECT);
853 /* Return the created portal */
861 * Find the portal of an existing open cursor
864 SPI_cursor_find(const char *name)
866 return GetPortalByName(name);
873 * Fetch rows in a cursor
876 SPI_cursor_fetch(Portal portal, bool forward, int count)
878 _SPI_cursor_operation(portal, forward, count,
879 CreateDestReceiver(SPI, NULL));
880 /* we know that the SPI receiver doesn't need a destroy call */
890 SPI_cursor_move(Portal portal, bool forward, int count)
892 _SPI_cursor_operation(portal, forward, count, None_Receiver);
902 SPI_cursor_close(Portal portal)
904 if (!PortalIsValid(portal))
905 elog(ERROR, "invalid portal in SPI cursor operation");
907 PortalDrop(portal, false);
910 /* =================== private functions =================== */
914 * Initialize to receive tuples from Executor into SPITupleTable
915 * of current SPI procedure
918 spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
920 SPITupleTable *tuptable;
921 MemoryContext oldcxt;
922 MemoryContext tuptabcxt;
925 * When called by Executor _SPI_curid expected to be equal to
928 if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
929 elog(ERROR, "improper call to spi_dest_startup");
930 if (_SPI_current != &(_SPI_stack[_SPI_curid]))
931 elog(ERROR, "SPI stack corrupted");
933 if (_SPI_current->tuptable != NULL)
934 elog(ERROR, "improper call to spi_dest_startup");
936 oldcxt = _SPI_procmem(); /* switch to procedure memory context */
938 tuptabcxt = AllocSetContextCreate(CurrentMemoryContext,
940 ALLOCSET_DEFAULT_MINSIZE,
941 ALLOCSET_DEFAULT_INITSIZE,
942 ALLOCSET_DEFAULT_MAXSIZE);
943 MemoryContextSwitchTo(tuptabcxt);
945 _SPI_current->tuptable = tuptable = (SPITupleTable *)
946 palloc(sizeof(SPITupleTable));
947 tuptable->tuptabcxt = tuptabcxt;
948 tuptable->alloced = tuptable->free = 128;
949 tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
950 tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
952 MemoryContextSwitchTo(oldcxt);
957 * store tuple retrieved by Executor into SPITupleTable
958 * of current SPI procedure
961 spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
963 SPITupleTable *tuptable;
964 MemoryContext oldcxt;
967 * When called by Executor _SPI_curid expected to be equal to
970 if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
971 elog(ERROR, "improper call to spi_printtup");
972 if (_SPI_current != &(_SPI_stack[_SPI_curid]))
973 elog(ERROR, "SPI stack corrupted");
975 tuptable = _SPI_current->tuptable;
976 if (tuptable == NULL)
977 elog(ERROR, "improper call to spi_printtup");
979 oldcxt = MemoryContextSwitchTo(tuptable->tuptabcxt);
981 if (tuptable->free == 0)
983 tuptable->free = 256;
984 tuptable->alloced += tuptable->free;
985 tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
986 tuptable->alloced * sizeof(HeapTuple));
989 tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple);
992 MemoryContextSwitchTo(oldcxt);
1000 * Plan and optionally execute a querystring.
1002 * If plan != NULL, just prepare plan tree, else execute immediately.
1005 _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
1007 List *raw_parsetree_list;
1008 List *query_list_list;
1012 Oid *argtypes = NULL;
1017 nargs = plan->nargs;
1018 argtypes = plan->argtypes;
1021 /* Increment CommandCounter to see changes made by now */
1022 CommandCounterIncrement();
1024 /* Reset state (only needed in case string is empty) */
1026 SPI_lastoid = InvalidOid;
1027 SPI_tuptable = NULL;
1028 _SPI_current->tuptable = NULL;
1031 * Parse the request string into a list of raw parse trees.
1033 raw_parsetree_list = pg_parse_query(src);
1036 * Do parse analysis and rule rewrite for each raw parsetree.
1038 * We save the querytrees from each raw parsetree as a separate sublist.
1039 * This allows _SPI_execute_plan() to know where the boundaries
1040 * between original queries fall.
1042 query_list_list = NIL;
1045 foreach(list_item, raw_parsetree_list)
1047 Node *parsetree = (Node *) lfirst(list_item);
1049 List *query_list_item;
1051 query_list = pg_analyze_and_rewrite(parsetree, argtypes, nargs);
1053 query_list_list = lappend(query_list_list, query_list);
1055 /* Reset state for each original parsetree */
1056 /* (at most one of its querytrees will be marked canSetTag) */
1058 SPI_lastoid = InvalidOid;
1059 SPI_tuptable = NULL;
1060 _SPI_current->tuptable = NULL;
1062 foreach(query_list_item, query_list)
1064 Query *queryTree = (Query *) lfirst(query_list_item);
1069 planTree = pg_plan_query(queryTree);
1070 plan_list = lappend(plan_list, planTree);
1072 dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL);
1073 if (queryTree->commandType == CMD_UTILITY)
1075 if (IsA(queryTree->utilityStmt, CopyStmt))
1077 CopyStmt *stmt = (CopyStmt *) queryTree->utilityStmt;
1079 if (stmt->filename == NULL)
1080 return SPI_ERROR_COPY;
1082 else if (IsA(queryTree->utilityStmt, DeclareCursorStmt) ||
1083 IsA(queryTree->utilityStmt, ClosePortalStmt) ||
1084 IsA(queryTree->utilityStmt, FetchStmt))
1085 return SPI_ERROR_CURSOR;
1086 else if (IsA(queryTree->utilityStmt, TransactionStmt))
1087 return SPI_ERROR_TRANSACTION;
1088 res = SPI_OK_UTILITY;
1091 ProcessUtility(queryTree->utilityStmt, dest, NULL);
1092 CommandCounterIncrement();
1095 else if (plan == NULL)
1097 qdesc = CreateQueryDesc(queryTree, planTree, dest,
1099 res = _SPI_pquery(qdesc, true, false,
1100 queryTree->canSetTag ? tcount : 0);
1103 CommandCounterIncrement();
1107 qdesc = CreateQueryDesc(queryTree, planTree, dest,
1109 res = _SPI_pquery(qdesc, false, false, 0);
1118 plan->qtlist = query_list_list;
1119 plan->ptlist = plan_list;
1126 _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
1127 bool useSnapshotNow, int tcount)
1129 List *query_list_list = plan->qtlist;
1130 List *plan_list = plan->ptlist;
1131 List *query_list_list_item;
1132 int nargs = plan->nargs;
1134 ParamListInfo paramLI;
1136 /* Increment CommandCounter to see changes made by now */
1137 CommandCounterIncrement();
1139 /* Convert parameters to form wanted by executor */
1144 paramLI = (ParamListInfo)
1145 palloc0((nargs + 1) * sizeof(ParamListInfoData));
1147 for (k = 0; k < nargs; k++)
1149 paramLI[k].kind = PARAM_NUM;
1150 paramLI[k].id = k + 1;
1151 paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
1152 paramLI[k].value = Values[k];
1154 paramLI[k].kind = PARAM_INVALID;
1159 /* Reset state (only needed in case string is empty) */
1161 SPI_lastoid = InvalidOid;
1162 SPI_tuptable = NULL;
1163 _SPI_current->tuptable = NULL;
1165 foreach(query_list_list_item, query_list_list)
1167 List *query_list = lfirst(query_list_list_item);
1168 List *query_list_item;
1170 /* Reset state for each original parsetree */
1171 /* (at most one of its querytrees will be marked canSetTag) */
1173 SPI_lastoid = InvalidOid;
1174 SPI_tuptable = NULL;
1175 _SPI_current->tuptable = NULL;
1177 foreach(query_list_item, query_list)
1179 Query *queryTree = (Query *) lfirst(query_list_item);
1184 planTree = lfirst(plan_list);
1185 plan_list = lnext(plan_list);
1187 dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL);
1188 if (queryTree->commandType == CMD_UTILITY)
1190 ProcessUtility(queryTree->utilityStmt, dest, NULL);
1191 res = SPI_OK_UTILITY;
1192 CommandCounterIncrement();
1196 qdesc = CreateQueryDesc(queryTree, planTree, dest,
1198 res = _SPI_pquery(qdesc, true, useSnapshotNow,
1199 queryTree->canSetTag ? tcount : 0);
1202 CommandCounterIncrement();
1211 _SPI_pquery(QueryDesc *queryDesc, bool runit, bool useSnapshotNow, int tcount)
1213 int operation = queryDesc->operation;
1220 res = SPI_OK_SELECT;
1221 if (queryDesc->parsetree->into != NULL) /* select into table */
1223 res = SPI_OK_SELINTO;
1224 queryDesc->dest = None_Receiver; /* don't output results */
1228 res = SPI_OK_INSERT;
1231 res = SPI_OK_DELETE;
1234 res = SPI_OK_UPDATE;
1237 return SPI_ERROR_OPUNKNOWN;
1240 if (!runit) /* plan preparation, don't execute */
1243 #ifdef SPI_EXECUTOR_STATS
1244 if (ShowExecutorStats)
1248 ExecutorStart(queryDesc, useSnapshotNow, false);
1250 ExecutorRun(queryDesc, ForwardScanDirection, (long) tcount);
1252 _SPI_current->processed = queryDesc->estate->es_processed;
1253 save_lastoid = queryDesc->estate->es_lastoid;
1255 if (operation == CMD_SELECT && queryDesc->dest->mydest == SPI)
1257 if (_SPI_checktuples())
1258 elog(ERROR, "consistency check on SPI tuple count failed");
1261 if (queryDesc->dest->mydest == SPI)
1263 SPI_processed = _SPI_current->processed;
1264 SPI_lastoid = save_lastoid;
1265 SPI_tuptable = _SPI_current->tuptable;
1267 else if (res == SPI_OK_SELECT)
1269 /* Don't return SPI_OK_SELECT if we discarded the result */
1270 res = SPI_OK_UTILITY;
1273 ExecutorEnd(queryDesc);
1275 FreeQueryDesc(queryDesc);
1277 #ifdef SPI_EXECUTOR_STATS
1278 if (ShowExecutorStats)
1279 ShowUsage("SPI EXECUTOR STATS");
1286 * _SPI_cursor_operation()
1288 * Do a FETCH or MOVE in a cursor
1291 _SPI_cursor_operation(Portal portal, bool forward, int count,
1296 /* Check that the portal is valid */
1297 if (!PortalIsValid(portal))
1298 elog(ERROR, "invalid portal in SPI cursor operation");
1300 /* Push the SPI stack */
1301 if (_SPI_begin_call(true) < 0)
1302 elog(ERROR, "SPI cursor operation called while not connected");
1304 /* Reset the SPI result */
1306 SPI_tuptable = NULL;
1307 _SPI_current->processed = 0;
1308 _SPI_current->tuptable = NULL;
1310 /* Run the cursor */
1311 nfetched = PortalRunFetch(portal,
1312 forward ? FETCH_FORWARD : FETCH_BACKWARD,
1317 * Think not to combine this store with the preceding function call.
1318 * If the portal contains calls to functions that use SPI, then
1319 * SPI_stack is likely to move around while the portal runs. When
1320 * control returns, _SPI_current will point to the correct stack
1321 * entry... but the pointer may be different than it was beforehand.
1322 * So we must be sure to re-fetch the pointer after the function call
1325 _SPI_current->processed = nfetched;
1327 if (dest->mydest == SPI && _SPI_checktuples())
1328 elog(ERROR, "consistency check on SPI tuple count failed");
1330 /* Put the result into place for access by caller */
1331 SPI_processed = _SPI_current->processed;
1332 SPI_tuptable = _SPI_current->tuptable;
1334 /* Pop the SPI stack */
1335 _SPI_end_call(true);
1339 static MemoryContext
1342 return MemoryContextSwitchTo(_SPI_current->execCxt);
1345 static MemoryContext
1348 return MemoryContextSwitchTo(_SPI_current->procCxt);
1352 * _SPI_begin_call: begin a SPI operation within a connected procedure
1355 _SPI_begin_call(bool execmem)
1357 if (_SPI_curid + 1 != _SPI_connected)
1358 return SPI_ERROR_UNCONNECTED;
1360 if (_SPI_current != &(_SPI_stack[_SPI_curid]))
1361 elog(ERROR, "SPI stack corrupted");
1363 if (execmem) /* switch to the Executor memory context */
1370 * _SPI_end_call: end a SPI operation within a connected procedure
1372 * Note: this currently has no failure return cases, so callers don't check
1375 _SPI_end_call(bool procmem)
1378 * We're returning to procedure where _SPI_curid == _SPI_connected - 1
1382 if (procmem) /* switch to the procedure memory context */
1385 /* and free Executor memory */
1386 MemoryContextResetAndDeleteChildren(_SPI_current->execCxt);
1393 _SPI_checktuples(void)
1395 uint32 processed = _SPI_current->processed;
1396 SPITupleTable *tuptable = _SPI_current->tuptable;
1397 bool failed = false;
1399 if (tuptable == NULL) /* spi_dest_startup was not called */
1401 else if (processed != (tuptable->alloced - tuptable->free))
1408 _SPI_copy_plan(_SPI_plan *plan, int location)
1411 MemoryContext oldcxt;
1412 MemoryContext plancxt;
1413 MemoryContext parentcxt;
1415 /* Determine correct parent for the plan's memory context */
1416 if (location == _SPI_CPLAN_PROCXT)
1417 parentcxt = _SPI_current->procCxt;
1418 else if (location == _SPI_CPLAN_TOPCXT)
1419 parentcxt = TopMemoryContext;
1421 /* (this case not currently used) */
1422 parentcxt = CurrentMemoryContext;
1425 * Create a memory context for the plan. We don't expect the plan to
1426 * be very large, so use smaller-than-default alloc parameters.
1428 plancxt = AllocSetContextCreate(parentcxt,
1430 ALLOCSET_SMALL_MINSIZE,
1431 ALLOCSET_SMALL_INITSIZE,
1432 ALLOCSET_SMALL_MAXSIZE);
1433 oldcxt = MemoryContextSwitchTo(plancxt);
1435 /* Copy the SPI plan into its own context */
1436 newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan));
1437 newplan->plancxt = plancxt;
1438 newplan->qtlist = (List *) copyObject(plan->qtlist);
1439 newplan->ptlist = (List *) copyObject(plan->ptlist);
1440 newplan->nargs = plan->nargs;
1441 if (plan->nargs > 0)
1443 newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
1444 memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
1447 newplan->argtypes = NULL;
1449 MemoryContextSwitchTo(oldcxt);