X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Fexecutor%2Fspi.c;h=3563ba23d3ad2f7cbb8f2fed207402b3714936b9;hb=147d4bf3e5e3da2ee0f0cc132718ab1c4912a877;hp=b533470e1d523b1a87c45720ba9e54a7d7b25b07;hpb=7069dbcc317c287f98e55d5296ff7ca77fadab49;p=postgresql diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index b533470e1d..3563ba23d3 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -3,12 +3,12 @@ * spi.c * Server Programming Interface * - * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.131 2004/10/13 01:25:10 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.150 2006/04/04 19:35:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,6 +20,7 @@ #include "executor/spi_priv.h" #include "tcop/tcopprot.h" #include "utils/lsyscache.h" +#include "utils/memutils.h" #include "utils/typcache.h" @@ -37,15 +38,15 @@ static int _SPI_curid = -1; static void _SPI_prepare_plan(const char *src, _SPI_plan *plan); static int _SPI_execute_plan(_SPI_plan *plan, - Datum *Values, const char *Nulls, - Snapshot snapshot, Snapshot crosscheck_snapshot, - bool read_only, int tcount); + Datum *Values, const char *Nulls, + Snapshot snapshot, Snapshot crosscheck_snapshot, + bool read_only, long tcount); -static int _SPI_pquery(QueryDesc *queryDesc, int tcount); +static int _SPI_pquery(QueryDesc *queryDesc, long tcount); static void _SPI_error_callback(void *arg); -static void _SPI_cursor_operation(Portal portal, bool forward, int count, +static void _SPI_cursor_operation(Portal portal, bool forward, long count, DestReceiver *dest); static _SPI_plan *_SPI_copy_plan(_SPI_plan *plan, int location); @@ -65,8 +66,8 @@ SPI_connect(void) int newdepth; /* - * When procedure called by Executor _SPI_curid expected to be equal - * to _SPI_connected + * When procedure called by Executor _SPI_curid expected to be equal to + * _SPI_connected */ if (_SPI_curid != _SPI_connected) return SPI_ERROR_CONNECT; @@ -103,8 +104,9 @@ SPI_connect(void) _SPI_current = &(_SPI_stack[_SPI_connected]); _SPI_current->processed = 0; + _SPI_current->lastoid = InvalidOid; _SPI_current->tuptable = NULL; - _SPI_current->procCxt = NULL; /* in case we fail to create 'em */ + _SPI_current->procCxt = NULL; /* in case we fail to create 'em */ _SPI_current->execCxt = NULL; _SPI_current->connectSubid = GetCurrentSubTransactionId(); @@ -112,20 +114,20 @@ SPI_connect(void) * Create memory contexts for this procedure * * XXX it would be better to use PortalContext as the parent context, but - * we may not be inside a portal (consider deferred-trigger - * execution). Perhaps CurTransactionContext would do? For now it - * doesn't matter because we clean up explicitly in AtEOSubXact_SPI(). + * we may not be inside a portal (consider deferred-trigger execution). + * Perhaps CurTransactionContext would do? For now it doesn't matter + * because we clean up explicitly in AtEOSubXact_SPI(). */ _SPI_current->procCxt = AllocSetContextCreate(TopTransactionContext, "SPI Proc", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); _SPI_current->execCxt = AllocSetContextCreate(TopTransactionContext, "SPI Exec", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); /* ... and switch to procedure's context */ _SPI_current->savedcxt = MemoryContextSwitchTo(_SPI_current->procCxt); @@ -159,9 +161,9 @@ SPI_finish(void) SPI_tuptable = NULL; /* - * After _SPI_begin_call _SPI_connected == _SPI_curid. Now we are - * closing connection to SPI and returning to upper Executor and so - * _SPI_connected must be equal to _SPI_curid. + * After _SPI_begin_call _SPI_connected == _SPI_curid. Now we are closing + * connection to SPI and returning to upper Executor and so _SPI_connected + * must be equal to _SPI_curid. */ _SPI_connected--; _SPI_curid--; @@ -180,9 +182,9 @@ void AtEOXact_SPI(bool isCommit) { /* - * Note that memory contexts belonging to SPI stack entries will be - * freed automatically, so we can ignore them here. We just need to - * restore our static variables to initial state. + * Note that memory contexts belonging to SPI stack entries will be freed + * automatically, so we can ignore them here. We just need to restore our + * static variables to initial state. */ if (isCommit && _SPI_connected != -1) ereport(WARNING, @@ -234,8 +236,8 @@ AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid) /* * Pop the stack entry and reset global variables. Unlike - * SPI_finish(), we don't risk switching to memory contexts that - * might be already gone. + * SPI_finish(), we don't risk switching to memory contexts that might + * be already gone. */ _SPI_connected--; _SPI_curid = _SPI_connected; @@ -270,9 +272,17 @@ SPI_pop(void) _SPI_curid--; } -/* Parse, plan, and execute a querystring */ +/* Restore state of SPI stack after aborting a subtransaction */ +void +SPI_restore_connection(void) +{ + Assert(_SPI_connected >= 0); + _SPI_curid = _SPI_connected - 1; +} + +/* Parse, plan, and execute a query string */ int -SPI_execute(const char *src, bool read_only, int tcount) +SPI_execute(const char *src, bool read_only, long tcount) { _SPI_plan plan; int res; @@ -301,7 +311,7 @@ SPI_execute(const char *src, bool read_only, int tcount) /* Obsolete version of SPI_execute */ int -SPI_exec(const char *src, int tcount) +SPI_exec(const char *src, long tcount) { return SPI_execute(src, false, tcount); } @@ -309,7 +319,7 @@ SPI_exec(const char *src, int tcount) /* Execute a previously prepared plan */ int SPI_execute_plan(void *plan, Datum *Values, const char *Nulls, - bool read_only, int tcount) + bool read_only, long tcount) { int res; @@ -334,7 +344,7 @@ SPI_execute_plan(void *plan, Datum *Values, const char *Nulls, /* Obsolete version of SPI_execute_plan */ int -SPI_execp(void *plan, Datum *Values, const char *Nulls, int tcount) +SPI_execp(void *plan, Datum *Values, const char *Nulls, long tcount) { return SPI_execute_plan(plan, Values, Nulls, false, tcount); } @@ -352,7 +362,7 @@ int SPI_execute_snapshot(void *plan, Datum *Values, const char *Nulls, Snapshot snapshot, Snapshot crosscheck_snapshot, - bool read_only, int tcount) + bool read_only, long tcount) { int res; @@ -550,8 +560,8 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum, mtuple = heap_formtuple(rel->rd_att, v, n); /* - * copy the identification info of the old tuple: t_ctid, t_self, - * and OID (if any) + * copy the identification info of the old tuple: t_ctid, t_self, and + * OID (if any) */ mtuple->t_data->t_ctid = tuple->t_data->t_ctid; mtuple->t_self = tuple->t_self; @@ -619,14 +629,12 @@ SPI_fname(TupleDesc tupdesc, int fnumber) char * SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber) { + char *result; Datum origval, - val, - result; + val; bool isnull; Oid typoid, - foutoid, - typioparam; - int32 typmod; + foutoid; bool typisvarlena; SPI_result = 0; @@ -643,37 +651,28 @@ SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber) return NULL; if (fnumber > 0) - { typoid = tupdesc->attrs[fnumber - 1]->atttypid; - typmod = tupdesc->attrs[fnumber - 1]->atttypmod; - } else - { typoid = (SystemAttributeDefinition(fnumber, true))->atttypid; - typmod = -1; - } - getTypeOutputInfo(typoid, &foutoid, &typioparam, &typisvarlena); + getTypeOutputInfo(typoid, &foutoid, &typisvarlena); /* - * If we have a toasted datum, forcibly detoast it here to avoid - * memory leakage inside the type's output routine. + * If we have a toasted datum, forcibly detoast it here to avoid memory + * leakage inside the type's output routine. */ if (typisvarlena) val = PointerGetDatum(PG_DETOAST_DATUM(origval)); else val = origval; - result = OidFunctionCall3(foutoid, - val, - ObjectIdGetDatum(typioparam), - Int32GetDatum(typmod)); + result = OidOutputFunctionCall(foutoid, val); /* Clean up detoasted copy, if any */ if (val != origval) pfree(DatumGetPointer(val)); - return DatumGetCString(result); + return result; } Datum @@ -752,6 +751,12 @@ SPI_getrelname(Relation rel) return pstrdup(RelationGetRelationName(rel)); } +char * +SPI_getnspname(Relation rel) +{ + return get_namespace_name(RelationGetNamespace(rel)); +} + void * SPI_palloc(Size size) { @@ -824,7 +829,7 @@ SPI_cursor_open(const char *name, void *plan, Portal portal; int k; - /* Ensure that the plan contains only one regular SELECT query */ + /* Ensure that the plan contains only one query */ if (list_length(ptlist) != 1 || list_length(qtlist) != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), @@ -832,16 +837,29 @@ SPI_cursor_open(const char *name, void *plan, queryTree = (Query *) linitial((List *) linitial(qtlist)); planTree = (Plan *) linitial(ptlist); - if (queryTree->commandType != CMD_SELECT) - ereport(ERROR, - (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), - errmsg("cannot open non-SELECT query as cursor"))); - if (queryTree->into != NULL) - ereport(ERROR, - (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), - errmsg("cannot open SELECT INTO query as cursor"))); + /* Must be a query that returns tuples */ + switch (queryTree->commandType) + { + case CMD_SELECT: + if (queryTree->into != NULL) + ereport(ERROR, + (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), + errmsg("cannot open SELECT INTO query as cursor"))); + break; + case CMD_UTILITY: + if (!UtilityReturnsTuples(queryTree->utilityStmt)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), + errmsg("cannot open non-SELECT query as cursor"))); + break; + default: + ereport(ERROR, + (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), + errmsg("cannot open non-SELECT query as cursor"))); + break; + } - /* Reset SPI result */ + /* Reset SPI result (note we deliberately don't touch lastoid) */ SPI_processed = 0; SPI_tuptable = NULL; _SPI_current->processed = 0; @@ -902,8 +920,8 @@ SPI_cursor_open(const char *name, void *plan, * Set up the portal. */ PortalDefineQuery(portal, - NULL, /* unfortunately don't have sourceText */ - "SELECT", /* cursor's query is always a SELECT */ + spiplan->query, + "SELECT", /* don't have the raw parse tree... */ list_make1(queryTree), list_make1(planTree), PortalGetHeapMemory(portal)); @@ -914,14 +932,14 @@ SPI_cursor_open(const char *name, void *plan, * Set up options for portal. */ portal->cursorOptions &= ~(CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL); - if (ExecSupportsBackwardScan(plan)) + if (planTree == NULL || ExecSupportsBackwardScan(planTree)) portal->cursorOptions |= CURSOR_OPT_SCROLL; else portal->cursorOptions |= CURSOR_OPT_NO_SCROLL; /* - * Set up the snapshot to use. (PortalStart will do CopySnapshot, - * so we skip that here.) + * Set up the snapshot to use. (PortalStart will do CopySnapshot, so we + * skip that here.) */ if (read_only) snapshot = ActiveSnapshot; @@ -936,7 +954,8 @@ SPI_cursor_open(const char *name, void *plan, */ PortalStart(portal, paramLI, snapshot); - Assert(portal->strategy == PORTAL_ONE_SELECT); + Assert(portal->strategy == PORTAL_ONE_SELECT || + portal->strategy == PORTAL_UTIL_SELECT); /* Return the created portal */ return portal; @@ -961,11 +980,11 @@ SPI_cursor_find(const char *name) * Fetch rows in a cursor */ void -SPI_cursor_fetch(Portal portal, bool forward, int count) +SPI_cursor_fetch(Portal portal, bool forward, long count) { _SPI_cursor_operation(portal, forward, count, - CreateDestReceiver(SPI, NULL)); - /* we know that the SPI receiver doesn't need a destroy call */ + CreateDestReceiver(DestSPI, NULL)); + /* we know that the DestSPI receiver doesn't need a destroy call */ } @@ -975,7 +994,7 @@ SPI_cursor_fetch(Portal portal, bool forward, int count) * Move in a cursor */ void -SPI_cursor_move(Portal portal, bool forward, int count) +SPI_cursor_move(Portal portal, bool forward, long count) { _SPI_cursor_operation(portal, forward, count, None_Receiver); } @@ -1169,7 +1188,7 @@ spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo) * of current SPI procedure */ void -spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self) +spi_printtup(TupleTableSlot *slot, DestReceiver *self) { SPITupleTable *tuptable; MemoryContext oldcxt; @@ -1194,10 +1213,11 @@ spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self) tuptable->free = 256; tuptable->alloced += tuptable->free; tuptable->vals = (HeapTuple *) repalloc(tuptable->vals, - tuptable->alloced * sizeof(HeapTuple)); + tuptable->alloced * sizeof(HeapTuple)); } - tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple); + tuptable->vals[tuptable->alloced - tuptable->free] = + ExecCopySlotTuple(slot); (tuptable->free)--; MemoryContextSwitchTo(oldcxt); @@ -1226,9 +1246,9 @@ _SPI_prepare_plan(const char *src, _SPI_plan *plan) int nargs = plan->nargs; /* - * Increment CommandCounter to see changes made by now. We must do - * this to be sure of seeing any schema changes made by a just-preceding - * SPI command. (But we don't bother advancing the snapshot, since the + * Increment CommandCounter to see changes made by now. We must do this + * to be sure of seeing any schema changes made by a just-preceding SPI + * command. (But we don't bother advancing the snapshot, since the * planner generally operates under SnapshotNow rules anyway.) */ CommandCounterIncrement(); @@ -1249,9 +1269,9 @@ _SPI_prepare_plan(const char *src, _SPI_plan *plan) /* * Do parse analysis and rule rewrite for each raw parsetree. * - * We save the querytrees from each raw parsetree as a separate - * sublist. This allows _SPI_execute_plan() to know where the - * boundaries between original queries fall. + * We save the querytrees from each raw parsetree as a separate sublist. + * This allows _SPI_execute_plan() to know where the boundaries between + * original queries fall. */ query_list_list = NIL; plan_list = NIL; @@ -1261,7 +1281,7 @@ _SPI_prepare_plan(const char *src, _SPI_plan *plan) Node *parsetree = (Node *) lfirst(list_item); List *query_list; - query_list = pg_analyze_and_rewrite(parsetree, argtypes, nargs); + query_list = pg_analyze_and_rewrite(parsetree, src, argtypes, nargs); query_list_list = lappend(query_list_list, query_list); @@ -1290,9 +1310,12 @@ _SPI_prepare_plan(const char *src, _SPI_plan *plan) static int _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls, Snapshot snapshot, Snapshot crosscheck_snapshot, - bool read_only, int tcount) + bool read_only, long tcount) { volatile int res = 0; + volatile uint32 my_processed = 0; + volatile Oid my_lastoid = InvalidOid; + SPITupleTable *volatile my_tuptable = NULL; Snapshot saveActiveSnapshot; /* Be sure to restore ActiveSnapshot on error exit */ @@ -1327,12 +1350,6 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls, else paramLI = NULL; - /* Reset state (only needed in case string is empty) */ - SPI_processed = 0; - SPI_lastoid = InvalidOid; - SPI_tuptable = NULL; - _SPI_current->tuptable = NULL; - /* * Setup error traceback support for ereport() */ @@ -1346,13 +1363,6 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls, List *query_list = lfirst(query_list_list_item); ListCell *query_list_item; - /* Reset state for each original parsetree */ - /* (at most one of its querytrees will be marked canSetTag) */ - SPI_processed = 0; - SPI_lastoid = InvalidOid; - SPI_tuptable = NULL; - _SPI_current->tuptable = NULL; - foreach(query_list_item, query_list) { Query *queryTree = (Query *) lfirst(query_list_item); @@ -1363,6 +1373,10 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls, planTree = lfirst(plan_list_item); plan_list_item = lnext(plan_list_item); + _SPI_current->processed = 0; + _SPI_current->lastoid = InvalidOid; + _SPI_current->tuptable = NULL; + if (queryTree->commandType == CMD_UTILITY) { if (IsA(queryTree->utilityStmt, CopyStmt)) @@ -1392,9 +1406,10 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls, if (read_only && !QueryIsReadOnly(queryTree)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - /* translator: %s is a SQL statement name */ - errmsg("%s is not allowed in a non-volatile function", - CreateQueryTag(queryTree)))); + /* translator: %s is a SQL statement name */ + errmsg("%s is not allowed in a non-volatile function", + CreateQueryTag(queryTree)))); + /* * If not read-only mode, advance the command counter before * each command. @@ -1402,7 +1417,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls, if (!read_only) CommandCounterIncrement(); - dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, + dest = CreateDestReceiver(queryTree->canSetTag ? DestSPI : DestNone, NULL); if (snapshot == InvalidSnapshot) @@ -1447,6 +1462,24 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls, } FreeSnapshot(ActiveSnapshot); ActiveSnapshot = NULL; + + /* + * The last canSetTag query sets the auxiliary values returned + * to the caller. Be careful to free any tuptables not + * returned, to avoid intratransaction memory leak. + */ + if (queryTree->canSetTag) + { + my_processed = _SPI_current->processed; + my_lastoid = _SPI_current->lastoid; + SPI_freetuptable(my_tuptable); + my_tuptable = _SPI_current->tuptable; + } + else + { + SPI_freetuptable(_SPI_current->tuptable); + _SPI_current->tuptable = NULL; + } /* we know that the receiver doesn't need a destroy call */ if (res < 0) goto fail; @@ -1470,25 +1503,34 @@ fail: ActiveSnapshot = saveActiveSnapshot; + /* Save results for caller */ + SPI_processed = my_processed; + SPI_lastoid = my_lastoid; + SPI_tuptable = my_tuptable; + return res; } static int -_SPI_pquery(QueryDesc *queryDesc, int tcount) +_SPI_pquery(QueryDesc *queryDesc, long tcount) { int operation = queryDesc->operation; int res; - Oid save_lastoid; switch (operation) { case CMD_SELECT: res = SPI_OK_SELECT; - if (queryDesc->parsetree->into != NULL) /* select into table */ + if (queryDesc->parsetree->into) /* select into table? */ { res = SPI_OK_SELINTO; queryDesc->dest = None_Receiver; /* don't output results */ } + else if (queryDesc->dest->mydest != DestSPI) + { + /* Don't return SPI_OK_SELECT if we're discarding result */ + res = SPI_OK_UTILITY; + } break; case CMD_INSERT: res = SPI_OK_INSERT; @@ -1510,35 +1552,23 @@ _SPI_pquery(QueryDesc *queryDesc, int tcount) AfterTriggerBeginQuery(); - ExecutorStart(queryDesc, false); + ExecutorStart(queryDesc, 0); - ExecutorRun(queryDesc, ForwardScanDirection, (long) tcount); + ExecutorRun(queryDesc, ForwardScanDirection, tcount); _SPI_current->processed = queryDesc->estate->es_processed; - save_lastoid = queryDesc->estate->es_lastoid; + _SPI_current->lastoid = queryDesc->estate->es_lastoid; - if (operation == CMD_SELECT && queryDesc->dest->mydest == SPI) + if (operation == CMD_SELECT && queryDesc->dest->mydest == DestSPI) { if (_SPI_checktuples()) elog(ERROR, "consistency check on SPI tuple count failed"); } - ExecutorEnd(queryDesc); - /* Take care of any queued AFTER triggers */ - AfterTriggerEndQuery(); + AfterTriggerEndQuery(queryDesc->estate); - if (queryDesc->dest->mydest == SPI) - { - SPI_processed = _SPI_current->processed; - SPI_lastoid = save_lastoid; - SPI_tuptable = _SPI_current->tuptable; - } - else if (res == SPI_OK_SELECT) - { - /* Don't return SPI_OK_SELECT if we discarded the result */ - res = SPI_OK_UTILITY; - } + ExecutorEnd(queryDesc); #ifdef SPI_EXECUTOR_STATS if (ShowExecutorStats) @@ -1560,8 +1590,8 @@ _SPI_error_callback(void *arg) int syntaxerrposition; /* - * If there is a syntax error position, convert to internal syntax - * error; otherwise treat the query as an item of context stack + * If there is a syntax error position, convert to internal syntax error; + * otherwise treat the query as an item of context stack */ syntaxerrposition = geterrposition(); if (syntaxerrposition > 0) @@ -1580,7 +1610,7 @@ _SPI_error_callback(void *arg) * Do a FETCH or MOVE in a cursor */ static void -_SPI_cursor_operation(Portal portal, bool forward, int count, +_SPI_cursor_operation(Portal portal, bool forward, long count, DestReceiver *dest) { long nfetched; @@ -1593,7 +1623,7 @@ _SPI_cursor_operation(Portal portal, bool forward, int count, if (_SPI_begin_call(true) < 0) elog(ERROR, "SPI cursor operation called while not connected"); - /* Reset the SPI result */ + /* Reset the SPI result (note we deliberately don't touch lastoid) */ SPI_processed = 0; SPI_tuptable = NULL; _SPI_current->processed = 0; @@ -1602,21 +1632,20 @@ _SPI_cursor_operation(Portal portal, bool forward, int count, /* Run the cursor */ nfetched = PortalRunFetch(portal, forward ? FETCH_FORWARD : FETCH_BACKWARD, - (long) count, + count, dest); /* - * Think not to combine this store with the preceding function call. - * If the portal contains calls to functions that use SPI, then - * SPI_stack is likely to move around while the portal runs. When - * control returns, _SPI_current will point to the correct stack - * entry... but the pointer may be different than it was beforehand. - * So we must be sure to re-fetch the pointer after the function call - * completes. + * Think not to combine this store with the preceding function call. If + * the portal contains calls to functions that use SPI, then SPI_stack is + * likely to move around while the portal runs. When control returns, + * _SPI_current will point to the correct stack entry... but the pointer + * may be different than it was beforehand. So we must be sure to re-fetch + * the pointer after the function call completes. */ _SPI_current->processed = nfetched; - if (dest->mydest == SPI && _SPI_checktuples()) + if (dest->mydest == DestSPI && _SPI_checktuples()) elog(ERROR, "consistency check on SPI tuple count failed"); /* Put the result into place for access by caller */ @@ -1709,12 +1738,13 @@ _SPI_copy_plan(_SPI_plan *plan, int location) parentcxt = _SPI_current->procCxt; else if (location == _SPI_CPLAN_TOPCXT) parentcxt = TopMemoryContext; - else /* (this case not currently used) */ + else + /* (this case not currently used) */ parentcxt = CurrentMemoryContext; /* - * Create a memory context for the plan. We don't expect the plan to - * be very large, so use smaller-than-default alloc parameters. + * Create a memory context for the plan. We don't expect the plan to be + * very large, so use smaller-than-default alloc parameters. */ plancxt = AllocSetContextCreate(parentcxt, "SPI Plan",