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.82 2002/12/17 15:51:59 tgl Exp $
13 *-------------------------------------------------------------------------
17 #include "access/printtup.h"
18 #include "catalog/heap.h"
19 #include "commands/portalcmds.h"
20 #include "executor/spi_priv.h"
21 #include "tcop/tcopprot.h"
22 #include "utils/lsyscache.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_connected = -1;
33 static int _SPI_curid = -1;
35 static int _SPI_execute(char *src, int tcount, _SPI_plan *plan);
36 static int _SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount);
38 static int _SPI_execute_plan(_SPI_plan *plan,
39 Datum *Values, char *Nulls, int tcount);
41 static void _SPI_cursor_operation(Portal portal, bool forward, int count,
44 static _SPI_plan *_SPI_copy_plan(_SPI_plan *plan, int location);
46 static int _SPI_begin_call(bool execmem);
47 static int _SPI_end_call(bool procmem);
48 static MemoryContext _SPI_execmem(void);
49 static MemoryContext _SPI_procmem(void);
50 static bool _SPI_checktuples(void);
53 /* =================== interface functions =================== */
58 _SPI_connection *new_SPI_stack;
61 * When procedure called by Executor _SPI_curid expected to be equal
64 if (_SPI_curid != _SPI_connected)
65 return SPI_ERROR_CONNECT;
67 if (_SPI_stack == NULL)
69 if (_SPI_connected != -1)
70 elog(FATAL, "SPI_connect: no connection(s) expected");
71 new_SPI_stack = (_SPI_connection *) malloc(sizeof(_SPI_connection));
75 if (_SPI_connected <= -1)
76 elog(FATAL, "SPI_connect: some connection(s) expected");
77 new_SPI_stack = (_SPI_connection *) realloc(_SPI_stack,
78 (_SPI_connected + 2) * sizeof(_SPI_connection));
81 if (new_SPI_stack == NULL)
82 elog(ERROR, "Memory exhausted in SPI_connect");
85 * We' returning to procedure where _SPI_curid == _SPI_connected - 1
87 _SPI_stack = new_SPI_stack;
90 _SPI_current = &(_SPI_stack[_SPI_connected]);
91 _SPI_current->processed = 0;
92 _SPI_current->tuptable = NULL;
94 /* Create memory contexts for this procedure */
95 _SPI_current->procCxt = AllocSetContextCreate(TopTransactionContext,
97 ALLOCSET_DEFAULT_MINSIZE,
98 ALLOCSET_DEFAULT_INITSIZE,
99 ALLOCSET_DEFAULT_MAXSIZE);
100 _SPI_current->execCxt = AllocSetContextCreate(TopTransactionContext,
102 ALLOCSET_DEFAULT_MINSIZE,
103 ALLOCSET_DEFAULT_INITSIZE,
104 ALLOCSET_DEFAULT_MAXSIZE);
105 /* ... and switch to procedure's context */
106 _SPI_current->savedcxt = MemoryContextSwitchTo(_SPI_current->procCxt);
108 return SPI_OK_CONNECT;
116 res = _SPI_begin_call(false); /* live in procedure memory */
120 /* Restore memory context as it was before procedure call */
121 MemoryContextSwitchTo(_SPI_current->savedcxt);
123 /* Release memory used in procedure call */
124 MemoryContextDelete(_SPI_current->execCxt);
125 MemoryContextDelete(_SPI_current->procCxt);
128 * After _SPI_begin_call _SPI_connected == _SPI_curid. Now we are
129 * closing connection to SPI and returning to upper Executor and so
130 * _SPI_connected must be equal to _SPI_curid.
134 if (_SPI_connected == -1)
142 _SPI_connection *new_SPI_stack;
144 new_SPI_stack = (_SPI_connection *) realloc(_SPI_stack,
145 (_SPI_connected + 1) * sizeof(_SPI_connection));
146 /* This could only fail with a pretty stupid malloc package ... */
147 if (new_SPI_stack == NULL)
148 elog(ERROR, "Memory exhausted in SPI_finish");
149 _SPI_stack = new_SPI_stack;
150 _SPI_current = &(_SPI_stack[_SPI_connected]);
153 return SPI_OK_FINISH;
158 * Clean up SPI state at transaction commit or abort (we don't care which).
164 * Note that memory contexts belonging to SPI stack entries will be
165 * freed automatically, so we can ignore them here. We just need to
166 * restore our static variables to initial state.
168 if (_SPI_stack != NULL) /* there was abort */
170 _SPI_current = _SPI_stack = NULL;
171 _SPI_connected = _SPI_curid = -1;
173 SPI_lastoid = InvalidOid;
190 SPI_exec(char *src, int tcount)
194 if (src == NULL || tcount < 0)
195 return SPI_ERROR_ARGUMENT;
197 res = _SPI_begin_call(true);
201 res = _SPI_execute(src, tcount, NULL);
208 SPI_execp(void *plan, Datum *Values, char *Nulls, int tcount)
212 if (plan == NULL || tcount < 0)
213 return SPI_ERROR_ARGUMENT;
215 if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL)
216 return SPI_ERROR_PARAM;
218 res = _SPI_begin_call(true);
222 res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, tcount);
229 SPI_prepare(char *src, int nargs, Oid *argtypes)
233 if (src == NULL || nargs < 0 || (nargs > 0 && argtypes == NULL))
235 SPI_result = SPI_ERROR_ARGUMENT;
239 SPI_result = _SPI_begin_call(true);
243 plan = (_SPI_plan *) palloc(sizeof(_SPI_plan)); /* Executor context */
244 plan->argtypes = argtypes;
247 SPI_result = _SPI_execute(src, 0, plan);
249 if (SPI_result >= 0) /* copy plan to procedure context */
250 plan = _SPI_copy_plan(plan, _SPI_CPLAN_PROCXT);
256 return (void *) plan;
260 SPI_saveplan(void *plan)
266 SPI_result = SPI_ERROR_ARGUMENT;
270 SPI_result = _SPI_begin_call(false); /* don't change context */
274 newplan = _SPI_copy_plan((_SPI_plan *) plan, _SPI_CPLAN_TOPCXT);
279 return (void *) newplan;
284 SPI_freeplan(void *plan)
286 _SPI_plan *spiplan = (_SPI_plan *) plan;
289 return SPI_ERROR_ARGUMENT;
291 MemoryContextDelete(spiplan->plancxt);
296 SPI_copytuple(HeapTuple tuple)
298 MemoryContext oldcxt = NULL;
303 SPI_result = SPI_ERROR_ARGUMENT;
307 if (_SPI_curid + 1 == _SPI_connected) /* connected */
309 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
310 elog(FATAL, "SPI: stack corrupted");
311 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
314 ctuple = heap_copytuple(tuple);
317 MemoryContextSwitchTo(oldcxt);
323 SPI_copytupledesc(TupleDesc tupdesc)
325 MemoryContext oldcxt = NULL;
330 SPI_result = SPI_ERROR_ARGUMENT;
334 if (_SPI_curid + 1 == _SPI_connected) /* connected */
336 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
337 elog(FATAL, "SPI: stack corrupted");
338 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
341 ctupdesc = CreateTupleDescCopy(tupdesc);
344 MemoryContextSwitchTo(oldcxt);
350 SPI_copytupleintoslot(HeapTuple tuple, TupleDesc tupdesc)
352 MemoryContext oldcxt = NULL;
353 TupleTableSlot *cslot;
357 if (tuple == NULL || tupdesc == NULL)
359 SPI_result = SPI_ERROR_ARGUMENT;
363 if (_SPI_curid + 1 == _SPI_connected) /* connected */
365 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
366 elog(FATAL, "SPI: stack corrupted");
367 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
370 ctuple = heap_copytuple(tuple);
371 ctupdesc = CreateTupleDescCopy(tupdesc);
373 cslot = MakeTupleTableSlot();
374 ExecSetSlotDescriptor(cslot, ctupdesc, true);
375 cslot = ExecStoreTuple(ctuple, cslot, InvalidBuffer, true);
378 MemoryContextSwitchTo(oldcxt);
384 SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
385 Datum *Values, char *Nulls)
387 MemoryContext oldcxt = NULL;
389 int numberOfAttributes;
395 if (rel == NULL || tuple == NULL || natts <= 0 || attnum == NULL || Values == NULL)
397 SPI_result = SPI_ERROR_ARGUMENT;
401 if (_SPI_curid + 1 == _SPI_connected) /* connected */
403 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
404 elog(FATAL, "SPI: stack corrupted");
405 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
408 numberOfAttributes = rel->rd_att->natts;
409 v = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
410 n = (char *) palloc(numberOfAttributes * sizeof(char));
412 /* fetch old values and nulls */
413 for (i = 0; i < numberOfAttributes; i++)
415 v[i] = heap_getattr(tuple, i + 1, rel->rd_att, &isnull);
416 n[i] = (isnull) ? 'n' : ' ';
419 /* replace values and nulls */
420 for (i = 0; i < natts; i++)
422 if (attnum[i] <= 0 || attnum[i] > numberOfAttributes)
424 v[attnum[i] - 1] = Values[i];
425 n[attnum[i] - 1] = (Nulls && Nulls[i] == 'n') ? 'n' : ' ';
428 if (i == natts) /* no errors in *attnum */
430 mtuple = heap_formtuple(rel->rd_att, v, n);
433 * copy the identification info of the old tuple: t_ctid, t_self,
436 mtuple->t_data->t_ctid = tuple->t_data->t_ctid;
437 mtuple->t_self = tuple->t_self;
438 mtuple->t_tableOid = tuple->t_tableOid;
439 if (rel->rd_rel->relhasoids)
440 HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple));
445 SPI_result = SPI_ERROR_NOATTRIBUTE;
452 MemoryContextSwitchTo(oldcxt);
458 SPI_fnumber(TupleDesc tupdesc, char *fname)
461 Form_pg_attribute sysatt;
463 for (res = 0; res < tupdesc->natts; res++)
465 if (namestrcmp(&tupdesc->attrs[res]->attname, fname) == 0)
469 sysatt = SystemAttributeByName(fname, true /* "oid" will be accepted */ );
471 return sysatt->attnum;
473 /* SPI_ERROR_NOATTRIBUTE is different from all sys column numbers */
474 return SPI_ERROR_NOATTRIBUTE;
478 SPI_fname(TupleDesc tupdesc, int fnumber)
480 Form_pg_attribute att;
484 if (fnumber > tupdesc->natts || fnumber == 0 ||
485 fnumber <= FirstLowInvalidHeapAttributeNumber)
487 SPI_result = SPI_ERROR_NOATTRIBUTE;
492 att = tupdesc->attrs[fnumber - 1];
494 att = SystemAttributeDefinition(fnumber, true);
496 return pstrdup(NameStr(att->attname));
500 SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
514 if (fnumber > tuple->t_data->t_natts || fnumber == 0 ||
515 fnumber <= FirstLowInvalidHeapAttributeNumber)
517 SPI_result = SPI_ERROR_NOATTRIBUTE;
521 origval = heap_getattr(tuple, fnumber, tupdesc, &isnull);
527 typoid = tupdesc->attrs[fnumber - 1]->atttypid;
528 typmod = tupdesc->attrs[fnumber - 1]->atttypmod;
532 typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
536 if (!getTypeOutputInfo(typoid, &foutoid, &typelem, &typisvarlena))
538 SPI_result = SPI_ERROR_NOOUTFUNC;
543 * If we have a toasted datum, forcibly detoast it here to avoid
544 * memory leakage inside the type's output routine.
547 val = PointerGetDatum(PG_DETOAST_DATUM(origval));
551 result = OidFunctionCall3(foutoid,
553 ObjectIdGetDatum(typelem),
554 Int32GetDatum(typmod));
556 /* Clean up detoasted copy, if any */
558 pfree(DatumGetPointer(val));
560 return DatumGetCString(result);
564 SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
568 if (fnumber > tuple->t_data->t_natts || fnumber == 0 ||
569 fnumber <= FirstLowInvalidHeapAttributeNumber)
571 SPI_result = SPI_ERROR_NOATTRIBUTE;
576 return heap_getattr(tuple, fnumber, tupdesc, isnull);
580 SPI_gettype(TupleDesc tupdesc, int fnumber)
588 if (fnumber > tupdesc->natts || fnumber == 0 ||
589 fnumber <= FirstLowInvalidHeapAttributeNumber)
591 SPI_result = SPI_ERROR_NOATTRIBUTE;
596 typoid = tupdesc->attrs[fnumber - 1]->atttypid;
598 typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
600 typeTuple = SearchSysCache(TYPEOID,
601 ObjectIdGetDatum(typoid),
604 if (!HeapTupleIsValid(typeTuple))
606 SPI_result = SPI_ERROR_TYPUNKNOWN;
610 result = pstrdup(NameStr(((Form_pg_type) GETSTRUCT(typeTuple))->typname));
611 ReleaseSysCache(typeTuple);
616 SPI_gettypeid(TupleDesc tupdesc, int fnumber)
620 if (fnumber > tupdesc->natts || fnumber == 0 ||
621 fnumber <= FirstLowInvalidHeapAttributeNumber)
623 SPI_result = SPI_ERROR_NOATTRIBUTE;
628 return tupdesc->attrs[fnumber - 1]->atttypid;
630 return (SystemAttributeDefinition(fnumber, true))->atttypid;
634 SPI_getrelname(Relation rel)
636 return pstrdup(RelationGetRelationName(rel));
640 SPI_palloc(Size size)
642 MemoryContext oldcxt = NULL;
645 if (_SPI_curid + 1 == _SPI_connected) /* connected */
647 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
648 elog(FATAL, "SPI: stack corrupted");
649 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
652 pointer = palloc(size);
655 MemoryContextSwitchTo(oldcxt);
661 SPI_repalloc(void *pointer, Size size)
663 /* No longer need to worry which context chunk was in... */
664 return repalloc(pointer, size);
668 SPI_pfree(void *pointer)
670 /* No longer need to worry which context chunk was in... */
675 SPI_freetuple(HeapTuple tuple)
677 /* No longer need to worry which context tuple was in... */
678 heap_freetuple(tuple);
682 SPI_freetuptable(SPITupleTable *tuptable)
684 if (tuptable != NULL)
685 MemoryContextDelete(tuptable->tuptabcxt);
693 * Open a prepared SPI plan as a portal
696 SPI_cursor_open(char *name, void *plan, Datum *Values, char *Nulls)
698 static int unnamed_portal_count = 0;
700 _SPI_plan *spiplan = (_SPI_plan *) plan;
701 List *qtlist = spiplan->qtlist;
702 List *ptlist = spiplan->ptlist;
705 ParamListInfo paramLI;
706 QueryDesc *queryDesc;
707 MemoryContext oldcontext;
712 /* Ensure that the plan contains only one regular SELECT query */
713 if (length(ptlist) != 1 || length(qtlist) != 1)
714 elog(ERROR, "cannot open multi-query plan as cursor");
715 queryTree = (Query *) lfirst((List *) lfirst(qtlist));
716 planTree = (Plan *) lfirst(ptlist);
718 if (queryTree->commandType != CMD_SELECT)
719 elog(ERROR, "plan in SPI_cursor_open() is not a SELECT");
720 if (queryTree->isPortal)
721 elog(ERROR, "plan in SPI_cursor_open() must NOT be a DECLARE already");
722 else if (queryTree->into != NULL)
723 elog(ERROR, "plan in SPI_cursor_open() must NOT be a SELECT INTO");
725 /* Increment CommandCounter to see changes made by now */
726 CommandCounterIncrement();
728 /* Reset SPI result */
731 _SPI_current->processed = 0;
732 _SPI_current->tuptable = NULL;
736 /* Make up a portal name if none given */
739 unnamed_portal_count++;
740 if (unnamed_portal_count < 0)
741 unnamed_portal_count = 0;
742 sprintf(portalname, "<unnamed cursor %d>", unnamed_portal_count);
743 if (GetPortalByName(portalname) == NULL)
751 /* Ensure the portal doesn't exist already */
752 portal = GetPortalByName(name);
754 elog(ERROR, "cursor \"%s\" already in use", name);
757 /* Create the portal */
758 portal = CreatePortal(name);
760 elog(ERROR, "failed to create portal \"%s\"", name);
762 /* Switch to portals memory and copy the parsetree and plan to there */
763 oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
764 queryTree = copyObject(queryTree);
765 planTree = copyObject(planTree);
767 /* Modify the parsetree to be a cursor */
768 queryTree->isPortal = true;
769 queryTree->into = makeNode(RangeVar);
770 queryTree->into->relname = pstrdup(name);
771 queryTree->isBinary = false;
773 /* If the plan has parameters, set them up */
774 if (spiplan->nargs > 0)
776 paramLI = (ParamListInfo) palloc0((spiplan->nargs + 1) *
777 sizeof(ParamListInfoData));
779 for (k = 0; k < spiplan->nargs; k++)
781 paramLI[k].kind = PARAM_NUM;
782 paramLI[k].id = k + 1;
783 paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
784 if (paramLI[k].isnull)
786 /* nulls just copy */
787 paramLI[k].value = Values[k];
791 /* pass-by-ref values must be copied into portal context */
795 get_typlenbyval(spiplan->argtypes[k],
796 ¶mTypLen, ¶mTypByVal);
797 paramLI[k].value = datumCopy(Values[k],
798 paramTypByVal, paramTypLen);
801 paramLI[k].kind = PARAM_INVALID;
806 /* Create the QueryDesc object */
807 queryDesc = CreateQueryDesc(queryTree, planTree, SPI, NULL,
810 /* Start the executor */
811 ExecutorStart(queryDesc);
813 /* Arrange to shut down the executor if portal is dropped */
814 PortalSetQuery(portal, queryDesc, PortalCleanup);
816 /* Switch back to the callers memory context */
817 MemoryContextSwitchTo(oldcontext);
819 /* Return the created portal */
827 * Find the portal of an existing open cursor
830 SPI_cursor_find(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");
874 /* =================== private functions =================== */
878 * store tuple retrieved by Executor into SPITupleTable
879 * of current SPI procedure
883 spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
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_printtup");
895 if (_SPI_current != &(_SPI_stack[_SPI_curid]))
896 elog(FATAL, "SPI: stack corrupted in spi_printtup");
898 oldcxt = _SPI_procmem(); /* switch to procedure memory context */
900 tuptable = _SPI_current->tuptable;
901 if (tuptable == NULL)
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(tupdesc);
919 MemoryContextSwitchTo(tuptable->tuptabcxt);
921 if (tuptable->free == 0)
923 tuptable->free = 256;
924 tuptable->alloced += tuptable->free;
925 tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
926 tuptable->alloced * sizeof(HeapTuple));
930 tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple);
933 MemoryContextSwitchTo(oldcxt);
942 * Plan and optionally execute a querystring.
944 * If plan != NULL, just prepare plan tree, else execute immediately.
947 _SPI_execute(char *src, int tcount, _SPI_plan *plan)
950 List *raw_parsetree_list;
951 List *query_list_list;
955 Oid *argtypes = NULL;
961 argtypes = plan->argtypes;
964 /* Increment CommandCounter to see changes made by now */
965 CommandCounterIncrement();
967 /* Reset state (only needed in case string is empty) */
969 SPI_lastoid = InvalidOid;
971 _SPI_current->tuptable = NULL;
974 * Parse the request string into a list of raw parse trees.
976 initStringInfo(&stri);
977 appendStringInfo(&stri, "%s", src);
979 raw_parsetree_list = pg_parse_query(&stri, argtypes, nargs);
982 * Do parse analysis and rule rewrite for each raw parsetree.
984 * We save the querytrees from each raw parsetree as a separate
985 * sublist. This allows _SPI_execute_plan() to know where the
986 * boundaries between original queries fall.
988 query_list_list = NIL;
991 foreach(list_item, raw_parsetree_list)
993 Node *parsetree = (Node *) lfirst(list_item);
995 bool foundOriginalQuery = false;
997 List *query_list_item;
999 switch (nodeTag(parsetree))
1002 origCmdType = CMD_INSERT;
1005 origCmdType = CMD_DELETE;
1008 origCmdType = CMD_UPDATE;
1011 origCmdType = CMD_SELECT;
1014 /* Otherwise, never match commandType */
1015 origCmdType = CMD_UNKNOWN;
1020 plan->origCmdType = origCmdType;
1022 query_list = pg_analyze_and_rewrite(parsetree);
1024 query_list_list = lappend(query_list_list, query_list);
1026 /* Reset state for each original parsetree */
1028 SPI_lastoid = InvalidOid;
1029 SPI_tuptable = NULL;
1030 _SPI_current->tuptable = NULL;
1032 foreach(query_list_item, query_list)
1034 Query *queryTree = (Query *) lfirst(query_list_item);
1039 planTree = pg_plan_query(queryTree);
1040 plan_list = lappend(plan_list, planTree);
1043 * This query can set the SPI result if it is the original
1044 * query, or if it is an INSTEAD query of the same kind as the
1045 * original and we haven't yet seen the original query.
1047 if (queryTree->querySource == QSRC_ORIGINAL)
1049 canSetResult = true;
1050 foundOriginalQuery = true;
1052 else if (!foundOriginalQuery &&
1053 queryTree->commandType == origCmdType &&
1054 (queryTree->querySource == QSRC_INSTEAD_RULE ||
1055 queryTree->querySource == QSRC_QUAL_INSTEAD_RULE))
1056 canSetResult = true;
1058 canSetResult = false;
1060 if (queryTree->commandType == CMD_UTILITY)
1062 if (IsA(queryTree->utilityStmt, CopyStmt))
1064 CopyStmt *stmt = (CopyStmt *) queryTree->utilityStmt;
1066 if (stmt->filename == NULL)
1067 return SPI_ERROR_COPY;
1069 else if (IsA(queryTree->utilityStmt, ClosePortalStmt) ||
1070 IsA(queryTree->utilityStmt, FetchStmt))
1071 return SPI_ERROR_CURSOR;
1072 else if (IsA(queryTree->utilityStmt, TransactionStmt))
1073 return SPI_ERROR_TRANSACTION;
1074 res = SPI_OK_UTILITY;
1077 ProcessUtility(queryTree->utilityStmt, None, NULL);
1078 CommandCounterIncrement();
1081 else if (plan == NULL)
1083 qdesc = CreateQueryDesc(queryTree, planTree,
1084 canSetResult ? SPI : None,
1086 res = _SPI_pquery(qdesc, true, canSetResult ? tcount : 0);
1089 CommandCounterIncrement();
1093 qdesc = CreateQueryDesc(queryTree, planTree,
1094 canSetResult ? SPI : None,
1096 res = _SPI_pquery(qdesc, false, 0);
1105 plan->qtlist = query_list_list;
1106 plan->ptlist = plan_list;
1113 _SPI_execute_plan(_SPI_plan *plan, Datum *Values, char *Nulls, int tcount)
1115 List *query_list_list = plan->qtlist;
1116 List *plan_list = plan->ptlist;
1117 List *query_list_list_item;
1118 int nargs = plan->nargs;
1121 /* Increment CommandCounter to see changes made by now */
1122 CommandCounterIncrement();
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;
1134 bool foundOriginalQuery = false;
1136 /* Reset state for each original parsetree */
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);
1149 planTree = lfirst(plan_list);
1150 plan_list = lnext(plan_list);
1153 * This query can set the SPI result if it is the original
1154 * query, or if it is an INSTEAD query of the same kind as the
1155 * original and we haven't yet seen the original query.
1157 if (queryTree->querySource == QSRC_ORIGINAL)
1159 canSetResult = true;
1160 foundOriginalQuery = true;
1162 else if (!foundOriginalQuery &&
1163 queryTree->commandType == plan->origCmdType &&
1164 (queryTree->querySource == QSRC_INSTEAD_RULE ||
1165 queryTree->querySource == QSRC_QUAL_INSTEAD_RULE))
1166 canSetResult = true;
1168 canSetResult = false;
1170 if (queryTree->commandType == CMD_UTILITY)
1172 res = SPI_OK_UTILITY;
1173 ProcessUtility(queryTree->utilityStmt, None, NULL);
1174 CommandCounterIncrement();
1178 ParamListInfo paramLI;
1184 paramLI = (ParamListInfo)
1185 palloc0((nargs + 1) * sizeof(ParamListInfoData));
1187 for (k = 0; k < plan->nargs; k++)
1189 paramLI[k].kind = PARAM_NUM;
1190 paramLI[k].id = k + 1;
1191 paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
1192 paramLI[k].value = Values[k];
1194 paramLI[k].kind = PARAM_INVALID;
1199 qdesc = CreateQueryDesc(queryTree, planTree,
1200 canSetResult ? SPI : None,
1201 NULL, paramLI, false);
1202 res = _SPI_pquery(qdesc, true, canSetResult ? tcount : 0);
1205 CommandCounterIncrement();
1214 _SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount)
1216 Query *parseTree = queryDesc->parsetree;
1217 int operation = queryDesc->operation;
1218 CommandDest dest = queryDesc->dest;
1219 bool isRetrieveIntoPortal = false;
1220 bool isRetrieveIntoRelation = false;
1221 char *intoName = NULL;
1228 res = SPI_OK_SELECT;
1229 if (parseTree->isPortal)
1231 isRetrieveIntoPortal = true;
1232 intoName = parseTree->into->relname;
1233 parseTree->isBinary = false; /* */
1235 return SPI_ERROR_CURSOR;
1238 else if (parseTree->into != NULL) /* select into table */
1240 res = SPI_OK_SELINTO;
1241 isRetrieveIntoRelation = true;
1242 queryDesc->dest = None; /* */
1246 res = SPI_OK_INSERT;
1249 res = SPI_OK_DELETE;
1252 res = SPI_OK_UPDATE;
1255 return SPI_ERROR_OPUNKNOWN;
1258 if (!runit) /* plan preparation, don't execute */
1261 #ifdef SPI_EXECUTOR_STATS
1262 if (ShowExecutorStats)
1266 ExecutorStart(queryDesc);
1269 * Don't work currently --- need to rearrange callers so that we
1270 * prepare the portal before doing ExecutorStart() etc. See
1271 * pquery.c for the correct order of operations.
1273 if (isRetrieveIntoPortal)
1274 elog(FATAL, "SPI_select: retrieve into portal not implemented");
1276 ExecutorRun(queryDesc, ForwardScanDirection, (long) tcount);
1278 _SPI_current->processed = queryDesc->estate->es_processed;
1279 save_lastoid = queryDesc->estate->es_lastoid;
1281 if (operation == CMD_SELECT && queryDesc->dest == SPI)
1283 if (_SPI_checktuples())
1284 elog(FATAL, "SPI_select: # of processed tuples check failed");
1289 SPI_processed = _SPI_current->processed;
1290 SPI_lastoid = save_lastoid;
1291 SPI_tuptable = _SPI_current->tuptable;
1294 ExecutorEnd(queryDesc);
1296 FreeQueryDesc(queryDesc);
1298 #ifdef SPI_EXECUTOR_STATS
1299 if (ShowExecutorStats)
1300 ShowUsage("SPI EXECUTOR STATS");
1307 * _SPI_cursor_operation()
1309 * Do a FETCH or MOVE in a cursor
1312 _SPI_cursor_operation(Portal portal, bool forward, int count,
1315 QueryDesc *querydesc;
1317 MemoryContext oldcontext;
1318 ScanDirection direction;
1319 CommandDest olddest;
1321 /* Check that the portal is valid */
1322 if (!PortalIsValid(portal))
1323 elog(ERROR, "invalid portal in SPI cursor operation");
1325 /* Push the SPI stack */
1326 _SPI_begin_call(true);
1328 /* Reset the SPI result */
1330 SPI_tuptable = NULL;
1331 _SPI_current->processed = 0;
1332 _SPI_current->tuptable = NULL;
1334 /* Switch to the portals memory context */
1335 oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
1337 querydesc = PortalGetQueryDesc(portal);
1338 estate = querydesc->estate;
1340 /* Save the queries command destination and set it to SPI (for fetch) */
1341 /* or None (for move) */
1342 olddest = querydesc->dest;
1343 querydesc->dest = dest;
1345 /* Run the executor like PerformPortalFetch and remember states */
1349 direction = NoMovementScanDirection;
1351 direction = ForwardScanDirection;
1353 ExecutorRun(querydesc, direction, (long) count);
1355 if (estate->es_processed > 0)
1356 portal->atStart = false; /* OK to back up now */
1357 if (count <= 0 || (int) estate->es_processed < count)
1358 portal->atEnd = true; /* we retrieved 'em all */
1362 if (portal->atStart)
1363 direction = NoMovementScanDirection;
1365 direction = BackwardScanDirection;
1367 ExecutorRun(querydesc, direction, (long) count);
1369 if (estate->es_processed > 0)
1370 portal->atEnd = false; /* OK to go forward now */
1371 if (count <= 0 || (int) estate->es_processed < count)
1372 portal->atStart = true; /* we retrieved 'em all */
1375 _SPI_current->processed = estate->es_processed;
1377 /* Restore the old command destination and switch back to callers */
1378 /* memory context */
1379 querydesc->dest = olddest;
1380 MemoryContextSwitchTo(oldcontext);
1382 if (dest == SPI && _SPI_checktuples())
1383 elog(FATAL, "SPI_fetch: # of processed tuples check failed");
1385 /* Put the result into place for access by caller */
1386 SPI_processed = _SPI_current->processed;
1387 SPI_tuptable = _SPI_current->tuptable;
1389 /* Pop the SPI stack */
1390 _SPI_end_call(true);
1394 static MemoryContext
1397 return MemoryContextSwitchTo(_SPI_current->execCxt);
1400 static MemoryContext
1403 return MemoryContextSwitchTo(_SPI_current->procCxt);
1411 _SPI_begin_call(bool execmem)
1413 if (_SPI_curid + 1 != _SPI_connected)
1414 return SPI_ERROR_UNCONNECTED;
1416 if (_SPI_current != &(_SPI_stack[_SPI_curid]))
1417 elog(FATAL, "SPI: stack corrupted");
1419 if (execmem) /* switch to the Executor memory context */
1426 _SPI_end_call(bool procmem)
1429 * We're returning to procedure where _SPI_curid == _SPI_connected - 1
1433 if (procmem) /* switch to the procedure memory context */
1436 /* and free Executor memory */
1437 MemoryContextResetAndDeleteChildren(_SPI_current->execCxt);
1444 _SPI_checktuples(void)
1446 uint32 processed = _SPI_current->processed;
1447 SPITupleTable *tuptable = _SPI_current->tuptable;
1448 bool failed = false;
1452 if (tuptable != NULL)
1457 /* some tuples were processed */
1458 if (tuptable == NULL) /* spi_printtup was not called */
1460 else if (processed != (tuptable->alloced - tuptable->free))
1468 _SPI_copy_plan(_SPI_plan *plan, int location)
1471 MemoryContext oldcxt;
1472 MemoryContext plancxt;
1473 MemoryContext parentcxt;
1475 /* Determine correct parent for the plan's memory context */
1476 if (location == _SPI_CPLAN_PROCXT)
1477 parentcxt = _SPI_current->procCxt;
1478 else if (location == _SPI_CPLAN_TOPCXT)
1479 parentcxt = TopMemoryContext;
1480 else /* (this case not currently used) */
1481 parentcxt = CurrentMemoryContext;
1484 * Create a memory context for the plan. We don't expect the plan to
1485 * be very large, so use smaller-than-default alloc parameters.
1487 plancxt = AllocSetContextCreate(parentcxt,
1489 ALLOCSET_SMALL_MINSIZE,
1490 ALLOCSET_SMALL_INITSIZE,
1491 ALLOCSET_SMALL_MAXSIZE);
1492 oldcxt = MemoryContextSwitchTo(plancxt);
1494 /* Copy the SPI plan into its own context */
1495 newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan));
1496 newplan->plancxt = plancxt;
1497 newplan->qtlist = (List *) copyObject(plan->qtlist);
1498 newplan->ptlist = (List *) copyObject(plan->ptlist);
1499 newplan->nargs = plan->nargs;
1500 if (plan->nargs > 0)
1502 newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
1503 memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
1506 newplan->argtypes = NULL;
1507 newplan->origCmdType = plan->origCmdType;
1509 MemoryContextSwitchTo(oldcxt);