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 * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.121 2004/07/27 05:10:51 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"
22 #include "utils/typcache.h"
25 uint32 SPI_processed = 0;
26 Oid SPI_lastoid = InvalidOid;
27 SPITupleTable *SPI_tuptable = NULL;
30 static _SPI_connection *_SPI_stack = NULL;
31 static _SPI_connection *_SPI_current = NULL;
32 static int _SPI_stack_depth = 0; /* allocated size of _SPI_stack */
33 static int _SPI_connected = -1;
34 static int _SPI_curid = -1;
36 static int _SPI_execute(const char *src, int tcount, _SPI_plan *plan);
37 static int _SPI_pquery(QueryDesc *queryDesc, bool runit,
38 bool useCurrentSnapshot, int tcount);
40 static int _SPI_execute_plan(_SPI_plan *plan,
41 Datum *Values, const char *Nulls,
42 bool useCurrentSnapshot, int tcount);
44 static void _SPI_error_callback(void *arg);
46 static void _SPI_cursor_operation(Portal portal, bool forward, int count,
49 static _SPI_plan *_SPI_copy_plan(_SPI_plan *plan, int location);
51 static int _SPI_begin_call(bool execmem);
52 static int _SPI_end_call(bool procmem);
53 static MemoryContext _SPI_execmem(void);
54 static MemoryContext _SPI_procmem(void);
55 static bool _SPI_checktuples(void);
58 /* =================== interface functions =================== */
66 * When procedure called by Executor _SPI_curid expected to be equal
69 if (_SPI_curid != _SPI_connected)
70 return SPI_ERROR_CONNECT;
72 if (_SPI_stack == NULL)
74 if (_SPI_connected != -1 || _SPI_stack_depth != 0)
75 elog(ERROR, "SPI stack corrupted");
77 _SPI_stack = (_SPI_connection *)
78 MemoryContextAlloc(TopTransactionContext,
79 newdepth * sizeof(_SPI_connection));
80 _SPI_stack_depth = newdepth;
84 if (_SPI_stack_depth <= 0 || _SPI_stack_depth <= _SPI_connected)
85 elog(ERROR, "SPI stack corrupted");
86 if (_SPI_stack_depth == _SPI_connected + 1)
88 newdepth = _SPI_stack_depth * 2;
89 _SPI_stack = (_SPI_connection *)
91 newdepth * sizeof(_SPI_connection));
92 _SPI_stack_depth = newdepth;
97 * We're entering procedure where _SPI_curid == _SPI_connected - 1
100 Assert(_SPI_connected >= 0 && _SPI_connected < _SPI_stack_depth);
102 _SPI_current = &(_SPI_stack[_SPI_connected]);
103 _SPI_current->processed = 0;
104 _SPI_current->tuptable = NULL;
105 _SPI_current->connectXid = GetCurrentTransactionId();
108 * Create memory contexts for this procedure
110 * XXX it would be better to use PortalContext as the parent context,
111 * but we may not be inside a portal (consider deferred-trigger
112 * execution). Perhaps CurTransactionContext would do? For now it
113 * doesn't matter because we clean up explicitly in AtEOSubXact_SPI().
115 _SPI_current->procCxt = AllocSetContextCreate(TopTransactionContext,
117 ALLOCSET_DEFAULT_MINSIZE,
118 ALLOCSET_DEFAULT_INITSIZE,
119 ALLOCSET_DEFAULT_MAXSIZE);
120 _SPI_current->execCxt = AllocSetContextCreate(TopTransactionContext,
122 ALLOCSET_DEFAULT_MINSIZE,
123 ALLOCSET_DEFAULT_INITSIZE,
124 ALLOCSET_DEFAULT_MAXSIZE);
125 /* ... and switch to procedure's context */
126 _SPI_current->savedcxt = MemoryContextSwitchTo(_SPI_current->procCxt);
128 return SPI_OK_CONNECT;
136 res = _SPI_begin_call(false); /* live in procedure memory */
140 /* Restore memory context as it was before procedure call */
141 MemoryContextSwitchTo(_SPI_current->savedcxt);
143 /* Release memory used in procedure call */
144 MemoryContextDelete(_SPI_current->execCxt);
145 MemoryContextDelete(_SPI_current->procCxt);
148 * Reset result variables, especially SPI_tuptable which is probably
149 * pointing at a just-deleted tuptable
152 SPI_lastoid = InvalidOid;
156 * After _SPI_begin_call _SPI_connected == _SPI_curid. Now we are
157 * closing connection to SPI and returning to upper Executor and so
158 * _SPI_connected must be equal to _SPI_curid.
162 if (_SPI_connected == -1)
165 _SPI_current = &(_SPI_stack[_SPI_connected]);
167 return SPI_OK_FINISH;
171 * Clean up SPI state at transaction commit or abort.
174 AtEOXact_SPI(bool isCommit)
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 (isCommit && _SPI_connected != -1)
183 (errcode(ERRCODE_WARNING),
184 errmsg("transaction left non-empty SPI stack"),
185 errhint("Check for missing \"SPI_finish\" calls")));
187 _SPI_current = _SPI_stack = NULL;
188 _SPI_stack_depth = 0;
189 _SPI_connected = _SPI_curid = -1;
191 SPI_lastoid = InvalidOid;
196 * Clean up SPI state at subtransaction commit or abort.
198 * During commit, there shouldn't be any unclosed entries remaining from
199 * the current transaction; we throw them away if found.
202 AtEOSubXact_SPI(bool isCommit, TransactionId childXid)
206 while (_SPI_connected >= 0)
208 _SPI_connection *connection = &(_SPI_stack[_SPI_connected]);
210 if (connection->connectXid != childXid)
211 break; /* couldn't be any underneath it either */
216 * Pop the stack entry and reset global variables. Unlike
217 * SPI_finish(), we don't risk switching to memory contexts that
218 * might be already gone, or deleting memory contexts that have been
219 * or will be thrown away anyway.
222 _SPI_curid = _SPI_connected;
223 if (_SPI_connected == -1)
226 _SPI_current = &(_SPI_stack[_SPI_connected]);
228 SPI_lastoid = InvalidOid;
232 if (found && isCommit)
234 (errcode(ERRCODE_WARNING),
235 errmsg("subtransaction left non-empty SPI stack"),
236 errhint("Check for missing \"SPI_finish\" calls")));
240 /* Pushes SPI stack to allow recursive SPI calls */
247 /* Pops SPI stack to allow recursive SPI calls */
255 SPI_exec(const char *src, int tcount)
259 if (src == NULL || tcount < 0)
260 return SPI_ERROR_ARGUMENT;
262 res = _SPI_begin_call(true);
266 res = _SPI_execute(src, tcount, NULL);
273 SPI_execp(void *plan, Datum *Values, const char *Nulls, int tcount)
277 if (plan == NULL || tcount < 0)
278 return SPI_ERROR_ARGUMENT;
280 if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL)
281 return SPI_ERROR_PARAM;
283 res = _SPI_begin_call(true);
287 res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, false, tcount);
294 * SPI_execp_current -- identical to SPI_execp, except that we expose the
295 * Executor option to use a current snapshot instead of the normal
296 * QuerySnapshot. This is currently not documented in spi.sgml because
297 * it is only intended for use by RI triggers.
300 SPI_execp_current(void *plan, Datum *Values, const char *Nulls,
301 bool useCurrentSnapshot, int tcount)
305 if (plan == NULL || tcount < 0)
306 return SPI_ERROR_ARGUMENT;
308 if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL)
309 return SPI_ERROR_PARAM;
311 res = _SPI_begin_call(true);
315 res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls,
316 useCurrentSnapshot, tcount);
323 SPI_prepare(const char *src, int nargs, Oid *argtypes)
328 if (src == NULL || nargs < 0 || (nargs > 0 && argtypes == NULL))
330 SPI_result = SPI_ERROR_ARGUMENT;
334 SPI_result = _SPI_begin_call(true);
338 plan.plancxt = NULL; /* doesn't have own context */
341 plan.argtypes = argtypes;
343 SPI_result = _SPI_execute(src, 0, &plan);
345 if (SPI_result >= 0) /* copy plan to procedure context */
346 result = _SPI_copy_plan(&plan, _SPI_CPLAN_PROCXT);
352 return (void *) result;
356 SPI_saveplan(void *plan)
362 SPI_result = SPI_ERROR_ARGUMENT;
366 SPI_result = _SPI_begin_call(false); /* don't change context */
370 newplan = _SPI_copy_plan((_SPI_plan *) plan, _SPI_CPLAN_TOPCXT);
375 return (void *) newplan;
379 SPI_freeplan(void *plan)
381 _SPI_plan *spiplan = (_SPI_plan *) plan;
384 return SPI_ERROR_ARGUMENT;
386 MemoryContextDelete(spiplan->plancxt);
391 SPI_copytuple(HeapTuple tuple)
393 MemoryContext oldcxt = NULL;
398 SPI_result = SPI_ERROR_ARGUMENT;
402 if (_SPI_curid + 1 == _SPI_connected) /* connected */
404 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
405 elog(ERROR, "SPI stack corrupted");
406 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
409 ctuple = heap_copytuple(tuple);
412 MemoryContextSwitchTo(oldcxt);
418 SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
420 MemoryContext oldcxt = NULL;
421 HeapTupleHeader dtup;
423 if (tuple == NULL || tupdesc == NULL)
425 SPI_result = SPI_ERROR_ARGUMENT;
429 /* For RECORD results, make sure a typmod has been assigned */
430 if (tupdesc->tdtypeid == RECORDOID &&
431 tupdesc->tdtypmod < 0)
432 assign_record_type_typmod(tupdesc);
434 if (_SPI_curid + 1 == _SPI_connected) /* connected */
436 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
437 elog(ERROR, "SPI stack corrupted");
438 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
441 dtup = (HeapTupleHeader) palloc(tuple->t_len);
442 memcpy((char *) dtup, (char *) tuple->t_data, tuple->t_len);
444 HeapTupleHeaderSetDatumLength(dtup, tuple->t_len);
445 HeapTupleHeaderSetTypeId(dtup, tupdesc->tdtypeid);
446 HeapTupleHeaderSetTypMod(dtup, tupdesc->tdtypmod);
449 MemoryContextSwitchTo(oldcxt);
455 SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
456 Datum *Values, const char *Nulls)
458 MemoryContext oldcxt = NULL;
460 int numberOfAttributes;
465 if (rel == NULL || tuple == NULL || natts < 0 || attnum == NULL || Values == NULL)
467 SPI_result = SPI_ERROR_ARGUMENT;
471 if (_SPI_curid + 1 == _SPI_connected) /* connected */
473 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
474 elog(ERROR, "SPI stack corrupted");
475 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
478 numberOfAttributes = rel->rd_att->natts;
479 v = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
480 n = (char *) palloc(numberOfAttributes * sizeof(char));
482 /* fetch old values and nulls */
483 heap_deformtuple(tuple, rel->rd_att, v, n);
485 /* replace values and nulls */
486 for (i = 0; i < natts; i++)
488 if (attnum[i] <= 0 || attnum[i] > numberOfAttributes)
490 v[attnum[i] - 1] = Values[i];
491 n[attnum[i] - 1] = (Nulls && Nulls[i] == 'n') ? 'n' : ' ';
494 if (i == natts) /* no errors in *attnum */
496 mtuple = heap_formtuple(rel->rd_att, v, n);
499 * copy the identification info of the old tuple: t_ctid, t_self,
502 mtuple->t_data->t_ctid = tuple->t_data->t_ctid;
503 mtuple->t_self = tuple->t_self;
504 mtuple->t_tableOid = tuple->t_tableOid;
505 if (rel->rd_att->tdhasoid)
506 HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple));
511 SPI_result = SPI_ERROR_NOATTRIBUTE;
518 MemoryContextSwitchTo(oldcxt);
524 SPI_fnumber(TupleDesc tupdesc, const char *fname)
527 Form_pg_attribute sysatt;
529 for (res = 0; res < tupdesc->natts; res++)
531 if (namestrcmp(&tupdesc->attrs[res]->attname, fname) == 0)
535 sysatt = SystemAttributeByName(fname, true /* "oid" will be accepted */ );
537 return sysatt->attnum;
539 /* SPI_ERROR_NOATTRIBUTE is different from all sys column numbers */
540 return SPI_ERROR_NOATTRIBUTE;
544 SPI_fname(TupleDesc tupdesc, int fnumber)
546 Form_pg_attribute att;
550 if (fnumber > tupdesc->natts || fnumber == 0 ||
551 fnumber <= FirstLowInvalidHeapAttributeNumber)
553 SPI_result = SPI_ERROR_NOATTRIBUTE;
558 att = tupdesc->attrs[fnumber - 1];
560 att = SystemAttributeDefinition(fnumber, true);
562 return pstrdup(NameStr(att->attname));
566 SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
580 if (fnumber > tuple->t_data->t_natts || fnumber == 0 ||
581 fnumber <= FirstLowInvalidHeapAttributeNumber)
583 SPI_result = SPI_ERROR_NOATTRIBUTE;
587 origval = heap_getattr(tuple, fnumber, tupdesc, &isnull);
593 typoid = tupdesc->attrs[fnumber - 1]->atttypid;
594 typmod = tupdesc->attrs[fnumber - 1]->atttypmod;
598 typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
602 getTypeOutputInfo(typoid, &foutoid, &typioparam, &typisvarlena);
605 * If we have a toasted datum, forcibly detoast it here to avoid
606 * memory leakage inside the type's output routine.
609 val = PointerGetDatum(PG_DETOAST_DATUM(origval));
613 result = OidFunctionCall3(foutoid,
615 ObjectIdGetDatum(typioparam),
616 Int32GetDatum(typmod));
618 /* Clean up detoasted copy, if any */
620 pfree(DatumGetPointer(val));
622 return DatumGetCString(result);
626 SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
630 if (fnumber > tuple->t_data->t_natts || fnumber == 0 ||
631 fnumber <= FirstLowInvalidHeapAttributeNumber)
633 SPI_result = SPI_ERROR_NOATTRIBUTE;
638 return heap_getattr(tuple, fnumber, tupdesc, isnull);
642 SPI_gettype(TupleDesc tupdesc, int fnumber)
650 if (fnumber > tupdesc->natts || fnumber == 0 ||
651 fnumber <= FirstLowInvalidHeapAttributeNumber)
653 SPI_result = SPI_ERROR_NOATTRIBUTE;
658 typoid = tupdesc->attrs[fnumber - 1]->atttypid;
660 typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
662 typeTuple = SearchSysCache(TYPEOID,
663 ObjectIdGetDatum(typoid),
666 if (!HeapTupleIsValid(typeTuple))
668 SPI_result = SPI_ERROR_TYPUNKNOWN;
672 result = pstrdup(NameStr(((Form_pg_type) GETSTRUCT(typeTuple))->typname));
673 ReleaseSysCache(typeTuple);
678 SPI_gettypeid(TupleDesc tupdesc, int fnumber)
682 if (fnumber > tupdesc->natts || fnumber == 0 ||
683 fnumber <= FirstLowInvalidHeapAttributeNumber)
685 SPI_result = SPI_ERROR_NOATTRIBUTE;
690 return tupdesc->attrs[fnumber - 1]->atttypid;
692 return (SystemAttributeDefinition(fnumber, true))->atttypid;
696 SPI_getrelname(Relation rel)
698 return pstrdup(RelationGetRelationName(rel));
702 SPI_palloc(Size size)
704 MemoryContext oldcxt = NULL;
707 if (_SPI_curid + 1 == _SPI_connected) /* connected */
709 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
710 elog(ERROR, "SPI stack corrupted");
711 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
714 pointer = palloc(size);
717 MemoryContextSwitchTo(oldcxt);
723 SPI_repalloc(void *pointer, Size size)
725 /* No longer need to worry which context chunk was in... */
726 return repalloc(pointer, size);
730 SPI_pfree(void *pointer)
732 /* No longer need to worry which context chunk was in... */
737 SPI_freetuple(HeapTuple tuple)
739 /* No longer need to worry which context tuple was in... */
740 heap_freetuple(tuple);
744 SPI_freetuptable(SPITupleTable *tuptable)
746 if (tuptable != NULL)
747 MemoryContextDelete(tuptable->tuptabcxt);
755 * Open a prepared SPI plan as a portal
758 SPI_cursor_open(const char *name, void *plan, Datum *Values, const char *Nulls)
760 _SPI_plan *spiplan = (_SPI_plan *) plan;
761 List *qtlist = spiplan->qtlist;
762 List *ptlist = spiplan->ptlist;
765 ParamListInfo paramLI;
766 MemoryContext oldcontext;
770 /* Ensure that the plan contains only one regular SELECT query */
771 if (list_length(ptlist) != 1 || list_length(qtlist) != 1)
773 (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
774 errmsg("cannot open multi-query plan as cursor")));
775 queryTree = (Query *) linitial((List *) linitial(qtlist));
776 planTree = (Plan *) linitial(ptlist);
778 if (queryTree->commandType != CMD_SELECT)
780 (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
781 errmsg("cannot open non-SELECT query as cursor")));
782 if (queryTree->into != NULL)
784 (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
785 errmsg("cannot open SELECT INTO query as cursor")));
787 /* Increment CommandCounter to see changes made by now */
788 CommandCounterIncrement();
790 /* Reset SPI result */
793 _SPI_current->processed = 0;
794 _SPI_current->tuptable = NULL;
796 /* Create the portal */
797 if (name == NULL || name[0] == '\0')
799 /* Use a random nonconflicting name */
800 portal = CreateNewPortal();
804 /* In this path, error if portal of same name already exists */
805 portal = CreatePortal(name, false, false);
808 /* Switch to portals memory and copy the parsetree and plan to there */
809 oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
810 queryTree = copyObject(queryTree);
811 planTree = copyObject(planTree);
813 /* If the plan has parameters, set them up */
814 if (spiplan->nargs > 0)
816 paramLI = (ParamListInfo) palloc0((spiplan->nargs + 1) *
817 sizeof(ParamListInfoData));
819 for (k = 0; k < spiplan->nargs; k++)
821 paramLI[k].kind = PARAM_NUM;
822 paramLI[k].id = k + 1;
823 paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
824 if (paramLI[k].isnull)
826 /* nulls just copy */
827 paramLI[k].value = Values[k];
831 /* pass-by-ref values must be copied into portal context */
835 get_typlenbyval(spiplan->argtypes[k],
836 ¶mTypLen, ¶mTypByVal);
837 paramLI[k].value = datumCopy(Values[k],
838 paramTypByVal, paramTypLen);
841 paramLI[k].kind = PARAM_INVALID;
849 PortalDefineQuery(portal,
850 NULL, /* unfortunately don't have sourceText */
851 "SELECT", /* cursor's query is always a SELECT */
852 list_make1(queryTree),
853 list_make1(planTree),
854 PortalGetHeapMemory(portal));
856 MemoryContextSwitchTo(oldcontext);
859 * Set up options for portal.
861 portal->cursorOptions &= ~(CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL);
862 if (ExecSupportsBackwardScan(plan))
863 portal->cursorOptions |= CURSOR_OPT_SCROLL;
865 portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
868 * Start portal execution.
870 PortalStart(portal, paramLI);
872 Assert(portal->strategy == PORTAL_ONE_SELECT);
874 /* Return the created portal */
882 * Find the portal of an existing open cursor
885 SPI_cursor_find(const char *name)
887 return GetPortalByName(name);
894 * Fetch rows in a cursor
897 SPI_cursor_fetch(Portal portal, bool forward, int count)
899 _SPI_cursor_operation(portal, forward, count,
900 CreateDestReceiver(SPI, NULL));
901 /* we know that the SPI receiver doesn't need a destroy call */
911 SPI_cursor_move(Portal portal, bool forward, int count)
913 _SPI_cursor_operation(portal, forward, count, None_Receiver);
923 SPI_cursor_close(Portal portal)
925 if (!PortalIsValid(portal))
926 elog(ERROR, "invalid portal in SPI cursor operation");
928 PortalDrop(portal, false);
932 * Returns the Oid representing the type id for argument at argIndex. First
933 * parameter is at index zero.
936 SPI_getargtypeid(void *plan, int argIndex)
938 if (plan == NULL || argIndex < 0 || argIndex >= ((_SPI_plan*)plan)->nargs)
940 SPI_result = SPI_ERROR_ARGUMENT;
943 return ((_SPI_plan *) plan)->argtypes[argIndex];
947 * Returns the number of arguments for the prepared plan.
950 SPI_getargcount(void *plan)
954 SPI_result = SPI_ERROR_ARGUMENT;
957 return ((_SPI_plan *) plan)->nargs;
961 * Returns true if the plan contains exactly one command
962 * and that command originates from normal SELECT (i.e.
963 * *not* a SELECT ... INTO). In essence, the result indicates
964 * if the command can be used with SPI_cursor_open
967 * plan A plan previously prepared using SPI_prepare
970 SPI_is_cursor_plan(void *plan)
972 _SPI_plan *spiplan = (_SPI_plan *) plan;
977 SPI_result = SPI_ERROR_ARGUMENT;
981 qtlist = spiplan->qtlist;
982 if (list_length(spiplan->ptlist) == 1 && list_length(qtlist) == 1)
984 Query *queryTree = (Query *) linitial((List *) linitial(qtlist));
986 if (queryTree->commandType == CMD_SELECT && queryTree->into == NULL)
992 /* =================== private functions =================== */
996 * Initialize to receive tuples from Executor into SPITupleTable
997 * of current SPI procedure
1000 spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
1002 SPITupleTable *tuptable;
1003 MemoryContext oldcxt;
1004 MemoryContext tuptabcxt;
1007 * When called by Executor _SPI_curid expected to be equal to
1010 if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
1011 elog(ERROR, "improper call to spi_dest_startup");
1012 if (_SPI_current != &(_SPI_stack[_SPI_curid]))
1013 elog(ERROR, "SPI stack corrupted");
1015 if (_SPI_current->tuptable != NULL)
1016 elog(ERROR, "improper call to spi_dest_startup");
1018 oldcxt = _SPI_procmem(); /* switch to procedure memory context */
1020 tuptabcxt = AllocSetContextCreate(CurrentMemoryContext,
1022 ALLOCSET_DEFAULT_MINSIZE,
1023 ALLOCSET_DEFAULT_INITSIZE,
1024 ALLOCSET_DEFAULT_MAXSIZE);
1025 MemoryContextSwitchTo(tuptabcxt);
1027 _SPI_current->tuptable = tuptable = (SPITupleTable *)
1028 palloc(sizeof(SPITupleTable));
1029 tuptable->tuptabcxt = tuptabcxt;
1030 tuptable->alloced = tuptable->free = 128;
1031 tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
1032 tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
1034 MemoryContextSwitchTo(oldcxt);
1039 * store tuple retrieved by Executor into SPITupleTable
1040 * of current SPI procedure
1043 spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
1045 SPITupleTable *tuptable;
1046 MemoryContext oldcxt;
1049 * When called by Executor _SPI_curid expected to be equal to
1052 if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
1053 elog(ERROR, "improper call to spi_printtup");
1054 if (_SPI_current != &(_SPI_stack[_SPI_curid]))
1055 elog(ERROR, "SPI stack corrupted");
1057 tuptable = _SPI_current->tuptable;
1058 if (tuptable == NULL)
1059 elog(ERROR, "improper call to spi_printtup");
1061 oldcxt = MemoryContextSwitchTo(tuptable->tuptabcxt);
1063 if (tuptable->free == 0)
1065 tuptable->free = 256;
1066 tuptable->alloced += tuptable->free;
1067 tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
1068 tuptable->alloced * sizeof(HeapTuple));
1071 tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple);
1074 MemoryContextSwitchTo(oldcxt);
1082 * Plan and optionally execute a querystring.
1084 * If plan != NULL, just prepare plan trees and save them in *plan;
1085 * else execute immediately.
1088 _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
1090 List *raw_parsetree_list;
1091 List *query_list_list;
1093 ListCell *list_item;
1094 ErrorContextCallback spierrcontext;
1096 Oid *argtypes = NULL;
1101 nargs = plan->nargs;
1102 argtypes = plan->argtypes;
1105 /* Increment CommandCounter to see changes made by now */
1106 CommandCounterIncrement();
1108 /* Reset state (only needed in case string is empty) */
1110 SPI_lastoid = InvalidOid;
1111 SPI_tuptable = NULL;
1112 _SPI_current->tuptable = NULL;
1115 * Setup error traceback support for ereport()
1117 spierrcontext.callback = _SPI_error_callback;
1118 spierrcontext.arg = (void *) src;
1119 spierrcontext.previous = error_context_stack;
1120 error_context_stack = &spierrcontext;
1123 * Parse the request string into a list of raw parse trees.
1125 raw_parsetree_list = pg_parse_query(src);
1128 * Do parse analysis and rule rewrite for each raw parsetree.
1130 * We save the querytrees from each raw parsetree as a separate sublist.
1131 * This allows _SPI_execute_plan() to know where the boundaries
1132 * between original queries fall.
1134 query_list_list = NIL;
1137 foreach(list_item, raw_parsetree_list)
1139 Node *parsetree = (Node *) lfirst(list_item);
1141 ListCell *query_list_item;
1143 query_list = pg_analyze_and_rewrite(parsetree, argtypes, nargs);
1145 query_list_list = lappend(query_list_list, query_list);
1147 /* Reset state for each original parsetree */
1148 /* (at most one of its querytrees will be marked canSetTag) */
1150 SPI_lastoid = InvalidOid;
1151 SPI_tuptable = NULL;
1152 _SPI_current->tuptable = NULL;
1154 foreach(query_list_item, query_list)
1156 Query *queryTree = (Query *) lfirst(query_list_item);
1161 planTree = pg_plan_query(queryTree, NULL);
1162 plan_list = lappend(plan_list, planTree);
1164 dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL);
1165 if (queryTree->commandType == CMD_UTILITY)
1167 if (IsA(queryTree->utilityStmt, CopyStmt))
1169 CopyStmt *stmt = (CopyStmt *) queryTree->utilityStmt;
1171 if (stmt->filename == NULL)
1173 res = SPI_ERROR_COPY;
1177 else if (IsA(queryTree->utilityStmt, DeclareCursorStmt) ||
1178 IsA(queryTree->utilityStmt, ClosePortalStmt) ||
1179 IsA(queryTree->utilityStmt, FetchStmt))
1181 res = SPI_ERROR_CURSOR;
1184 else if (IsA(queryTree->utilityStmt, TransactionStmt))
1186 res = SPI_ERROR_TRANSACTION;
1189 res = SPI_OK_UTILITY;
1192 ProcessUtility(queryTree->utilityStmt, dest, NULL);
1193 CommandCounterIncrement();
1196 else if (plan == NULL)
1198 qdesc = CreateQueryDesc(queryTree, planTree, dest,
1200 res = _SPI_pquery(qdesc, true, false,
1201 queryTree->canSetTag ? tcount : 0);
1204 CommandCounterIncrement();
1208 qdesc = CreateQueryDesc(queryTree, planTree, dest,
1210 res = _SPI_pquery(qdesc, false, false, 0);
1219 plan->qtlist = query_list_list;
1220 plan->ptlist = plan_list;
1226 * Pop the error context stack
1228 error_context_stack = spierrcontext.previous;
1234 _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
1235 bool useCurrentSnapshot, int tcount)
1237 List *query_list_list = plan->qtlist;
1238 ListCell *plan_list_item = list_head(plan->ptlist);
1239 ListCell *query_list_list_item;
1240 ErrorContextCallback spierrcontext;
1241 int nargs = plan->nargs;
1243 ParamListInfo paramLI;
1245 /* Increment CommandCounter to see changes made by now */
1246 CommandCounterIncrement();
1248 /* Convert parameters to form wanted by executor */
1253 paramLI = (ParamListInfo)
1254 palloc0((nargs + 1) * sizeof(ParamListInfoData));
1256 for (k = 0; k < nargs; k++)
1258 paramLI[k].kind = PARAM_NUM;
1259 paramLI[k].id = k + 1;
1260 paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
1261 paramLI[k].value = Values[k];
1263 paramLI[k].kind = PARAM_INVALID;
1268 /* Reset state (only needed in case string is empty) */
1270 SPI_lastoid = InvalidOid;
1271 SPI_tuptable = NULL;
1272 _SPI_current->tuptable = NULL;
1275 * Setup error traceback support for ereport()
1277 spierrcontext.callback = _SPI_error_callback;
1278 spierrcontext.arg = (void *) plan->query;
1279 spierrcontext.previous = error_context_stack;
1280 error_context_stack = &spierrcontext;
1282 foreach(query_list_list_item, query_list_list)
1284 List *query_list = lfirst(query_list_list_item);
1285 ListCell *query_list_item;
1287 /* Reset state for each original parsetree */
1288 /* (at most one of its querytrees will be marked canSetTag) */
1290 SPI_lastoid = InvalidOid;
1291 SPI_tuptable = NULL;
1292 _SPI_current->tuptable = NULL;
1294 foreach(query_list_item, query_list)
1296 Query *queryTree = (Query *) lfirst(query_list_item);
1301 planTree = lfirst(plan_list_item);
1302 plan_list_item = lnext(plan_list_item);
1304 dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL);
1305 if (queryTree->commandType == CMD_UTILITY)
1307 ProcessUtility(queryTree->utilityStmt, dest, NULL);
1308 res = SPI_OK_UTILITY;
1309 CommandCounterIncrement();
1313 qdesc = CreateQueryDesc(queryTree, planTree, dest,
1315 res = _SPI_pquery(qdesc, true, useCurrentSnapshot,
1316 queryTree->canSetTag ? tcount : 0);
1319 CommandCounterIncrement();
1327 * Pop the error context stack
1329 error_context_stack = spierrcontext.previous;
1335 _SPI_pquery(QueryDesc *queryDesc, bool runit,
1336 bool useCurrentSnapshot, int tcount)
1338 int operation = queryDesc->operation;
1345 res = SPI_OK_SELECT;
1346 if (queryDesc->parsetree->into != NULL) /* select into table */
1348 res = SPI_OK_SELINTO;
1349 queryDesc->dest = None_Receiver; /* don't output results */
1353 res = SPI_OK_INSERT;
1356 res = SPI_OK_DELETE;
1359 res = SPI_OK_UPDATE;
1362 return SPI_ERROR_OPUNKNOWN;
1365 if (!runit) /* plan preparation, don't execute */
1368 #ifdef SPI_EXECUTOR_STATS
1369 if (ShowExecutorStats)
1373 ExecutorStart(queryDesc, useCurrentSnapshot, false);
1375 ExecutorRun(queryDesc, ForwardScanDirection, (long) tcount);
1377 _SPI_current->processed = queryDesc->estate->es_processed;
1378 save_lastoid = queryDesc->estate->es_lastoid;
1380 if (operation == CMD_SELECT && queryDesc->dest->mydest == SPI)
1382 if (_SPI_checktuples())
1383 elog(ERROR, "consistency check on SPI tuple count failed");
1386 if (queryDesc->dest->mydest == SPI)
1388 SPI_processed = _SPI_current->processed;
1389 SPI_lastoid = save_lastoid;
1390 SPI_tuptable = _SPI_current->tuptable;
1392 else if (res == SPI_OK_SELECT)
1394 /* Don't return SPI_OK_SELECT if we discarded the result */
1395 res = SPI_OK_UTILITY;
1398 ExecutorEnd(queryDesc);
1400 FreeQueryDesc(queryDesc);
1402 #ifdef SPI_EXECUTOR_STATS
1403 if (ShowExecutorStats)
1404 ShowUsage("SPI EXECUTOR STATS");
1411 * _SPI_error_callback
1413 * Add context information when a query invoked via SPI fails
1416 _SPI_error_callback(void *arg)
1418 const char *query = (const char *) arg;
1419 int syntaxerrposition;
1422 * If there is a syntax error position, convert to internal syntax error;
1423 * otherwise treat the query as an item of context stack
1425 syntaxerrposition = geterrposition();
1426 if (syntaxerrposition > 0)
1429 internalerrposition(syntaxerrposition);
1430 internalerrquery(query);
1433 errcontext("SQL query \"%s\"", query);
1437 * _SPI_cursor_operation()
1439 * Do a FETCH or MOVE in a cursor
1442 _SPI_cursor_operation(Portal portal, bool forward, int count,
1447 /* Check that the portal is valid */
1448 if (!PortalIsValid(portal))
1449 elog(ERROR, "invalid portal in SPI cursor operation");
1451 /* Push the SPI stack */
1452 if (_SPI_begin_call(true) < 0)
1453 elog(ERROR, "SPI cursor operation called while not connected");
1455 /* Reset the SPI result */
1457 SPI_tuptable = NULL;
1458 _SPI_current->processed = 0;
1459 _SPI_current->tuptable = NULL;
1461 /* Run the cursor */
1462 nfetched = PortalRunFetch(portal,
1463 forward ? FETCH_FORWARD : FETCH_BACKWARD,
1468 * Think not to combine this store with the preceding function call.
1469 * If the portal contains calls to functions that use SPI, then
1470 * SPI_stack is likely to move around while the portal runs. When
1471 * control returns, _SPI_current will point to the correct stack
1472 * entry... but the pointer may be different than it was beforehand.
1473 * So we must be sure to re-fetch the pointer after the function call
1476 _SPI_current->processed = nfetched;
1478 if (dest->mydest == SPI && _SPI_checktuples())
1479 elog(ERROR, "consistency check on SPI tuple count failed");
1481 /* Put the result into place for access by caller */
1482 SPI_processed = _SPI_current->processed;
1483 SPI_tuptable = _SPI_current->tuptable;
1485 /* Pop the SPI stack */
1486 _SPI_end_call(true);
1490 static MemoryContext
1493 return MemoryContextSwitchTo(_SPI_current->execCxt);
1496 static MemoryContext
1499 return MemoryContextSwitchTo(_SPI_current->procCxt);
1503 * _SPI_begin_call: begin a SPI operation within a connected procedure
1506 _SPI_begin_call(bool execmem)
1508 if (_SPI_curid + 1 != _SPI_connected)
1509 return SPI_ERROR_UNCONNECTED;
1511 if (_SPI_current != &(_SPI_stack[_SPI_curid]))
1512 elog(ERROR, "SPI stack corrupted");
1514 if (execmem) /* switch to the Executor memory context */
1521 * _SPI_end_call: end a SPI operation within a connected procedure
1523 * Note: this currently has no failure return cases, so callers don't check
1526 _SPI_end_call(bool procmem)
1529 * We're returning to procedure where _SPI_curid == _SPI_connected - 1
1533 if (procmem) /* switch to the procedure memory context */
1536 /* and free Executor memory */
1537 MemoryContextResetAndDeleteChildren(_SPI_current->execCxt);
1544 _SPI_checktuples(void)
1546 uint32 processed = _SPI_current->processed;
1547 SPITupleTable *tuptable = _SPI_current->tuptable;
1548 bool failed = false;
1550 if (tuptable == NULL) /* spi_dest_startup was not called */
1552 else if (processed != (tuptable->alloced - tuptable->free))
1559 _SPI_copy_plan(_SPI_plan *plan, int location)
1562 MemoryContext oldcxt;
1563 MemoryContext plancxt;
1564 MemoryContext parentcxt;
1566 /* Determine correct parent for the plan's memory context */
1567 if (location == _SPI_CPLAN_PROCXT)
1568 parentcxt = _SPI_current->procCxt;
1569 else if (location == _SPI_CPLAN_TOPCXT)
1570 parentcxt = TopMemoryContext;
1571 else /* (this case not currently used) */
1572 parentcxt = CurrentMemoryContext;
1575 * Create a memory context for the plan. We don't expect the plan to
1576 * be very large, so use smaller-than-default alloc parameters.
1578 plancxt = AllocSetContextCreate(parentcxt,
1580 ALLOCSET_SMALL_MINSIZE,
1581 ALLOCSET_SMALL_INITSIZE,
1582 ALLOCSET_SMALL_MAXSIZE);
1583 oldcxt = MemoryContextSwitchTo(plancxt);
1585 /* Copy the SPI plan into its own context */
1586 newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan));
1587 newplan->plancxt = plancxt;
1588 newplan->query = pstrdup(plan->query);
1589 newplan->qtlist = (List *) copyObject(plan->qtlist);
1590 newplan->ptlist = (List *) copyObject(plan->ptlist);
1591 newplan->nargs = plan->nargs;
1592 if (plan->nargs > 0)
1594 newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
1595 memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
1598 newplan->argtypes = NULL;
1600 MemoryContextSwitchTo(oldcxt);