1 /*-------------------------------------------------------------------------
4 * Server Programming Interface
6 * Portions Copyright (c) 1996-2007, 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.176 2007/04/16 01:14:56 tgl Exp $
13 *-------------------------------------------------------------------------
17 #include "access/printtup.h"
18 #include "catalog/heap.h"
19 #include "commands/trigger.h"
20 #include "executor/spi_priv.h"
21 #include "utils/lsyscache.h"
22 #include "utils/memutils.h"
23 #include "utils/typcache.h"
26 uint32 SPI_processed = 0;
27 Oid SPI_lastoid = InvalidOid;
28 SPITupleTable *SPI_tuptable = NULL;
31 static _SPI_connection *_SPI_stack = NULL;
32 static _SPI_connection *_SPI_current = NULL;
33 static int _SPI_stack_depth = 0; /* allocated size of _SPI_stack */
34 static int _SPI_connected = -1;
35 static int _SPI_curid = -1;
37 static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan,
40 static int _SPI_execute_plan(SPIPlanPtr plan,
41 Datum *Values, const char *Nulls,
42 Snapshot snapshot, Snapshot crosscheck_snapshot,
43 bool read_only, long tcount);
45 static int _SPI_pquery(QueryDesc *queryDesc, long tcount);
47 static void _SPI_error_callback(void *arg);
49 static void _SPI_cursor_operation(Portal portal,
50 FetchDirection direction, long count,
53 static SPIPlanPtr _SPI_copy_plan(SPIPlanPtr plan, MemoryContext parentcxt);
54 static SPIPlanPtr _SPI_save_plan(SPIPlanPtr plan);
56 static int _SPI_begin_call(bool execmem);
57 static int _SPI_end_call(bool procmem);
58 static MemoryContext _SPI_execmem(void);
59 static MemoryContext _SPI_procmem(void);
60 static bool _SPI_checktuples(void);
63 /* =================== interface functions =================== */
71 * When procedure called by Executor _SPI_curid expected to be equal to
74 if (_SPI_curid != _SPI_connected)
75 return SPI_ERROR_CONNECT;
77 if (_SPI_stack == NULL)
79 if (_SPI_connected != -1 || _SPI_stack_depth != 0)
80 elog(ERROR, "SPI stack corrupted");
82 _SPI_stack = (_SPI_connection *)
83 MemoryContextAlloc(TopTransactionContext,
84 newdepth * sizeof(_SPI_connection));
85 _SPI_stack_depth = newdepth;
89 if (_SPI_stack_depth <= 0 || _SPI_stack_depth <= _SPI_connected)
90 elog(ERROR, "SPI stack corrupted");
91 if (_SPI_stack_depth == _SPI_connected + 1)
93 newdepth = _SPI_stack_depth * 2;
94 _SPI_stack = (_SPI_connection *)
96 newdepth * sizeof(_SPI_connection));
97 _SPI_stack_depth = newdepth;
102 * We're entering procedure where _SPI_curid == _SPI_connected - 1
105 Assert(_SPI_connected >= 0 && _SPI_connected < _SPI_stack_depth);
107 _SPI_current = &(_SPI_stack[_SPI_connected]);
108 _SPI_current->processed = 0;
109 _SPI_current->lastoid = InvalidOid;
110 _SPI_current->tuptable = NULL;
111 _SPI_current->procCxt = NULL; /* in case we fail to create 'em */
112 _SPI_current->execCxt = NULL;
113 _SPI_current->connectSubid = GetCurrentSubTransactionId();
116 * Create memory contexts for this procedure
118 * XXX it would be better to use PortalContext as the parent context, but
119 * we may not be inside a portal (consider deferred-trigger execution).
120 * Perhaps CurTransactionContext would do? For now it doesn't matter
121 * because we clean up explicitly in AtEOSubXact_SPI().
123 _SPI_current->procCxt = AllocSetContextCreate(TopTransactionContext,
125 ALLOCSET_DEFAULT_MINSIZE,
126 ALLOCSET_DEFAULT_INITSIZE,
127 ALLOCSET_DEFAULT_MAXSIZE);
128 _SPI_current->execCxt = AllocSetContextCreate(TopTransactionContext,
130 ALLOCSET_DEFAULT_MINSIZE,
131 ALLOCSET_DEFAULT_INITSIZE,
132 ALLOCSET_DEFAULT_MAXSIZE);
133 /* ... and switch to procedure's context */
134 _SPI_current->savedcxt = MemoryContextSwitchTo(_SPI_current->procCxt);
136 return SPI_OK_CONNECT;
144 res = _SPI_begin_call(false); /* live in procedure memory */
148 /* Restore memory context as it was before procedure call */
149 MemoryContextSwitchTo(_SPI_current->savedcxt);
151 /* Release memory used in procedure call */
152 MemoryContextDelete(_SPI_current->execCxt);
153 _SPI_current->execCxt = NULL;
154 MemoryContextDelete(_SPI_current->procCxt);
155 _SPI_current->procCxt = NULL;
158 * Reset result variables, especially SPI_tuptable which is probably
159 * pointing at a just-deleted tuptable
162 SPI_lastoid = InvalidOid;
166 * After _SPI_begin_call _SPI_connected == _SPI_curid. Now we are closing
167 * connection to SPI and returning to upper Executor and so _SPI_connected
168 * must be equal to _SPI_curid.
172 if (_SPI_connected == -1)
175 _SPI_current = &(_SPI_stack[_SPI_connected]);
177 return SPI_OK_FINISH;
181 * Clean up SPI state at transaction commit or abort.
184 AtEOXact_SPI(bool isCommit)
187 * Note that memory contexts belonging to SPI stack entries will be freed
188 * automatically, so we can ignore them here. We just need to restore our
189 * static variables to initial state.
191 if (isCommit && _SPI_connected != -1)
193 (errcode(ERRCODE_WARNING),
194 errmsg("transaction left non-empty SPI stack"),
195 errhint("Check for missing \"SPI_finish\" calls.")));
197 _SPI_current = _SPI_stack = NULL;
198 _SPI_stack_depth = 0;
199 _SPI_connected = _SPI_curid = -1;
201 SPI_lastoid = InvalidOid;
206 * Clean up SPI state at subtransaction commit or abort.
208 * During commit, there shouldn't be any unclosed entries remaining from
209 * the current subtransaction; we emit a warning if any are found.
212 AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid)
216 while (_SPI_connected >= 0)
218 _SPI_connection *connection = &(_SPI_stack[_SPI_connected]);
220 if (connection->connectSubid != mySubid)
221 break; /* couldn't be any underneath it either */
226 * Release procedure memory explicitly (see note in SPI_connect)
228 if (connection->execCxt)
230 MemoryContextDelete(connection->execCxt);
231 connection->execCxt = NULL;
233 if (connection->procCxt)
235 MemoryContextDelete(connection->procCxt);
236 connection->procCxt = NULL;
240 * Pop the stack entry and reset global variables. Unlike
241 * SPI_finish(), we don't risk switching to memory contexts that might
245 _SPI_curid = _SPI_connected;
246 if (_SPI_connected == -1)
249 _SPI_current = &(_SPI_stack[_SPI_connected]);
251 SPI_lastoid = InvalidOid;
255 if (found && isCommit)
257 (errcode(ERRCODE_WARNING),
258 errmsg("subtransaction left non-empty SPI stack"),
259 errhint("Check for missing \"SPI_finish\" calls.")));
262 * If we are aborting a subtransaction and there is an open SPI context
263 * surrounding the subxact, clean up to prevent memory leakage.
265 if (_SPI_current && !isCommit)
267 /* free Executor memory the same as _SPI_end_call would do */
268 MemoryContextResetAndDeleteChildren(_SPI_current->execCxt);
269 /* throw away any partially created tuple-table */
270 SPI_freetuptable(_SPI_current->tuptable);
271 _SPI_current->tuptable = NULL;
276 /* Pushes SPI stack to allow recursive SPI calls */
283 /* Pops SPI stack to allow recursive SPI calls */
290 /* Restore state of SPI stack after aborting a subtransaction */
292 SPI_restore_connection(void)
294 Assert(_SPI_connected >= 0);
295 _SPI_curid = _SPI_connected - 1;
298 /* Parse, plan, and execute a query string */
300 SPI_execute(const char *src, bool read_only, long tcount)
305 if (src == NULL || tcount < 0)
306 return SPI_ERROR_ARGUMENT;
308 res = _SPI_begin_call(true);
312 memset(&plan, 0, sizeof(_SPI_plan));
313 plan.magic = _SPI_PLAN_MAGIC;
315 _SPI_prepare_plan(src, &plan, 0);
317 res = _SPI_execute_plan(&plan, NULL, NULL,
318 InvalidSnapshot, InvalidSnapshot,
325 /* Obsolete version of SPI_execute */
327 SPI_exec(const char *src, long tcount)
329 return SPI_execute(src, false, tcount);
332 /* Execute a previously prepared plan */
334 SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
335 bool read_only, long tcount)
339 if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
340 return SPI_ERROR_ARGUMENT;
342 if (plan->nargs > 0 && Values == NULL)
343 return SPI_ERROR_PARAM;
345 res = _SPI_begin_call(true);
349 res = _SPI_execute_plan(plan,
351 InvalidSnapshot, InvalidSnapshot,
358 /* Obsolete version of SPI_execute_plan */
360 SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls, long tcount)
362 return SPI_execute_plan(plan, Values, Nulls, false, tcount);
366 * SPI_execute_snapshot -- identical to SPI_execute_plan, except that we allow
367 * the caller to specify exactly which snapshots to use. This is currently
368 * not documented in spi.sgml because it is only intended for use by RI
371 * Passing snapshot == InvalidSnapshot will select the normal behavior of
372 * fetching a new snapshot for each query.
375 SPI_execute_snapshot(SPIPlanPtr plan,
376 Datum *Values, const char *Nulls,
377 Snapshot snapshot, Snapshot crosscheck_snapshot,
378 bool read_only, long tcount)
382 if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
383 return SPI_ERROR_ARGUMENT;
385 if (plan->nargs > 0 && Values == NULL)
386 return SPI_ERROR_PARAM;
388 res = _SPI_begin_call(true);
392 res = _SPI_execute_plan(plan,
394 snapshot, crosscheck_snapshot,
402 SPI_prepare(const char *src, int nargs, Oid *argtypes)
404 return SPI_prepare_cursor(src, nargs, argtypes, 0);
408 SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
414 if (src == NULL || nargs < 0 || (nargs > 0 && argtypes == NULL))
416 SPI_result = SPI_ERROR_ARGUMENT;
420 SPI_result = _SPI_begin_call(true);
424 memset(&plan, 0, sizeof(_SPI_plan));
425 plan.magic = _SPI_PLAN_MAGIC;
427 plan.argtypes = argtypes;
429 _SPI_prepare_plan(src, &plan, cursorOptions);
431 /* copy plan to procedure context */
432 result = _SPI_copy_plan(&plan, _SPI_current->procCxt);
440 SPI_saveplan(SPIPlanPtr plan)
444 /* We don't currently support copying an already-saved plan */
445 if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || plan->saved)
447 SPI_result = SPI_ERROR_ARGUMENT;
451 SPI_result = _SPI_begin_call(false); /* don't change context */
455 newplan = _SPI_save_plan(plan);
464 SPI_freeplan(SPIPlanPtr plan)
466 if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
467 return SPI_ERROR_ARGUMENT;
469 /* If plancache.c owns the plancache entries, we must release them */
474 foreach(lc, plan->plancache_list)
476 CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
478 DropCachedPlan(plansource);
482 /* Now get rid of the _SPI_plan and subsidiary data in its plancxt */
483 MemoryContextDelete(plan->plancxt);
489 SPI_copytuple(HeapTuple tuple)
491 MemoryContext oldcxt = NULL;
496 SPI_result = SPI_ERROR_ARGUMENT;
500 if (_SPI_curid + 1 == _SPI_connected) /* connected */
502 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
503 elog(ERROR, "SPI stack corrupted");
504 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
507 ctuple = heap_copytuple(tuple);
510 MemoryContextSwitchTo(oldcxt);
516 SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
518 MemoryContext oldcxt = NULL;
519 HeapTupleHeader dtup;
521 if (tuple == NULL || tupdesc == NULL)
523 SPI_result = SPI_ERROR_ARGUMENT;
527 /* For RECORD results, make sure a typmod has been assigned */
528 if (tupdesc->tdtypeid == RECORDOID &&
529 tupdesc->tdtypmod < 0)
530 assign_record_type_typmod(tupdesc);
532 if (_SPI_curid + 1 == _SPI_connected) /* connected */
534 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
535 elog(ERROR, "SPI stack corrupted");
536 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
539 dtup = (HeapTupleHeader) palloc(tuple->t_len);
540 memcpy((char *) dtup, (char *) tuple->t_data, tuple->t_len);
542 HeapTupleHeaderSetDatumLength(dtup, tuple->t_len);
543 HeapTupleHeaderSetTypeId(dtup, tupdesc->tdtypeid);
544 HeapTupleHeaderSetTypMod(dtup, tupdesc->tdtypmod);
547 MemoryContextSwitchTo(oldcxt);
553 SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
554 Datum *Values, const char *Nulls)
556 MemoryContext oldcxt = NULL;
558 int numberOfAttributes;
563 if (rel == NULL || tuple == NULL || natts < 0 || attnum == NULL || Values == NULL)
565 SPI_result = SPI_ERROR_ARGUMENT;
569 if (_SPI_curid + 1 == _SPI_connected) /* connected */
571 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
572 elog(ERROR, "SPI stack corrupted");
573 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
576 numberOfAttributes = rel->rd_att->natts;
577 v = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
578 n = (char *) palloc(numberOfAttributes * sizeof(char));
580 /* fetch old values and nulls */
581 heap_deformtuple(tuple, rel->rd_att, v, n);
583 /* replace values and nulls */
584 for (i = 0; i < natts; i++)
586 if (attnum[i] <= 0 || attnum[i] > numberOfAttributes)
588 v[attnum[i] - 1] = Values[i];
589 n[attnum[i] - 1] = (Nulls && Nulls[i] == 'n') ? 'n' : ' ';
592 if (i == natts) /* no errors in *attnum */
594 mtuple = heap_formtuple(rel->rd_att, v, n);
597 * copy the identification info of the old tuple: t_ctid, t_self, and
600 mtuple->t_data->t_ctid = tuple->t_data->t_ctid;
601 mtuple->t_self = tuple->t_self;
602 mtuple->t_tableOid = tuple->t_tableOid;
603 if (rel->rd_att->tdhasoid)
604 HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple));
609 SPI_result = SPI_ERROR_NOATTRIBUTE;
616 MemoryContextSwitchTo(oldcxt);
622 SPI_fnumber(TupleDesc tupdesc, const char *fname)
625 Form_pg_attribute sysatt;
627 for (res = 0; res < tupdesc->natts; res++)
629 if (namestrcmp(&tupdesc->attrs[res]->attname, fname) == 0)
633 sysatt = SystemAttributeByName(fname, true /* "oid" will be accepted */ );
635 return sysatt->attnum;
637 /* SPI_ERROR_NOATTRIBUTE is different from all sys column numbers */
638 return SPI_ERROR_NOATTRIBUTE;
642 SPI_fname(TupleDesc tupdesc, int fnumber)
644 Form_pg_attribute att;
648 if (fnumber > tupdesc->natts || fnumber == 0 ||
649 fnumber <= FirstLowInvalidHeapAttributeNumber)
651 SPI_result = SPI_ERROR_NOATTRIBUTE;
656 att = tupdesc->attrs[fnumber - 1];
658 att = SystemAttributeDefinition(fnumber, true);
660 return pstrdup(NameStr(att->attname));
664 SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
676 if (fnumber > HeapTupleHeaderGetNatts(tuple->t_data) || fnumber == 0 ||
677 fnumber <= FirstLowInvalidHeapAttributeNumber)
679 SPI_result = SPI_ERROR_NOATTRIBUTE;
683 origval = heap_getattr(tuple, fnumber, tupdesc, &isnull);
688 typoid = tupdesc->attrs[fnumber - 1]->atttypid;
690 typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
692 getTypeOutputInfo(typoid, &foutoid, &typisvarlena);
695 * If we have a toasted datum, forcibly detoast it here to avoid memory
696 * leakage inside the type's output routine.
699 val = PointerGetDatum(PG_DETOAST_DATUM(origval));
703 result = OidOutputFunctionCall(foutoid, val);
705 /* Clean up detoasted copy, if any */
707 pfree(DatumGetPointer(val));
713 SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
717 if (fnumber > HeapTupleHeaderGetNatts(tuple->t_data) || fnumber == 0 ||
718 fnumber <= FirstLowInvalidHeapAttributeNumber)
720 SPI_result = SPI_ERROR_NOATTRIBUTE;
725 return heap_getattr(tuple, fnumber, tupdesc, isnull);
729 SPI_gettype(TupleDesc tupdesc, int fnumber)
737 if (fnumber > tupdesc->natts || fnumber == 0 ||
738 fnumber <= FirstLowInvalidHeapAttributeNumber)
740 SPI_result = SPI_ERROR_NOATTRIBUTE;
745 typoid = tupdesc->attrs[fnumber - 1]->atttypid;
747 typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
749 typeTuple = SearchSysCache(TYPEOID,
750 ObjectIdGetDatum(typoid),
753 if (!HeapTupleIsValid(typeTuple))
755 SPI_result = SPI_ERROR_TYPUNKNOWN;
759 result = pstrdup(NameStr(((Form_pg_type) GETSTRUCT(typeTuple))->typname));
760 ReleaseSysCache(typeTuple);
765 SPI_gettypeid(TupleDesc tupdesc, int fnumber)
769 if (fnumber > tupdesc->natts || fnumber == 0 ||
770 fnumber <= FirstLowInvalidHeapAttributeNumber)
772 SPI_result = SPI_ERROR_NOATTRIBUTE;
777 return tupdesc->attrs[fnumber - 1]->atttypid;
779 return (SystemAttributeDefinition(fnumber, true))->atttypid;
783 SPI_getrelname(Relation rel)
785 return pstrdup(RelationGetRelationName(rel));
789 SPI_getnspname(Relation rel)
791 return get_namespace_name(RelationGetNamespace(rel));
795 SPI_palloc(Size size)
797 MemoryContext oldcxt = NULL;
800 if (_SPI_curid + 1 == _SPI_connected) /* connected */
802 if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
803 elog(ERROR, "SPI stack corrupted");
804 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
807 pointer = palloc(size);
810 MemoryContextSwitchTo(oldcxt);
816 SPI_repalloc(void *pointer, Size size)
818 /* No longer need to worry which context chunk was in... */
819 return repalloc(pointer, size);
823 SPI_pfree(void *pointer)
825 /* No longer need to worry which context chunk was in... */
830 SPI_freetuple(HeapTuple tuple)
832 /* No longer need to worry which context tuple was in... */
833 heap_freetuple(tuple);
837 SPI_freetuptable(SPITupleTable *tuptable)
839 if (tuptable != NULL)
840 MemoryContextDelete(tuptable->tuptabcxt);
847 * Open a prepared SPI plan as a portal
850 SPI_cursor_open(const char *name, SPIPlanPtr plan,
851 Datum *Values, const char *Nulls,
854 CachedPlanSource *plansource;
857 ParamListInfo paramLI;
859 MemoryContext oldcontext;
864 * Check that the plan is something the Portal code will special-case as
865 * returning one tupleset.
867 if (!SPI_is_cursor_plan(plan))
869 /* try to give a good error message */
870 if (list_length(plan->plancache_list) != 1)
872 (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
873 errmsg("cannot open multi-query plan as cursor")));
874 plansource = (CachedPlanSource *) linitial(plan->plancache_list);
876 (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
877 /* translator: %s is name of a SQL command, eg INSERT */
878 errmsg("cannot open %s query as cursor",
879 plansource->commandTag)));
882 Assert(list_length(plan->plancache_list) == 1);
883 plansource = (CachedPlanSource *) linitial(plan->plancache_list);
885 /* Reset SPI result (note we deliberately don't touch lastoid) */
888 _SPI_current->processed = 0;
889 _SPI_current->tuptable = NULL;
891 /* Create the portal */
892 if (name == NULL || name[0] == '\0')
894 /* Use a random nonconflicting name */
895 portal = CreateNewPortal();
899 /* In this path, error if portal of same name already exists */
900 portal = CreatePortal(name, false, false);
903 /* If the plan has parameters, copy them into the portal */
906 oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
907 /* sizeof(ParamListInfoData) includes the first array element */
908 paramLI = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
909 (plan->nargs - 1) *sizeof(ParamExternData));
910 paramLI->numParams = plan->nargs;
912 for (k = 0; k < plan->nargs; k++)
914 ParamExternData *prm = ¶mLI->params[k];
916 prm->ptype = plan->argtypes[k];
918 prm->isnull = (Nulls && Nulls[k] == 'n');
921 /* nulls just copy */
922 prm->value = Values[k];
926 /* pass-by-ref values must be copied into portal context */
930 get_typlenbyval(prm->ptype, ¶mTypLen, ¶mTypByVal);
931 prm->value = datumCopy(Values[k],
932 paramTypByVal, paramTypLen);
935 MemoryContextSwitchTo(oldcontext);
942 /* Replan if needed, and increment plan refcount for portal */
943 cplan = RevalidateCachedPlan(plansource, false);
944 stmt_list = cplan->stmt_list;
948 /* No replan, but copy the plan into the portal's context */
949 oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
950 stmt_list = copyObject(plansource->plan->stmt_list);
951 MemoryContextSwitchTo(oldcontext);
952 cplan = NULL; /* portal shouldn't depend on cplan */
958 PortalDefineQuery(portal,
959 NULL, /* no statement name */
960 plansource->query_string,
961 plansource->commandTag,
966 * Set up options for portal.
968 portal->cursorOptions &= ~(CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL);
969 if (list_length(stmt_list) == 1 &&
970 IsA((Node *) linitial(stmt_list), PlannedStmt) &&
971 ExecSupportsBackwardScan(((PlannedStmt *) linitial(stmt_list))->planTree))
972 portal->cursorOptions |= CURSOR_OPT_SCROLL;
974 portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
977 * If told to be read-only, we'd better check for read-only queries.
978 * This can't be done earlier because we need to look at the finished,
979 * planned queries. (In particular, we don't want to do it between
980 * RevalidateCachedPlan and PortalDefineQuery, because throwing an error
981 * between those steps would result in leaking our plancache refcount.)
987 foreach(lc, stmt_list)
989 Node *pstmt = (Node *) lfirst(lc);
991 if (!CommandIsReadOnly(pstmt))
993 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
994 /* translator: %s is a SQL statement name */
995 errmsg("%s is not allowed in a non-volatile function",
996 CreateCommandTag(pstmt))));
1001 * Set up the snapshot to use. (PortalStart will do CopySnapshot, so we
1005 snapshot = ActiveSnapshot;
1008 CommandCounterIncrement();
1009 snapshot = GetTransactionSnapshot();
1013 * Start portal execution.
1015 PortalStart(portal, paramLI, snapshot);
1017 Assert(portal->strategy != PORTAL_MULTI_QUERY);
1019 /* Return the created portal */
1027 * Find the portal of an existing open cursor
1030 SPI_cursor_find(const char *name)
1032 return GetPortalByName(name);
1037 * SPI_cursor_fetch()
1039 * Fetch rows in a cursor
1042 SPI_cursor_fetch(Portal portal, bool forward, long count)
1044 _SPI_cursor_operation(portal,
1045 forward ? FETCH_FORWARD : FETCH_BACKWARD, count,
1046 CreateDestReceiver(DestSPI, NULL));
1047 /* we know that the DestSPI receiver doesn't need a destroy call */
1057 SPI_cursor_move(Portal portal, bool forward, long count)
1059 _SPI_cursor_operation(portal,
1060 forward ? FETCH_FORWARD : FETCH_BACKWARD, count,
1066 * SPI_scroll_cursor_fetch()
1068 * Fetch rows in a scrollable cursor
1071 SPI_scroll_cursor_fetch(Portal portal, FetchDirection direction, long count)
1073 _SPI_cursor_operation(portal,
1075 CreateDestReceiver(DestSPI, NULL));
1076 /* we know that the DestSPI receiver doesn't need a destroy call */
1081 * SPI_scroll_cursor_move()
1083 * Move in a scrollable cursor
1086 SPI_scroll_cursor_move(Portal portal, FetchDirection direction, long count)
1088 _SPI_cursor_operation(portal, direction, count, None_Receiver);
1093 * SPI_cursor_close()
1098 SPI_cursor_close(Portal portal)
1100 if (!PortalIsValid(portal))
1101 elog(ERROR, "invalid portal in SPI cursor operation");
1103 PortalDrop(portal, false);
1107 * Returns the Oid representing the type id for argument at argIndex. First
1108 * parameter is at index zero.
1111 SPI_getargtypeid(SPIPlanPtr plan, int argIndex)
1113 if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC ||
1114 argIndex < 0 || argIndex >= plan->nargs)
1116 SPI_result = SPI_ERROR_ARGUMENT;
1119 return plan->argtypes[argIndex];
1123 * Returns the number of arguments for the prepared plan.
1126 SPI_getargcount(SPIPlanPtr plan)
1128 if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
1130 SPI_result = SPI_ERROR_ARGUMENT;
1137 * Returns true if the plan contains exactly one command
1138 * and that command returns tuples to the caller (eg, SELECT or
1139 * INSERT ... RETURNING, but not SELECT ... INTO). In essence,
1140 * the result indicates if the command can be used with SPI_cursor_open
1143 * plan: A plan previously prepared using SPI_prepare
1146 SPI_is_cursor_plan(SPIPlanPtr plan)
1148 CachedPlanSource *plansource;
1151 if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
1153 SPI_result = SPI_ERROR_ARGUMENT;
1157 if (list_length(plan->plancache_list) != 1)
1158 return false; /* not exactly 1 pre-rewrite command */
1159 plansource = (CachedPlanSource *) linitial(plan->plancache_list);
1163 /* Make sure the plan is up to date */
1164 cplan = RevalidateCachedPlan(plansource, true);
1165 ReleaseCachedPlan(cplan, true);
1168 /* Does it return tuples? */
1169 if (plansource->resultDesc)
1176 * SPI_result_code_string --- convert any SPI return code to a string
1178 * This is often useful in error messages. Most callers will probably
1179 * only pass negative (error-case) codes, but for generality we recognize
1180 * the success codes too.
1183 SPI_result_code_string(int code)
1185 static char buf[64];
1189 case SPI_ERROR_CONNECT:
1190 return "SPI_ERROR_CONNECT";
1191 case SPI_ERROR_COPY:
1192 return "SPI_ERROR_COPY";
1193 case SPI_ERROR_OPUNKNOWN:
1194 return "SPI_ERROR_OPUNKNOWN";
1195 case SPI_ERROR_UNCONNECTED:
1196 return "SPI_ERROR_UNCONNECTED";
1197 case SPI_ERROR_ARGUMENT:
1198 return "SPI_ERROR_ARGUMENT";
1199 case SPI_ERROR_PARAM:
1200 return "SPI_ERROR_PARAM";
1201 case SPI_ERROR_TRANSACTION:
1202 return "SPI_ERROR_TRANSACTION";
1203 case SPI_ERROR_NOATTRIBUTE:
1204 return "SPI_ERROR_NOATTRIBUTE";
1205 case SPI_ERROR_NOOUTFUNC:
1206 return "SPI_ERROR_NOOUTFUNC";
1207 case SPI_ERROR_TYPUNKNOWN:
1208 return "SPI_ERROR_TYPUNKNOWN";
1209 case SPI_OK_CONNECT:
1210 return "SPI_OK_CONNECT";
1212 return "SPI_OK_FINISH";
1214 return "SPI_OK_FETCH";
1215 case SPI_OK_UTILITY:
1216 return "SPI_OK_UTILITY";
1218 return "SPI_OK_SELECT";
1219 case SPI_OK_SELINTO:
1220 return "SPI_OK_SELINTO";
1222 return "SPI_OK_INSERT";
1224 return "SPI_OK_DELETE";
1226 return "SPI_OK_UPDATE";
1228 return "SPI_OK_CURSOR";
1229 case SPI_OK_INSERT_RETURNING:
1230 return "SPI_OK_INSERT_RETURNING";
1231 case SPI_OK_DELETE_RETURNING:
1232 return "SPI_OK_DELETE_RETURNING";
1233 case SPI_OK_UPDATE_RETURNING:
1234 return "SPI_OK_UPDATE_RETURNING";
1236 /* Unrecognized code ... return something useful ... */
1237 sprintf(buf, "Unrecognized SPI code %d", code);
1241 /* =================== private functions =================== */
1245 * Initialize to receive tuples from Executor into SPITupleTable
1246 * of current SPI procedure
1249 spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
1251 SPITupleTable *tuptable;
1252 MemoryContext oldcxt;
1253 MemoryContext tuptabcxt;
1256 * When called by Executor _SPI_curid expected to be equal to
1259 if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
1260 elog(ERROR, "improper call to spi_dest_startup");
1261 if (_SPI_current != &(_SPI_stack[_SPI_curid]))
1262 elog(ERROR, "SPI stack corrupted");
1264 if (_SPI_current->tuptable != NULL)
1265 elog(ERROR, "improper call to spi_dest_startup");
1267 oldcxt = _SPI_procmem(); /* switch to procedure memory context */
1269 tuptabcxt = AllocSetContextCreate(CurrentMemoryContext,
1271 ALLOCSET_DEFAULT_MINSIZE,
1272 ALLOCSET_DEFAULT_INITSIZE,
1273 ALLOCSET_DEFAULT_MAXSIZE);
1274 MemoryContextSwitchTo(tuptabcxt);
1276 _SPI_current->tuptable = tuptable = (SPITupleTable *)
1277 palloc(sizeof(SPITupleTable));
1278 tuptable->tuptabcxt = tuptabcxt;
1279 tuptable->alloced = tuptable->free = 128;
1280 tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
1281 tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
1283 MemoryContextSwitchTo(oldcxt);
1288 * store tuple retrieved by Executor into SPITupleTable
1289 * of current SPI procedure
1292 spi_printtup(TupleTableSlot *slot, DestReceiver *self)
1294 SPITupleTable *tuptable;
1295 MemoryContext oldcxt;
1298 * When called by Executor _SPI_curid expected to be equal to
1301 if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
1302 elog(ERROR, "improper call to spi_printtup");
1303 if (_SPI_current != &(_SPI_stack[_SPI_curid]))
1304 elog(ERROR, "SPI stack corrupted");
1306 tuptable = _SPI_current->tuptable;
1307 if (tuptable == NULL)
1308 elog(ERROR, "improper call to spi_printtup");
1310 oldcxt = MemoryContextSwitchTo(tuptable->tuptabcxt);
1312 if (tuptable->free == 0)
1314 tuptable->free = 256;
1315 tuptable->alloced += tuptable->free;
1316 tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
1317 tuptable->alloced * sizeof(HeapTuple));
1320 tuptable->vals[tuptable->alloced - tuptable->free] =
1321 ExecCopySlotTuple(slot);
1324 MemoryContextSwitchTo(oldcxt);
1332 * Parse and plan a querystring.
1334 * At entry, plan->argtypes and plan->nargs must be valid.
1336 * Results are stored into *plan (specifically, plan->plancache_list).
1337 * Note however that the result trees are all in CurrentMemoryContext
1338 * and need to be copied somewhere to survive.
1341 _SPI_prepare_plan(const char *src, SPIPlanPtr plan, int cursorOptions)
1343 List *raw_parsetree_list;
1344 List *plancache_list;
1345 ListCell *list_item;
1346 ErrorContextCallback spierrcontext;
1347 Oid *argtypes = plan->argtypes;
1348 int nargs = plan->nargs;
1351 * Increment CommandCounter to see changes made by now. We must do this
1352 * to be sure of seeing any schema changes made by a just-preceding SPI
1353 * command. (But we don't bother advancing the snapshot, since the
1354 * planner generally operates under SnapshotNow rules anyway.)
1356 CommandCounterIncrement();
1359 * Setup error traceback support for ereport()
1361 spierrcontext.callback = _SPI_error_callback;
1362 spierrcontext.arg = (void *) src;
1363 spierrcontext.previous = error_context_stack;
1364 error_context_stack = &spierrcontext;
1367 * Parse the request string into a list of raw parse trees.
1369 raw_parsetree_list = pg_parse_query(src);
1372 * Do parse analysis and rule rewrite for each raw parsetree, then
1373 * cons up a phony plancache entry for each one.
1375 plancache_list = NIL;
1377 foreach(list_item, raw_parsetree_list)
1379 Node *parsetree = (Node *) lfirst(list_item);
1381 CachedPlanSource *plansource;
1384 /* Need a copyObject here to keep parser from modifying raw tree */
1385 stmt_list = pg_analyze_and_rewrite(copyObject(parsetree),
1386 src, argtypes, nargs);
1387 stmt_list = pg_plan_queries(stmt_list, cursorOptions, NULL, false);
1389 plansource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource));
1390 cplan = (CachedPlan *) palloc0(sizeof(CachedPlan));
1392 plansource->raw_parse_tree = parsetree;
1393 /* cast-away-const here is a bit ugly, but there's no reason to copy */
1394 plansource->query_string = (char *) src;
1395 plansource->commandTag = CreateCommandTag(parsetree);
1396 plansource->param_types = argtypes;
1397 plansource->num_params = nargs;
1398 plansource->fully_planned = true;
1399 plansource->fixed_result = false;
1400 plansource->resultDesc = PlanCacheComputeResultDesc(stmt_list);
1401 plansource->plan = cplan;
1403 cplan->stmt_list = stmt_list;
1404 cplan->fully_planned = true;
1406 plancache_list = lappend(plancache_list, plansource);
1409 plan->plancache_list = plancache_list;
1412 * Pop the error context stack
1414 error_context_stack = spierrcontext.previous;
1418 * Execute the given plan with the given parameter values
1420 * snapshot: query snapshot to use, or InvalidSnapshot for the normal
1421 * behavior of taking a new snapshot for each query.
1422 * crosscheck_snapshot: for RI use, all others pass InvalidSnapshot
1423 * read_only: TRUE for read-only execution (no CommandCounterIncrement)
1424 * tcount: execution tuple-count limit, or 0 for none
1427 _SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
1428 Snapshot snapshot, Snapshot crosscheck_snapshot,
1429 bool read_only, long tcount)
1431 volatile int my_res = 0;
1432 volatile uint32 my_processed = 0;
1433 volatile Oid my_lastoid = InvalidOid;
1434 SPITupleTable *volatile my_tuptable = NULL;
1435 volatile int res = 0;
1436 Snapshot saveActiveSnapshot;
1438 /* Be sure to restore ActiveSnapshot on error exit */
1439 saveActiveSnapshot = ActiveSnapshot;
1443 ErrorContextCallback spierrcontext;
1444 int nargs = plan->nargs;
1445 ParamListInfo paramLI;
1446 CachedPlan *cplan = NULL;
1448 /* Convert parameters to form wanted by executor */
1453 /* sizeof(ParamListInfoData) includes the first array element */
1454 paramLI = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
1455 (nargs - 1) *sizeof(ParamExternData));
1456 paramLI->numParams = nargs;
1458 for (k = 0; k < nargs; k++)
1460 ParamExternData *prm = ¶mLI->params[k];
1462 prm->value = Values[k];
1463 prm->isnull = (Nulls && Nulls[k] == 'n');
1465 prm->ptype = plan->argtypes[k];
1472 * Setup error traceback support for ereport()
1474 spierrcontext.callback = _SPI_error_callback;
1475 spierrcontext.arg = NULL;
1476 spierrcontext.previous = error_context_stack;
1477 error_context_stack = &spierrcontext;
1479 foreach(lc1, plan->plancache_list)
1481 CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc1);
1485 spierrcontext.arg = (void *) plansource->query_string;
1489 /* Replan if needed, and increment plan refcount locally */
1490 cplan = RevalidateCachedPlan(plansource, true);
1491 stmt_list = cplan->stmt_list;
1495 /* No replan here */
1497 stmt_list = plansource->plan->stmt_list;
1500 foreach(lc2, stmt_list)
1502 Node *stmt = (Node *) lfirst(lc2);
1507 _SPI_current->processed = 0;
1508 _SPI_current->lastoid = InvalidOid;
1509 _SPI_current->tuptable = NULL;
1511 if (IsA(stmt, PlannedStmt))
1513 canSetTag = ((PlannedStmt *) stmt)->canSetTag;
1517 /* utilities are canSetTag if only thing in list */
1518 canSetTag = (list_length(stmt_list) == 1);
1520 if (IsA(stmt, CopyStmt))
1522 CopyStmt *cstmt = (CopyStmt *) stmt;
1524 if (cstmt->filename == NULL)
1526 my_res = SPI_ERROR_COPY;
1530 else if (IsA(stmt, TransactionStmt))
1532 my_res = SPI_ERROR_TRANSACTION;
1537 if (read_only && !CommandIsReadOnly(stmt))
1539 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1540 /* translator: %s is a SQL statement name */
1541 errmsg("%s is not allowed in a non-volatile function",
1542 CreateCommandTag(stmt))));
1545 * If not read-only mode, advance the command counter before
1549 CommandCounterIncrement();
1551 dest = CreateDestReceiver(canSetTag ? DestSPI : DestNone,
1554 if (snapshot == InvalidSnapshot)
1557 * Default read_only behavior is to use the entry-time
1558 * ActiveSnapshot; if read-write, grab a full new snap.
1561 ActiveSnapshot = CopySnapshot(saveActiveSnapshot);
1563 ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
1568 * We interpret read_only with a specified snapshot to be
1569 * exactly that snapshot, but read-write means use the
1570 * snap with advancing of command ID.
1572 ActiveSnapshot = CopySnapshot(snapshot);
1574 ActiveSnapshot->curcid = GetCurrentCommandId();
1577 if (IsA(stmt, PlannedStmt))
1579 qdesc = CreateQueryDesc((PlannedStmt *) stmt,
1581 crosscheck_snapshot,
1584 res = _SPI_pquery(qdesc, canSetTag ? tcount : 0);
1585 FreeQueryDesc(qdesc);
1589 ProcessUtility(stmt,
1590 plansource->query_string,
1592 false, /* not top level */
1595 /* Update "processed" if stmt returned tuples */
1596 if (_SPI_current->tuptable)
1597 _SPI_current->processed = _SPI_current->tuptable->alloced - _SPI_current->tuptable->free;
1598 res = SPI_OK_UTILITY;
1600 FreeSnapshot(ActiveSnapshot);
1601 ActiveSnapshot = NULL;
1604 * The last canSetTag query sets the status values returned to
1605 * the caller. Be careful to free any tuptables not returned,
1606 * to avoid intratransaction memory leak.
1610 my_processed = _SPI_current->processed;
1611 my_lastoid = _SPI_current->lastoid;
1612 SPI_freetuptable(my_tuptable);
1613 my_tuptable = _SPI_current->tuptable;
1618 SPI_freetuptable(_SPI_current->tuptable);
1619 _SPI_current->tuptable = NULL;
1621 /* we know that the receiver doesn't need a destroy call */
1629 /* Done with this plan, so release refcount */
1631 ReleaseCachedPlan(cplan, true);
1637 /* We no longer need the cached plan refcount, if any */
1639 ReleaseCachedPlan(cplan, true);
1642 * Pop the error context stack
1644 error_context_stack = spierrcontext.previous;
1648 /* Restore global vars and propagate error */
1649 ActiveSnapshot = saveActiveSnapshot;
1654 ActiveSnapshot = saveActiveSnapshot;
1656 /* Save results for caller */
1657 SPI_processed = my_processed;
1658 SPI_lastoid = my_lastoid;
1659 SPI_tuptable = my_tuptable;
1661 /* tuptable now is caller's responsibility, not SPI's */
1662 _SPI_current->tuptable = NULL;
1665 * If none of the queries had canSetTag, we return the last query's result
1666 * code, but not its auxiliary results (for backwards compatibility).
1675 _SPI_pquery(QueryDesc *queryDesc, long tcount)
1677 int operation = queryDesc->operation;
1683 if (queryDesc->plannedstmt->into) /* select into table? */
1684 res = SPI_OK_SELINTO;
1685 else if (queryDesc->dest->mydest != DestSPI)
1687 /* Don't return SPI_OK_SELECT if we're discarding result */
1688 res = SPI_OK_UTILITY;
1691 res = SPI_OK_SELECT;
1694 if (queryDesc->plannedstmt->returningLists)
1695 res = SPI_OK_INSERT_RETURNING;
1697 res = SPI_OK_INSERT;
1700 if (queryDesc->plannedstmt->returningLists)
1701 res = SPI_OK_DELETE_RETURNING;
1703 res = SPI_OK_DELETE;
1706 if (queryDesc->plannedstmt->returningLists)
1707 res = SPI_OK_UPDATE_RETURNING;
1709 res = SPI_OK_UPDATE;
1712 return SPI_ERROR_OPUNKNOWN;
1715 #ifdef SPI_EXECUTOR_STATS
1716 if (ShowExecutorStats)
1720 AfterTriggerBeginQuery();
1722 ExecutorStart(queryDesc, 0);
1724 ExecutorRun(queryDesc, ForwardScanDirection, tcount);
1726 _SPI_current->processed = queryDesc->estate->es_processed;
1727 _SPI_current->lastoid = queryDesc->estate->es_lastoid;
1729 if ((res == SPI_OK_SELECT || queryDesc->plannedstmt->returningLists) &&
1730 queryDesc->dest->mydest == DestSPI)
1732 if (_SPI_checktuples())
1733 elog(ERROR, "consistency check on SPI tuple count failed");
1736 /* Take care of any queued AFTER triggers */
1737 AfterTriggerEndQuery(queryDesc->estate);
1739 ExecutorEnd(queryDesc);
1741 #ifdef SPI_EXECUTOR_STATS
1742 if (ShowExecutorStats)
1743 ShowUsage("SPI EXECUTOR STATS");
1750 * _SPI_error_callback
1752 * Add context information when a query invoked via SPI fails
1755 _SPI_error_callback(void *arg)
1757 const char *query = (const char *) arg;
1758 int syntaxerrposition;
1761 * If there is a syntax error position, convert to internal syntax error;
1762 * otherwise treat the query as an item of context stack
1764 syntaxerrposition = geterrposition();
1765 if (syntaxerrposition > 0)
1768 internalerrposition(syntaxerrposition);
1769 internalerrquery(query);
1772 errcontext("SQL statement \"%s\"", query);
1776 * _SPI_cursor_operation()
1778 * Do a FETCH or MOVE in a cursor
1781 _SPI_cursor_operation(Portal portal, FetchDirection direction, long count,
1786 /* Check that the portal is valid */
1787 if (!PortalIsValid(portal))
1788 elog(ERROR, "invalid portal in SPI cursor operation");
1790 /* Push the SPI stack */
1791 if (_SPI_begin_call(true) < 0)
1792 elog(ERROR, "SPI cursor operation called while not connected");
1794 /* Reset the SPI result (note we deliberately don't touch lastoid) */
1796 SPI_tuptable = NULL;
1797 _SPI_current->processed = 0;
1798 _SPI_current->tuptable = NULL;
1800 /* Run the cursor */
1801 nfetched = PortalRunFetch(portal,
1807 * Think not to combine this store with the preceding function call. If
1808 * the portal contains calls to functions that use SPI, then SPI_stack is
1809 * likely to move around while the portal runs. When control returns,
1810 * _SPI_current will point to the correct stack entry... but the pointer
1811 * may be different than it was beforehand. So we must be sure to re-fetch
1812 * the pointer after the function call completes.
1814 _SPI_current->processed = nfetched;
1816 if (dest->mydest == DestSPI && _SPI_checktuples())
1817 elog(ERROR, "consistency check on SPI tuple count failed");
1819 /* Put the result into place for access by caller */
1820 SPI_processed = _SPI_current->processed;
1821 SPI_tuptable = _SPI_current->tuptable;
1823 /* tuptable now is caller's responsibility, not SPI's */
1824 _SPI_current->tuptable = NULL;
1826 /* Pop the SPI stack */
1827 _SPI_end_call(true);
1831 static MemoryContext
1834 return MemoryContextSwitchTo(_SPI_current->execCxt);
1837 static MemoryContext
1840 return MemoryContextSwitchTo(_SPI_current->procCxt);
1844 * _SPI_begin_call: begin a SPI operation within a connected procedure
1847 _SPI_begin_call(bool execmem)
1849 if (_SPI_curid + 1 != _SPI_connected)
1850 return SPI_ERROR_UNCONNECTED;
1852 if (_SPI_current != &(_SPI_stack[_SPI_curid]))
1853 elog(ERROR, "SPI stack corrupted");
1855 if (execmem) /* switch to the Executor memory context */
1862 * _SPI_end_call: end a SPI operation within a connected procedure
1864 * Note: this currently has no failure return cases, so callers don't check
1867 _SPI_end_call(bool procmem)
1870 * We're returning to procedure where _SPI_curid == _SPI_connected - 1
1874 if (procmem) /* switch to the procedure memory context */
1877 /* and free Executor memory */
1878 MemoryContextResetAndDeleteChildren(_SPI_current->execCxt);
1885 _SPI_checktuples(void)
1887 uint32 processed = _SPI_current->processed;
1888 SPITupleTable *tuptable = _SPI_current->tuptable;
1889 bool failed = false;
1891 if (tuptable == NULL) /* spi_dest_startup was not called */
1893 else if (processed != (tuptable->alloced - tuptable->free))
1900 * Make an "unsaved" copy of the given plan, in a child context of parentcxt.
1903 _SPI_copy_plan(SPIPlanPtr plan, MemoryContext parentcxt)
1906 MemoryContext plancxt;
1907 MemoryContext oldcxt;
1910 Assert(!plan->saved); /* not currently supported */
1913 * Create a memory context for the plan. We don't expect the plan to be
1914 * very large, so use smaller-than-default alloc parameters.
1916 plancxt = AllocSetContextCreate(parentcxt,
1918 ALLOCSET_SMALL_MINSIZE,
1919 ALLOCSET_SMALL_INITSIZE,
1920 ALLOCSET_SMALL_MAXSIZE);
1921 oldcxt = MemoryContextSwitchTo(plancxt);
1923 /* Copy the SPI plan into its own context */
1924 newplan = (SPIPlanPtr) palloc(sizeof(_SPI_plan));
1925 newplan->magic = _SPI_PLAN_MAGIC;
1926 newplan->saved = false;
1927 newplan->plancache_list = NIL;
1928 newplan->plancxt = plancxt;
1929 newplan->nargs = plan->nargs;
1930 if (plan->nargs > 0)
1932 newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
1933 memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
1936 newplan->argtypes = NULL;
1938 foreach(lc, plan->plancache_list)
1940 CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
1941 CachedPlanSource *newsource;
1943 CachedPlan *newcplan;
1945 /* Note: we assume we don't need to revalidate the plan */
1946 cplan = plansource->plan;
1948 newsource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource));
1949 newcplan = (CachedPlan *) palloc0(sizeof(CachedPlan));
1951 newsource->raw_parse_tree = copyObject(plansource->raw_parse_tree);
1952 newsource->query_string = pstrdup(plansource->query_string);
1953 newsource->commandTag = plansource->commandTag;
1954 newsource->param_types = newplan->argtypes;
1955 newsource->num_params = newplan->nargs;
1956 newsource->fully_planned = plansource->fully_planned;
1957 newsource->fixed_result = plansource->fixed_result;
1958 if (plansource->resultDesc)
1959 newsource->resultDesc = CreateTupleDescCopy(plansource->resultDesc);
1960 newsource->plan = newcplan;
1962 newcplan->stmt_list = copyObject(cplan->stmt_list);
1963 newcplan->fully_planned = cplan->fully_planned;
1965 newplan->plancache_list = lappend(newplan->plancache_list, newsource);
1968 MemoryContextSwitchTo(oldcxt);
1974 * Make a "saved" copy of the given plan, entrusting everything to plancache.c
1977 _SPI_save_plan(SPIPlanPtr plan)
1980 MemoryContext plancxt;
1981 MemoryContext oldcxt;
1984 Assert(!plan->saved); /* not currently supported */
1987 * Create a memory context for the plan. We don't expect the plan to be
1988 * very large, so use smaller-than-default alloc parameters.
1990 plancxt = AllocSetContextCreate(CacheMemoryContext,
1992 ALLOCSET_SMALL_MINSIZE,
1993 ALLOCSET_SMALL_INITSIZE,
1994 ALLOCSET_SMALL_MAXSIZE);
1995 oldcxt = MemoryContextSwitchTo(plancxt);
1997 /* Copy the SPI plan into its own context */
1998 newplan = (SPIPlanPtr) palloc(sizeof(_SPI_plan));
1999 newplan->magic = _SPI_PLAN_MAGIC;
2000 newplan->saved = true;
2001 newplan->plancache_list = NIL;
2002 newplan->plancxt = plancxt;
2003 newplan->nargs = plan->nargs;
2004 if (plan->nargs > 0)
2006 newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
2007 memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
2010 newplan->argtypes = NULL;
2012 foreach(lc, plan->plancache_list)
2014 CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
2015 CachedPlanSource *newsource;
2018 /* Note: we assume we don't need to revalidate the plan */
2019 cplan = plansource->plan;
2021 newsource = CreateCachedPlan(plansource->raw_parse_tree,
2022 plansource->query_string,
2023 plansource->commandTag,
2030 newplan->plancache_list = lappend(newplan->plancache_list, newsource);
2033 MemoryContextSwitchTo(oldcxt);