Index: src/backend/access/transam/xact.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/access/transam/xact.c,v
retrieving revision 1.262
diff -c -p -r1.262 xact.c
*** src/backend/access/transam/xact.c	26 Mar 2008 18:48:59 -0000	1.262
--- src/backend/access/transam/xact.c	8 Apr 2008 00:10:02 -0000
*************** CommitTransaction(void)
*** 1752,1757 ****
--- 1752,1758 ----
  	AtEOXact_ComboCid();
  	AtEOXact_HashTables(true);
  	AtEOXact_PgStat(true);
+ 	AtEOXact_Snapshot(true);
  	pgstat_report_xact_timestamp(0);
  
  	CurrentResourceOwner = NULL;
*************** PrepareTransaction(void)
*** 1984,1989 ****
--- 1985,1991 ----
  	AtEOXact_ComboCid();
  	AtEOXact_HashTables(true);
  	/* don't call AtEOXact_PgStat here */
+ 	AtEOXact_Snapshot(true);
  
  	CurrentResourceOwner = NULL;
  	ResourceOwnerDelete(TopTransactionResourceOwner);
*************** AbortTransaction(void)
*** 2128,2133 ****
--- 2130,2136 ----
  	AtEOXact_ComboCid();
  	AtEOXact_HashTables(false);
  	AtEOXact_PgStat(false);
+ 	AtEOXact_Snapshot(false);
  	pgstat_report_xact_timestamp(0);
  
  	/*
*************** CommitSubTransaction(void)
*** 3806,3811 ****
--- 3809,3815 ----
  	AtSubCommit_Notify();
  	AtEOSubXact_UpdateFlatFiles(true, s->subTransactionId,
  								s->parent->subTransactionId);
+ 	AtSubCommit_Snapshot();
  
  	CallSubXactCallbacks(SUBXACT_EVENT_COMMIT_SUB, s->subTransactionId,
  						 s->parent->subTransactionId);
*************** AbortSubTransaction(void)
*** 3926,3931 ****
--- 3930,3936 ----
  		AtSubAbort_Notify();
  		AtEOSubXact_UpdateFlatFiles(false, s->subTransactionId,
  									s->parent->subTransactionId);
+ 		AtSubAbort_Snapshot();
  
  		/* Advertise the fact that we aborted in pg_clog. */
  		(void) RecordTransactionAbort(true);
Index: src/backend/commands/cluster.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/cluster.c,v
retrieving revision 1.172
diff -c -p -r1.172 cluster.c
*** src/backend/commands/cluster.c	26 Mar 2008 21:10:37 -0000	1.172
--- src/backend/commands/cluster.c	7 Apr 2008 23:12:27 -0000
*************** cluster(ClusterStmt *stmt, bool isTopLev
*** 211,216 ****
--- 211,217 ----
  		rvs = get_tables_to_cluster(cluster_context);
  
  		/* Commit to get out of starting transaction */
+ 		PopActiveSnapshot();
  		CommitTransactionCommand();
  
  		/* Ok, now that we've got them all, cluster them one by one */
*************** cluster(ClusterStmt *stmt, bool isTopLev
*** 221,228 ****
  			/* Start a new transaction for each relation. */
  			StartTransactionCommand();
  			/* functions in indexes may want a snapshot set */
! 			ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
  			cluster_rel(rvtc, true);
  			CommitTransactionCommand();
  		}
  
--- 222,230 ----
  			/* Start a new transaction for each relation. */
  			StartTransactionCommand();
  			/* functions in indexes may want a snapshot set */
! 			PushActiveSnapshot(GetTransactionSnapshot());
  			cluster_rel(rvtc, true);
+ 			PopActiveSnapshot();
  			CommitTransactionCommand();
  		}
  
Index: src/backend/commands/copy.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/copy.c,v
retrieving revision 1.298
diff -c -p -r1.298 copy.c
*** src/backend/commands/copy.c	26 Mar 2008 18:48:59 -0000	1.298
--- src/backend/commands/copy.c	4 Apr 2008 23:22:25 -0000
*************** DoCopy(const CopyStmt *stmt, const char 
*** 1003,1008 ****
--- 1003,1009 ----
  		Query	   *query;
  		PlannedStmt *plan;
  		DestReceiver *dest;
+ 		Snapshot	snap;
  
  		Assert(!is_from);
  		cstate->rel = NULL;
*************** DoCopy(const CopyStmt *stmt, const char 
*** 1044,1065 ****
  		plan = planner(query, 0, NULL);
  
  		/*
! 		 * Update snapshot command ID to ensure this query sees results of any
! 		 * previously executed queries.  (It's a bit cheesy to modify
! 		 * ActiveSnapshot without making a copy, but for the limited ways in
! 		 * which COPY can be invoked, I think it's OK, because the active
! 		 * snapshot shouldn't be shared with anything else anyway.)
  		 */
! 		ActiveSnapshot->curcid = GetCurrentCommandId(false);
  
  		/* Create dest receiver for COPY OUT */
  		dest = CreateDestReceiver(DestCopyOut, NULL);
  		((DR_copy *) dest)->cstate = cstate;
  
  		/* Create a QueryDesc requesting no output */
! 		cstate->queryDesc = CreateQueryDesc(plan,
! 											ActiveSnapshot, InvalidSnapshot,
  											dest, NULL, false);
  
  		/*
  		 * Call ExecutorStart to prepare the plan for execution.
--- 1045,1064 ----
  		plan = planner(query, 0, NULL);
  
  		/*
! 		 * Use a snapshot with an updated command ID to ensure this query sees
! 		 * results of any previously executed queries.
  		 */
! 		snap = CopySnapshot(GetActiveSnapshot());
! 		snap->curcid = GetCurrentCommandId(false);
  
  		/* Create dest receiver for COPY OUT */
  		dest = CreateDestReceiver(DestCopyOut, NULL);
  		((DR_copy *) dest)->cstate = cstate;
  
  		/* Create a QueryDesc requesting no output */
! 		cstate->queryDesc = CreateQueryDesc(plan, snap, InvalidSnapshot,
  											dest, NULL, false);
+ 		FreeSnapshot(snap);
  
  		/*
  		 * Call ExecutorStart to prepare the plan for execution.
*************** CopyTo(CopyState cstate)
*** 1390,1396 ****
  		values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
  		nulls = (bool *) palloc(num_phys_attrs * sizeof(bool));
  
! 		scandesc = heap_beginscan(cstate->rel, ActiveSnapshot, 0, NULL);
  
  		while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL)
  		{
--- 1389,1395 ----
  		values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
  		nulls = (bool *) palloc(num_phys_attrs * sizeof(bool));
  
! 		scandesc = heap_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL);
  
  		while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL)
  		{
Index: src/backend/commands/explain.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/explain.c,v
retrieving revision 1.171
diff -c -p -r1.171 explain.c
*** src/backend/commands/explain.c	26 Mar 2008 18:48:59 -0000	1.171
--- src/backend/commands/explain.c	4 Apr 2008 23:23:29 -0000
*************** ExplainOnePlan(PlannedStmt *plannedstmt,
*** 226,246 ****
  	ExplainState *es;
  	StringInfoData buf;
  	int			eflags;
  
  	/*
! 	 * Update snapshot command ID to ensure this query sees results of any
! 	 * previously executed queries.  (It's a bit cheesy to modify
! 	 * ActiveSnapshot without making a copy, but for the limited ways in which
! 	 * EXPLAIN can be invoked, I think it's OK, because the active snapshot
! 	 * shouldn't be shared with anything else anyway.)
  	 */
! 	ActiveSnapshot->curcid = GetCurrentCommandId(false);
  
  	/* Create a QueryDesc requesting no output */
  	queryDesc = CreateQueryDesc(plannedstmt,
! 								ActiveSnapshot, InvalidSnapshot,
  								None_Receiver, params,
  								stmt->analyze);
  
  	INSTR_TIME_SET_CURRENT(starttime);
  
--- 226,246 ----
  	ExplainState *es;
  	StringInfoData buf;
  	int			eflags;
+ 	Snapshot	snap;
  
  	/*
! 	 * Use a snapshot with an updates command ID to ensure this query sees
! 	 * results of any previously executed queries.
  	 */
! 	snap = CopySnapshot(GetActiveSnapshot());
! 	snap->curcid = GetCurrentCommandId(false);
  
  	/* Create a QueryDesc requesting no output */
  	queryDesc = CreateQueryDesc(plannedstmt,
! 								snap, InvalidSnapshot,
  								None_Receiver, params,
  								stmt->analyze);
+ 	FreeSnapshot(snap);
  
  	INSTR_TIME_SET_CURRENT(starttime);
  
Index: src/backend/commands/indexcmds.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/indexcmds.c,v
retrieving revision 1.174
diff -c -p -r1.174 indexcmds.c
*** src/backend/commands/indexcmds.c	26 Mar 2008 21:10:37 -0000	1.174
--- src/backend/commands/indexcmds.c	7 Apr 2008 23:13:13 -0000
*************** DefineIndex(RangeVar *heapRelation,
*** 483,488 ****
--- 483,489 ----
  	 */
  	LockRelationIdForSession(&heaprelid, ShareUpdateExclusiveLock);
  
+ 	PopActiveSnapshot();
  	CommitTransactionCommand();
  	StartTransactionCommand();
  
*************** DefineIndex(RangeVar *heapRelation,
*** 541,547 ****
  	indexRelation = index_open(indexRelationId, RowExclusiveLock);
  
  	/* Set ActiveSnapshot since functions in the indexes may need it */
! 	ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
  
  	/* We have to re-build the IndexInfo struct, since it was lost in commit */
  	indexInfo = BuildIndexInfo(indexRelation);
--- 542,548 ----
  	indexRelation = index_open(indexRelationId, RowExclusiveLock);
  
  	/* Set ActiveSnapshot since functions in the indexes may need it */
! 	PushActiveSnapshot(GetTransactionSnapshot());
  
  	/* We have to re-build the IndexInfo struct, since it was lost in commit */
  	indexInfo = BuildIndexInfo(indexRelation);
*************** DefineIndex(RangeVar *heapRelation,
*** 580,585 ****
--- 581,588 ----
  
  	heap_close(pg_index, RowExclusiveLock);
  
+ 	PopActiveSnapshot();
+ 
  	/*
  	 * Commit this transaction to make the indisready update visible.
  	 */
*************** DefineIndex(RangeVar *heapRelation,
*** 616,622 ****
  	 * need a snapshot.
  	 */
  	snapshot = CopySnapshot(GetTransactionSnapshot());
! 	ActiveSnapshot = snapshot;
  
  	/*
  	 * Scan the index and the heap, insert any missing index entries.
--- 619,625 ----
  	 * need a snapshot.
  	 */
  	snapshot = CopySnapshot(GetTransactionSnapshot());
! 	PushActiveSnapshot(snapshot);
  
  	/*
  	 * Scan the index and the heap, insert any missing index entries.
*************** DefineIndex(RangeVar *heapRelation,
*** 644,650 ****
  	 * Also, GetCurrentVirtualXIDs never reports our own vxid, so we need not
  	 * check for that.
  	 */
! 	old_snapshots = GetCurrentVirtualXIDs(ActiveSnapshot->xmax, false,
  										  PROC_IS_AUTOVACUUM | PROC_IN_VACUUM);
  
  	while (VirtualTransactionIdIsValid(*old_snapshots))
--- 647,653 ----
  	 * Also, GetCurrentVirtualXIDs never reports our own vxid, so we need not
  	 * check for that.
  	 */
! 	old_snapshots = GetCurrentVirtualXIDs(snapshot->xmax, false,
  										  PROC_IS_AUTOVACUUM | PROC_IN_VACUUM);
  
  	while (VirtualTransactionIdIsValid(*old_snapshots))
*************** DefineIndex(RangeVar *heapRelation,
*** 675,680 ****
--- 678,685 ----
  
  	heap_close(pg_index, RowExclusiveLock);
  
+ 	PopActiveSnapshot();
+ 
  	/*
  	 * The pg_index update will cause backends (including this one) to update
  	 * relcache entries for the index itself, but we should also send a
*************** ReindexDatabase(const char *databaseName
*** 1459,1469 ****
  
  		StartTransactionCommand();
  		/* functions in indexes may want a snapshot set */
! 		ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
  		if (reindex_relation(relid, true))
  			ereport(NOTICE,
  					(errmsg("table \"%s\" was reindexed",
  							get_rel_name(relid))));
  		CommitTransactionCommand();
  	}
  	StartTransactionCommand();
--- 1464,1475 ----
  
  		StartTransactionCommand();
  		/* functions in indexes may want a snapshot set */
! 		PushActiveSnapshot(GetTransactionSnapshot());
  		if (reindex_relation(relid, true))
  			ereport(NOTICE,
  					(errmsg("table \"%s\" was reindexed",
  							get_rel_name(relid))));
+ 		PopActiveSnapshot();
  		CommitTransactionCommand();
  	}
  	StartTransactionCommand();
Index: src/backend/commands/portalcmds.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/portalcmds.c,v
retrieving revision 1.73
diff -c -p -r1.73 portalcmds.c
*** src/backend/commands/portalcmds.c	2 Apr 2008 18:31:50 -0000	1.73
--- src/backend/commands/portalcmds.c	4 Apr 2008 23:28:30 -0000
*************** PerformCursorOpen(PlannedStmt *stmt, Par
*** 121,127 ****
  	/*
  	 * Start execution, inserting parameters if any.
  	 */
! 	PortalStart(portal, params, ActiveSnapshot);
  
  	Assert(portal->strategy == PORTAL_ONE_SELECT);
  
--- 121,127 ----
  	/*
  	 * Start execution, inserting parameters if any.
  	 */
! 	PortalStart(portal, params, GetActiveSnapshot());
  
  	Assert(portal->strategy == PORTAL_ONE_SELECT);
  
*************** PersistHoldablePortal(Portal portal)
*** 293,299 ****
  {
  	QueryDesc  *queryDesc = PortalGetQueryDesc(portal);
  	Portal		saveActivePortal;
- 	Snapshot	saveActiveSnapshot;
  	ResourceOwner saveResourceOwner;
  	MemoryContext savePortalContext;
  	MemoryContext oldcxt;
--- 293,298 ----
*************** PersistHoldablePortal(Portal portal)
*** 334,351 ****
  	 * Set up global portal context pointers.
  	 */
  	saveActivePortal = ActivePortal;
- 	saveActiveSnapshot = ActiveSnapshot;
  	saveResourceOwner = CurrentResourceOwner;
  	savePortalContext = PortalContext;
  	PG_TRY();
  	{
  		ActivePortal = portal;
- 		ActiveSnapshot = queryDesc->snapshot;
  		CurrentResourceOwner = portal->resowner;
  		PortalContext = PortalGetHeapMemory(portal);
  
  		MemoryContextSwitchTo(PortalContext);
  
  		/*
  		 * Rewind the executor: we need to store the entire result set in the
  		 * tuplestore, so that subsequent backward FETCHs can be processed.
--- 333,350 ----
  	 * Set up global portal context pointers.
  	 */
  	saveActivePortal = ActivePortal;
  	saveResourceOwner = CurrentResourceOwner;
  	savePortalContext = PortalContext;
  	PG_TRY();
  	{
  		ActivePortal = portal;
  		CurrentResourceOwner = portal->resowner;
  		PortalContext = PortalGetHeapMemory(portal);
  
  		MemoryContextSwitchTo(PortalContext);
  
+ 		PushActiveSnapshot(queryDesc->snapshot);
+ 
  		/*
  		 * Rewind the executor: we need to store the entire result set in the
  		 * tuplestore, so that subsequent backward FETCHs can be processed.
*************** PersistHoldablePortal(Portal portal)
*** 411,417 ****
  
  		/* Restore global vars and propagate error */
  		ActivePortal = saveActivePortal;
- 		ActiveSnapshot = saveActiveSnapshot;
  		CurrentResourceOwner = saveResourceOwner;
  		PortalContext = savePortalContext;
  
--- 410,415 ----
*************** PersistHoldablePortal(Portal portal)
*** 425,434 ****
  	portal->status = PORTAL_READY;
  
  	ActivePortal = saveActivePortal;
- 	ActiveSnapshot = saveActiveSnapshot;
  	CurrentResourceOwner = saveResourceOwner;
  	PortalContext = savePortalContext;
  
  	/*
  	 * We can now release any subsidiary memory of the portal's heap context;
  	 * we'll never use it again.  The executor already dropped its context,
--- 423,433 ----
  	portal->status = PORTAL_READY;
  
  	ActivePortal = saveActivePortal;
  	CurrentResourceOwner = saveResourceOwner;
  	PortalContext = savePortalContext;
  
+ 	PopActiveSnapshot();
+ 
  	/*
  	 * We can now release any subsidiary memory of the portal's heap context;
  	 * we'll never use it again.  The executor already dropped its context,
Index: src/backend/commands/prepare.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/prepare.c,v
retrieving revision 1.85
diff -c -p -r1.85 prepare.c
*** src/backend/commands/prepare.c	2 Apr 2008 18:31:50 -0000	1.85
--- src/backend/commands/prepare.c	4 Apr 2008 23:19:10 -0000
*************** ExecuteQuery(ExecuteStmt *stmt, const ch
*** 264,270 ****
  	/*
  	 * Run the portal to completion.
  	 */
! 	PortalStart(portal, paramLI, ActiveSnapshot);
  
  	(void) PortalRun(portal, FETCH_ALL, false, dest, dest, completionTag);
  
--- 264,270 ----
  	/*
  	 * Run the portal to completion.
  	 */
! 	PortalStart(portal, paramLI, GetActiveSnapshot());
  
  	(void) PortalRun(portal, FETCH_ALL, false, dest, dest, completionTag);
  
Index: src/backend/commands/trigger.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/trigger.c,v
retrieving revision 1.231
diff -c -p -r1.231 trigger.c
*** src/backend/commands/trigger.c	28 Mar 2008 00:21:55 -0000	1.231
--- src/backend/commands/trigger.c	4 Apr 2008 23:30:11 -0000
*************** void
*** 2974,2979 ****
--- 2974,2980 ----
  AfterTriggerFireDeferred(void)
  {
  	AfterTriggerEventList *events;
+ 	bool		snap_pushed = false;
  
  	/* Must be inside a transaction */
  	Assert(afterTriggers != NULL);
*************** AfterTriggerFireDeferred(void)
*** 2988,2994 ****
  	 */
  	events = &afterTriggers->events;
  	if (events->head != NULL)
! 		ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
  
  	/*
  	 * Run all the remaining triggers.	Loop until they are all gone, in case
--- 2989,2998 ----
  	 */
  	events = &afterTriggers->events;
  	if (events->head != NULL)
! 	{
! 		PushActiveSnapshot(GetTransactionSnapshot());
! 		snap_pushed = true;
! 	}
  
  	/*
  	 * Run all the remaining triggers.	Loop until they are all gone, in case
*************** AfterTriggerFireDeferred(void)
*** 3001,3006 ****
--- 3005,3013 ----
  		afterTriggerInvokeEvents(events, firing_id, NULL, true);
  	}
  
+ 	if (snap_pushed)
+ 		PopActiveSnapshot();
+ 
  	Assert(events->head == NULL);
  }
  
Index: src/backend/commands/vacuum.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/vacuum.c,v
retrieving revision 1.371
diff -c -p -r1.371 vacuum.c
*** src/backend/commands/vacuum.c	26 Mar 2008 21:10:38 -0000	1.371
--- src/backend/commands/vacuum.c	7 Apr 2008 23:11:49 -0000
*************** vacuum(VacuumStmt *vacstmt, List *relids
*** 407,412 ****
--- 407,416 ----
  	 */
  	if (use_own_xacts)
  	{
+ 		/* ActiveSnapshot is not set by autovacuum */
+ 		if (ActiveSnapshotSet())
+ 			PopActiveSnapshot();
+ 
  		/* matches the StartTransaction in PostgresMain() */
  		CommitTransactionCommand();
  	}
*************** vacuum(VacuumStmt *vacstmt, List *relids
*** 444,450 ****
  				{
  					StartTransactionCommand();
  					/* functions in indexes may want a snapshot set */
! 					ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
  				}
  				else
  					old_context = MemoryContextSwitchTo(anl_context);
--- 448,454 ----
  				{
  					StartTransactionCommand();
  					/* functions in indexes may want a snapshot set */
! 					PushActiveSnapshot(GetTransactionSnapshot());
  				}
  				else
  					old_context = MemoryContextSwitchTo(anl_context);
*************** vacuum(VacuumStmt *vacstmt, List *relids
*** 452,458 ****
--- 456,465 ----
  				analyze_rel(relid, vacstmt, vac_strategy);
  
  				if (use_own_xacts)
+ 				{
+ 					PopActiveSnapshot();
  					CommitTransactionCommand();
+ 				}
  				else
  				{
  					MemoryContextSwitchTo(old_context);
*************** vacuum_rel(Oid relid, VacuumStmt *vacstm
*** 979,985 ****
  	if (vacstmt->full)
  	{
  		/* functions in indexes may want a snapshot set */
! 		ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
  	}
  	else
  	{
--- 986,992 ----
  	if (vacstmt->full)
  	{
  		/* functions in indexes may want a snapshot set */
! 		PushActiveSnapshot(GetTransactionSnapshot());
  	}
  	else
  	{
*************** vacuum_rel(Oid relid, VacuumStmt *vacstm
*** 1036,1041 ****
--- 1043,1050 ----
  
  	if (!onerel)
  	{
+ 		if (vacstmt->full)
+ 			PopActiveSnapshot();
  		CommitTransactionCommand();
  		return;
  	}
*************** vacuum_rel(Oid relid, VacuumStmt *vacstm
*** 1066,1071 ****
--- 1075,1082 ----
  					(errmsg("skipping \"%s\" --- only table or database owner can vacuum it",
  							RelationGetRelationName(onerel))));
  		relation_close(onerel, lmode);
+ 		if (vacstmt->full)
+ 			PopActiveSnapshot();
  		CommitTransactionCommand();
  		return;
  	}
*************** vacuum_rel(Oid relid, VacuumStmt *vacstm
*** 1080,1085 ****
--- 1091,1098 ----
  				(errmsg("skipping \"%s\" --- cannot vacuum indexes, views, or special system tables",
  						RelationGetRelationName(onerel))));
  		relation_close(onerel, lmode);
+ 		if (vacstmt->full)
+ 			PopActiveSnapshot();
  		CommitTransactionCommand();
  		return;
  	}
*************** vacuum_rel(Oid relid, VacuumStmt *vacstm
*** 1094,1099 ****
--- 1107,1114 ----
  	if (isOtherTempNamespace(RelationGetNamespace(onerel)))
  	{
  		relation_close(onerel, lmode);
+ 		if (vacstmt->full)
+ 			PopActiveSnapshot();
  		CommitTransactionCommand();
  		return;
  	}
*************** vacuum_rel(Oid relid, VacuumStmt *vacstm
*** 1141,1146 ****
--- 1156,1163 ----
  	/*
  	 * Complete the transaction and free all temporary memory used.
  	 */
+ 	if (vacstmt->full)
+ 		PopActiveSnapshot();
  	CommitTransactionCommand();
  
  	/*
Index: src/backend/executor/functions.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/executor/functions.c,v
retrieving revision 1.124
diff -c -p -r1.124 functions.c
*** src/backend/executor/functions.c	26 Mar 2008 18:48:59 -0000	1.124
--- src/backend/executor/functions.c	8 Apr 2008 00:49:40 -0000
*************** postquel_start(execution_state *es, SQLF
*** 299,305 ****
  	 * unconditionally do FreeSnapshot.
  	 */
  	if (fcache->readonly_func)
! 		snapshot = CopySnapshot(ActiveSnapshot);
  	else
  	{
  		CommandCounterIncrement();
--- 299,305 ----
  	 * unconditionally do FreeSnapshot.
  	 */
  	if (fcache->readonly_func)
! 		snapshot = CopySnapshot(GetActiveSnapshot());
  	else
  	{
  		CommandCounterIncrement();
*************** static TupleTableSlot *
*** 340,353 ****
  postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache)
  {
  	TupleTableSlot *result;
- 	Snapshot	saveActiveSnapshot;
  	long		count;
  
  	/* Make our snapshot the active one for any called functions */
! 	saveActiveSnapshot = ActiveSnapshot;
! 	PG_TRY();
! 	{
! 		ActiveSnapshot = es->qd->snapshot;
  
  		if (es->qd->utilitystmt)
  		{
--- 340,349 ----
  postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache)
  {
  	TupleTableSlot *result;
  	long		count;
  
  	/* Make our snapshot the active one for any called functions */
! 	PushActiveSnapshot(es->qd->snapshot);
  
  		if (es->qd->utilitystmt)
  		{
*************** postquel_getnext(execution_state *es, SQ
*** 380,395 ****
  
  			result = ExecutorRun(es->qd, ForwardScanDirection, count);
  		}
- 	}
- 	PG_CATCH();
- 	{
- 		/* Restore global vars and propagate error */
- 		ActiveSnapshot = saveActiveSnapshot;
- 		PG_RE_THROW();
- 	}
- 	PG_END_TRY();
  
! 	ActiveSnapshot = saveActiveSnapshot;
  
  	return result;
  }
--- 376,383 ----
  
  			result = ExecutorRun(es->qd, ForwardScanDirection, count);
  		}
  
! 	PopActiveSnapshot();
  
  	return result;
  }
*************** postquel_getnext(execution_state *es, SQ
*** 397,404 ****
  static void
  postquel_end(execution_state *es)
  {
- 	Snapshot	saveActiveSnapshot;
- 
  	/* mark status done to ensure we don't do ExecutorEnd twice */
  	es->status = F_EXEC_DONE;
  
--- 385,390 ----
*************** postquel_end(execution_state *es)
*** 406,428 ****
  	if (es->qd->utilitystmt == NULL)
  	{
  		/* Make our snapshot the active one for any called functions */
! 		saveActiveSnapshot = ActiveSnapshot;
! 		PG_TRY();
! 		{
! 			ActiveSnapshot = es->qd->snapshot;
  
  			if (es->qd->operation != CMD_SELECT)
  				AfterTriggerEndQuery(es->qd->estate);
  			ExecutorEnd(es->qd);
! 		}
! 		PG_CATCH();
! 		{
! 			/* Restore global vars and propagate error */
! 			ActiveSnapshot = saveActiveSnapshot;
! 			PG_RE_THROW();
! 		}
! 		PG_END_TRY();
! 		ActiveSnapshot = saveActiveSnapshot;
  	}
  
  	FreeSnapshot(es->qd->snapshot);
--- 392,404 ----
  	if (es->qd->utilitystmt == NULL)
  	{
  		/* Make our snapshot the active one for any called functions */
! 		PushActiveSnapshot(es->qd->snapshot);
  
  			if (es->qd->operation != CMD_SELECT)
  				AfterTriggerEndQuery(es->qd->estate);
  			ExecutorEnd(es->qd);
! 
! 		PopActiveSnapshot();
  	}
  
  	FreeSnapshot(es->qd->snapshot);
Index: src/backend/executor/spi.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/executor/spi.c,v
retrieving revision 1.193
diff -c -p -r1.193 spi.c
*** src/backend/executor/spi.c	2 Apr 2008 18:31:50 -0000	1.193
--- src/backend/executor/spi.c	8 Apr 2008 00:48:05 -0000
*************** SPI_cursor_open(const char *name, SPIPla
*** 1105,1111 ****
  	 * skip that here.)
  	 */
  	if (read_only)
! 		snapshot = ActiveSnapshot;
  	else
  	{
  		CommandCounterIncrement();
--- 1105,1111 ----
  	 * skip that here.)
  	 */
  	if (read_only)
! 		snapshot = GetActiveSnapshot();
  	else
  	{
  		CommandCounterIncrement();
*************** _SPI_execute_plan(SPIPlanPtr plan, Param
*** 1609,1623 ****
  	volatile Oid my_lastoid = InvalidOid;
  	SPITupleTable *volatile my_tuptable = NULL;
  	volatile int res = 0;
! 	Snapshot	saveActiveSnapshot;
! 
! 	/* Be sure to restore ActiveSnapshot on error exit */
! 	saveActiveSnapshot = ActiveSnapshot;
! 	PG_TRY();
! 	{
! 		ErrorContextCallback spierrcontext;
! 		CachedPlan *cplan = NULL;
! 		ListCell   *lc1;
  
  		/*
  		 * Setup error traceback support for ereport()
--- 1609,1618 ----
  	volatile Oid my_lastoid = InvalidOid;
  	SPITupleTable *volatile my_tuptable = NULL;
  	volatile int res = 0;
! 	bool		have_active_snap = ActiveSnapshotSet();
! 	ErrorContextCallback spierrcontext;
! 	CachedPlan *cplan = NULL;
! 	ListCell   *lc1;
  
  		/*
  		 * Setup error traceback support for ereport()
*************** _SPI_execute_plan(SPIPlanPtr plan, Param
*** 1653,1658 ****
--- 1648,1654 ----
  				Node	   *stmt = (Node *) lfirst(lc2);
  				bool		canSetTag;
  				DestReceiver *dest;
+ 				bool		pushed_active_snap = false;
  
  				_SPI_current->processed = 0;
  				_SPI_current->lastoid = InvalidOid;
*************** _SPI_execute_plan(SPIPlanPtr plan, Param
*** 1708,1737 ****
  					 * ActiveSnapshot; if read-write, grab a full new snap.
  					 */
  					if (read_only)
! 						ActiveSnapshot = CopySnapshot(saveActiveSnapshot);
  					else
! 						ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
  				}
  				else
  				{
  					/*
  					 * We interpret read_only with a specified snapshot to be
  					 * exactly that snapshot, but read-write means use the
  					 * snap with advancing of command ID.
  					 */
! 					ActiveSnapshot = CopySnapshot(snapshot);
  					if (!read_only)
! 						ActiveSnapshot->curcid = GetCurrentCommandId(false);
  				}
  
  				if (IsA(stmt, PlannedStmt) &&
  					((PlannedStmt *) stmt)->utilityStmt == NULL)
  				{
  					QueryDesc  *qdesc;
  
  					qdesc = CreateQueryDesc((PlannedStmt *) stmt,
! 											ActiveSnapshot,
! 											crosscheck_snapshot,
  											dest,
  											paramLI, false);
  					res = _SPI_pquery(qdesc, fire_triggers,
--- 1704,1752 ----
  					 * ActiveSnapshot; if read-write, grab a full new snap.
  					 */
  					if (read_only)
! 					{
! 						if (have_active_snap)
! 						{
! 							PushActiveSnapshot(GetActiveSnapshot());
! 							pushed_active_snap = true;
! 						}
! 					}
  					else
! 					{
! 						PushActiveSnapshot(GetTransactionSnapshot());
! 						pushed_active_snap = true;
! 					}
  				}
  				else
  				{
+ 					Snapshot	snap;
+ 
  					/*
  					 * We interpret read_only with a specified snapshot to be
  					 * exactly that snapshot, but read-write means use the
  					 * snap with advancing of command ID.
  					 */
! 					snap = CopySnapshot(snapshot);
  					if (!read_only)
! 						snap->curcid = GetCurrentCommandId(false);
! 					PushActiveSnapshot(snap);
! 					pushed_active_snap = true;
! 					FreeSnapshot(snap);
  				}
  
  				if (IsA(stmt, PlannedStmt) &&
  					((PlannedStmt *) stmt)->utilityStmt == NULL)
  				{
  					QueryDesc  *qdesc;
+ 					Snapshot	snap;
+ 
+ 					if (ActiveSnapshotSet())
+ 						snap = GetActiveSnapshot();
+ 					else
+ 						snap = InvalidSnapshot;
  
  					qdesc = CreateQueryDesc((PlannedStmt *) stmt,
! 											snap, crosscheck_snapshot,
  											dest,
  											paramLI, false);
  					res = _SPI_pquery(qdesc, fire_triggers,
*************** _SPI_execute_plan(SPIPlanPtr plan, Param
*** 1751,1758 ****
  						_SPI_current->processed = _SPI_current->tuptable->alloced - _SPI_current->tuptable->free;
  					res = SPI_OK_UTILITY;
  				}
! 				FreeSnapshot(ActiveSnapshot);
! 				ActiveSnapshot = NULL;
  
  				/*
  				 * The last canSetTag query sets the status values returned to
--- 1766,1774 ----
  						_SPI_current->processed = _SPI_current->tuptable->alloced - _SPI_current->tuptable->free;
  					res = SPI_OK_UTILITY;
  				}
! 
! 				if (pushed_active_snap)
! 					PopActiveSnapshot();
  
  				/*
  				 * The last canSetTag query sets the status values returned to
*************** fail:
*** 1804,1819 ****
  		 * Pop the error context stack
  		 */
  		error_context_stack = spierrcontext.previous;
- 	}
- 	PG_CATCH();
- 	{
- 		/* Restore global vars and propagate error */
- 		ActiveSnapshot = saveActiveSnapshot;
- 		PG_RE_THROW();
- 	}
- 	PG_END_TRY();
- 
- 	ActiveSnapshot = saveActiveSnapshot;
  
  	/* Save results for caller */
  	SPI_processed = my_processed;
--- 1820,1825 ----
Index: src/backend/storage/large_object/inv_api.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/storage/large_object/inv_api.c,v
retrieving revision 1.131
diff -c -p -r1.131 inv_api.c
*** src/backend/storage/large_object/inv_api.c	26 Mar 2008 21:10:38 -0000	1.131
--- src/backend/storage/large_object/inv_api.c	4 Apr 2008 21:21:28 -0000
*************** inv_open(Oid lobjId, int flags, MemoryCo
*** 248,254 ****
  		/* be sure to copy snap into mcxt */
  		MemoryContext oldContext = MemoryContextSwitchTo(mcxt);
  
! 		retval->snapshot = CopySnapshot(ActiveSnapshot);
  		retval->flags = IFS_RDLOCK;
  		MemoryContextSwitchTo(oldContext);
  	}
--- 248,254 ----
  		/* be sure to copy snap into mcxt */
  		MemoryContext oldContext = MemoryContextSwitchTo(mcxt);
  
! 		retval->snapshot = CopySnapshot(GetActiveSnapshot());
  		retval->flags = IFS_RDLOCK;
  		MemoryContextSwitchTo(oldContext);
  	}
Index: src/backend/tcop/fastpath.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/tcop/fastpath.c,v
retrieving revision 1.99
diff -c -p -r1.99 fastpath.c
*** src/backend/tcop/fastpath.c	26 Mar 2008 18:48:59 -0000	1.99
--- src/backend/tcop/fastpath.c	4 Apr 2008 21:23:29 -0000
*************** HandleFunctionRequest(StringInfo msgBuf)
*** 309,315 ****
  	 * Now that we know we are in a valid transaction, set snapshot in case
  	 * needed by function itself or one of the datatype I/O routines.
  	 */
! 	ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
  
  	/*
  	 * Begin parsing the buffer contents.
--- 309,315 ----
  	 * Now that we know we are in a valid transaction, set snapshot in case
  	 * needed by function itself or one of the datatype I/O routines.
  	 */
! 	PushActiveSnapshot(GetTransactionSnapshot());
  
  	/*
  	 * Begin parsing the buffer contents.
*************** HandleFunctionRequest(StringInfo msgBuf)
*** 412,417 ****
--- 412,419 ----
  			break;
  	}
  
+ 	PopActiveSnapshot();
+ 
  	return 0;
  }
  
Index: src/backend/tcop/postgres.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/tcop/postgres.c,v
retrieving revision 1.548
diff -c -p -r1.548 postgres.c
*** src/backend/tcop/postgres.c	2 Apr 2008 18:31:50 -0000	1.548
--- src/backend/tcop/postgres.c	8 Apr 2008 00:50:40 -0000
*************** pg_plan_queries(List *querytrees, int cu
*** 732,744 ****
  				bool needSnapshot)
  {
  	List	   * volatile stmt_list = NIL;
! 	Snapshot	saveActiveSnapshot = ActiveSnapshot;
! 
! 	/* PG_TRY to ensure previous ActiveSnapshot is restored on error */
! 	PG_TRY();
! 	{
! 		Snapshot	mySnapshot = NULL;
! 		ListCell   *query_list;
  
  		foreach(query_list, querytrees)
  		{
--- 732,739 ----
  				bool needSnapshot)
  {
  	List	   * volatile stmt_list = NIL;
! 	ListCell   *query_list;
! 	bool		snapshot_set = false;
  
  		foreach(query_list, querytrees)
  		{
*************** pg_plan_queries(List *querytrees, int cu
*** 752,762 ****
  			}
  			else
  			{
! 				if (needSnapshot && mySnapshot == NULL)
  				{
! 					mySnapshot = CopySnapshot(GetTransactionSnapshot());
! 					ActiveSnapshot = mySnapshot;
  				}
  				stmt = (Node *) pg_plan_query(query, cursorOptions,
  											  boundParams);
  			}
--- 747,758 ----
  			}
  			else
  			{
! 				if (needSnapshot && !snapshot_set)
  				{
! 					PushActiveSnapshot(GetTransactionSnapshot());
! 					snapshot_set = true;
  				}
+ 
  				stmt = (Node *) pg_plan_query(query, cursorOptions,
  											  boundParams);
  			}
*************** pg_plan_queries(List *querytrees, int cu
*** 764,779 ****
  			stmt_list = lappend(stmt_list, stmt);
  		}
  
! 		if (mySnapshot)
! 			FreeSnapshot(mySnapshot);
! 	}
! 	PG_CATCH();
! 	{
! 		ActiveSnapshot = saveActiveSnapshot;
! 		PG_RE_THROW();
! 	}
! 	PG_END_TRY();
! 	ActiveSnapshot = saveActiveSnapshot;
  
  	return stmt_list;
  }
--- 760,767 ----
  			stmt_list = lappend(stmt_list, stmt);
  		}
  
! 	if (snapshot_set)
! 		PopActiveSnapshot();
  
  	return stmt_list;
  }
Index: src/backend/tcop/pquery.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/tcop/pquery.c,v
retrieving revision 1.122
diff -c -p -r1.122 pquery.c
*** src/backend/tcop/pquery.c	26 Mar 2008 18:48:59 -0000	1.122
--- src/backend/tcop/pquery.c	8 Apr 2008 01:31:27 -0000
*************** CreateQueryDesc(PlannedStmt *plannedstmt
*** 70,77 ****
  	qd->operation = plannedstmt->commandType;	/* operation */
  	qd->plannedstmt = plannedstmt;		/* plan */
  	qd->utilitystmt = plannedstmt->utilityStmt; /* in case DECLARE CURSOR */
! 	qd->snapshot = snapshot;	/* snapshot */
! 	qd->crosscheck_snapshot = crosscheck_snapshot;		/* RI check snapshot */
  	qd->dest = dest;			/* output dest */
  	qd->params = params;		/* parameter values passed into query */
  	qd->doInstrument = doInstrument;	/* instrumentation wanted? */
--- 70,80 ----
  	qd->operation = plannedstmt->commandType;	/* operation */
  	qd->plannedstmt = plannedstmt;		/* plan */
  	qd->utilitystmt = plannedstmt->utilityStmt; /* in case DECLARE CURSOR */
! 	qd->snapshot = CopySnapshot(snapshot);	/* snapshot */
! 	if (crosscheck_snapshot != InvalidSnapshot)		/* RI check snapshot */
! 		qd->crosscheck_snapshot = CopySnapshot(crosscheck_snapshot);
! 	else
! 		qd->crosscheck_snapshot = InvalidSnapshot;
  	qd->dest = dest;			/* output dest */
  	qd->params = params;		/* parameter values passed into query */
  	qd->doInstrument = doInstrument;	/* instrumentation wanted? */
*************** CreateUtilityQueryDesc(Node *utilitystmt
*** 98,104 ****
  	qd->operation = CMD_UTILITY;	/* operation */
  	qd->plannedstmt = NULL;
  	qd->utilitystmt = utilitystmt;		/* utility command */
! 	qd->snapshot = snapshot;	/* snapshot */
  	qd->crosscheck_snapshot = InvalidSnapshot;	/* RI check snapshot */
  	qd->dest = dest;			/* output dest */
  	qd->params = params;		/* parameter values passed into query */
--- 101,107 ----
  	qd->operation = CMD_UTILITY;	/* operation */
  	qd->plannedstmt = NULL;
  	qd->utilitystmt = utilitystmt;		/* utility command */
! 	qd->snapshot = CopySnapshot(snapshot);	/* snapshot */
  	qd->crosscheck_snapshot = InvalidSnapshot;	/* RI check snapshot */
  	qd->dest = dest;			/* output dest */
  	qd->params = params;		/* parameter values passed into query */
*************** ProcessQuery(PlannedStmt *plan,
*** 152,167 ****
  	elog(DEBUG3, "ProcessQuery");
  
  	/*
! 	 * Must always set snapshot for plannable queries.	Note we assume that
! 	 * caller will take care of restoring ActiveSnapshot on exit/error.
  	 */
! 	ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
  
  	/*
  	 * Create the QueryDesc object
  	 */
  	queryDesc = CreateQueryDesc(plan,
! 								ActiveSnapshot, InvalidSnapshot,
  								dest, params, false);
  
  	/*
--- 155,169 ----
  	elog(DEBUG3, "ProcessQuery");
  
  	/*
! 	 * Must always set a snapshot for plannable queries.
  	 */
! 	PushActiveSnapshot(GetTransactionSnapshot());
  
  	/*
  	 * Create the QueryDesc object
  	 */
  	queryDesc = CreateQueryDesc(plan,
! 								GetActiveSnapshot(), InvalidSnapshot,
  								dest, params, false);
  
  	/*
*************** ProcessQuery(PlannedStmt *plan,
*** 216,230 ****
  	/* Now take care of any queued AFTER triggers */
  	AfterTriggerEndQuery(queryDesc->estate);
  
  	/*
  	 * Now, we close down all the scans and free allocated resources.
  	 */
  	ExecutorEnd(queryDesc);
  
  	FreeQueryDesc(queryDesc);
- 
- 	FreeSnapshot(ActiveSnapshot);
- 	ActiveSnapshot = NULL;
  }
  
  /*
--- 218,231 ----
  	/* Now take care of any queued AFTER triggers */
  	AfterTriggerEndQuery(queryDesc->estate);
  
+ 	PopActiveSnapshot();
+ 
  	/*
  	 * Now, we close down all the scans and free allocated resources.
  	 */
  	ExecutorEnd(queryDesc);
  
  	FreeQueryDesc(queryDesc);
  }
  
  /*
*************** void
*** 446,452 ****
  PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
  {
  	Portal		saveActivePortal;
- 	Snapshot	saveActiveSnapshot;
  	ResourceOwner saveResourceOwner;
  	MemoryContext savePortalContext;
  	MemoryContext oldContext;
--- 447,452 ----
*************** PortalStart(Portal portal, ParamListInfo
*** 460,472 ****
  	 * Set up global portal context pointers.
  	 */
  	saveActivePortal = ActivePortal;
- 	saveActiveSnapshot = ActiveSnapshot;
  	saveResourceOwner = CurrentResourceOwner;
  	savePortalContext = PortalContext;
  	PG_TRY();
  	{
  		ActivePortal = portal;
- 		ActiveSnapshot = NULL;	/* will be set later */
  		CurrentResourceOwner = portal->resowner;
  		PortalContext = PortalGetHeapMemory(portal);
  
--- 460,470 ----
*************** PortalStart(Portal portal, ParamListInfo
*** 492,507 ****
  				 * copy it into the portal's context.
  				 */
  				if (snapshot)
! 					ActiveSnapshot = CopySnapshot(snapshot);
  				else
! 					ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
  
  				/*
  				 * Create QueryDesc in portal's context; for the moment, set
  				 * the destination to DestNone.
  				 */
  				queryDesc = CreateQueryDesc((PlannedStmt *) linitial(portal->stmts),
! 											ActiveSnapshot,
  											InvalidSnapshot,
  											None_Receiver,
  											params,
--- 490,505 ----
  				 * copy it into the portal's context.
  				 */
  				if (snapshot)
! 					PushActiveSnapshot(snapshot);
  				else
! 					PushActiveSnapshot(GetTransactionSnapshot());
  
  				/*
  				 * Create QueryDesc in portal's context; for the moment, set
  				 * the destination to DestNone.
  				 */
  				queryDesc = CreateQueryDesc((PlannedStmt *) linitial(portal->stmts),
! 											GetActiveSnapshot(),
  											InvalidSnapshot,
  											None_Receiver,
  											params,
*************** PortalStart(Portal portal, ParamListInfo
*** 545,550 ****
--- 543,550 ----
  				portal->atEnd = false;	/* allow fetches */
  				portal->portalPos = 0;
  				portal->posOverflow = false;
+ 
+ 				PopActiveSnapshot();
  				break;
  
  			case PORTAL_ONE_RETURNING:
*************** PortalStart(Portal portal, ParamListInfo
*** 608,614 ****
  
  		/* Restore global vars and propagate error */
  		ActivePortal = saveActivePortal;
- 		ActiveSnapshot = saveActiveSnapshot;
  		CurrentResourceOwner = saveResourceOwner;
  		PortalContext = savePortalContext;
  
--- 608,613 ----
*************** PortalStart(Portal portal, ParamListInfo
*** 619,625 ****
  	MemoryContextSwitchTo(oldContext);
  
  	ActivePortal = saveActivePortal;
- 	ActiveSnapshot = saveActiveSnapshot;
  	CurrentResourceOwner = saveResourceOwner;
  	PortalContext = savePortalContext;
  
--- 618,623 ----
*************** PortalRun(Portal portal, long count, boo
*** 707,713 ****
  	ResourceOwner saveTopTransactionResourceOwner;
  	MemoryContext saveTopTransactionContext;
  	Portal		saveActivePortal;
- 	Snapshot	saveActiveSnapshot;
  	ResourceOwner saveResourceOwner;
  	MemoryContext savePortalContext;
  	MemoryContext saveMemoryContext;
--- 705,710 ----
*************** PortalRun(Portal portal, long count, boo
*** 751,764 ****
  	saveTopTransactionResourceOwner = TopTransactionResourceOwner;
  	saveTopTransactionContext = TopTransactionContext;
  	saveActivePortal = ActivePortal;
- 	saveActiveSnapshot = ActiveSnapshot;
  	saveResourceOwner = CurrentResourceOwner;
  	savePortalContext = PortalContext;
  	saveMemoryContext = CurrentMemoryContext;
  	PG_TRY();
  	{
  		ActivePortal = portal;
- 		ActiveSnapshot = NULL;	/* will be set later */
  		CurrentResourceOwner = portal->resowner;
  		PortalContext = PortalGetHeapMemory(portal);
  
--- 748,759 ----
*************** PortalRun(Portal portal, long count, boo
*** 839,845 ****
  		else
  			MemoryContextSwitchTo(saveMemoryContext);
  		ActivePortal = saveActivePortal;
- 		ActiveSnapshot = saveActiveSnapshot;
  		if (saveResourceOwner == saveTopTransactionResourceOwner)
  			CurrentResourceOwner = TopTransactionResourceOwner;
  		else
--- 834,839 ----
*************** PortalRun(Portal portal, long count, boo
*** 855,861 ****
  	else
  		MemoryContextSwitchTo(saveMemoryContext);
  	ActivePortal = saveActivePortal;
- 	ActiveSnapshot = saveActiveSnapshot;
  	if (saveResourceOwner == saveTopTransactionResourceOwner)
  		CurrentResourceOwner = TopTransactionResourceOwner;
  	else
--- 849,854 ----
*************** PortalRunSelect(Portal portal,
*** 940,948 ****
  			nprocessed = RunFromStore(portal, direction, count, dest);
  		else
  		{
! 			ActiveSnapshot = queryDesc->snapshot;
  			ExecutorRun(queryDesc, direction, count);
  			nprocessed = queryDesc->estate->es_processed;
  		}
  
  		if (!ScanDirectionIsNoMovement(direction))
--- 933,942 ----
  			nprocessed = RunFromStore(portal, direction, count, dest);
  		else
  		{
! 			PushActiveSnapshot(queryDesc->snapshot);
  			ExecutorRun(queryDesc, direction, count);
  			nprocessed = queryDesc->estate->es_processed;
+ 			PopActiveSnapshot();
  		}
  
  		if (!ScanDirectionIsNoMovement(direction))
*************** PortalRunSelect(Portal portal,
*** 982,990 ****
  			nprocessed = RunFromStore(portal, direction, count, dest);
  		else
  		{
! 			ActiveSnapshot = queryDesc->snapshot;
  			ExecutorRun(queryDesc, direction, count);
  			nprocessed = queryDesc->estate->es_processed;
  		}
  
  		if (!ScanDirectionIsNoMovement(direction))
--- 976,985 ----
  			nprocessed = RunFromStore(portal, direction, count, dest);
  		else
  		{
! 			PushActiveSnapshot(queryDesc->snapshot);
  			ExecutorRun(queryDesc, direction, count);
  			nprocessed = queryDesc->estate->es_processed;
+ 			PopActiveSnapshot();
  		}
  
  		if (!ScanDirectionIsNoMovement(direction))
*************** static void
*** 1140,1145 ****
--- 1135,1142 ----
  PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
  				 DestReceiver *dest, char *completionTag)
  {
+ 	bool	active_snapshot_set;
+ 
  	elog(DEBUG3, "ProcessUtility");
  
  	/*
*************** PortalRunUtility(Portal portal, Node *ut
*** 1152,1160 ****
  	 * hacks.  Beware of listing anything that can modify the database --- if,
  	 * say, it has to update an index with expressions that invoke
  	 * user-defined functions, then it had better have a snapshot.
- 	 *
- 	 * Note we assume that caller will take care of restoring ActiveSnapshot
- 	 * on exit/error.
  	 */
  	if (!(IsA(utilityStmt, TransactionStmt) ||
  		  IsA(utilityStmt, LockStmt) ||
--- 1149,1154 ----
*************** PortalRunUtility(Portal portal, Node *ut
*** 1167,1175 ****
  		  IsA(utilityStmt, NotifyStmt) ||
  		  IsA(utilityStmt, UnlistenStmt) ||
  		  IsA(utilityStmt, CheckPointStmt)))
! 		ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
  	else
! 		ActiveSnapshot = NULL;
  
  	ProcessUtility(utilityStmt,
  				   portal->sourceText,
--- 1161,1172 ----
  		  IsA(utilityStmt, NotifyStmt) ||
  		  IsA(utilityStmt, UnlistenStmt) ||
  		  IsA(utilityStmt, CheckPointStmt)))
! 	{
! 		PushActiveSnapshot(GetTransactionSnapshot());
! 		active_snapshot_set = true;
! 	}
  	else
! 		active_snapshot_set = false;
  
  	ProcessUtility(utilityStmt,
  				   portal->sourceText,
*************** PortalRunUtility(Portal portal, Node *ut
*** 1181,1189 ****
  	/* Some utility statements may change context on us */
  	MemoryContextSwitchTo(PortalGetHeapMemory(portal));
  
! 	if (ActiveSnapshot)
! 		FreeSnapshot(ActiveSnapshot);
! 	ActiveSnapshot = NULL;
  }
  
  /*
--- 1178,1192 ----
  	/* Some utility statements may change context on us */
  	MemoryContextSwitchTo(PortalGetHeapMemory(portal));
  
! 	/*
! 	 * Some utility commands may pop the ActiveSnapshot stack from under us,
! 	 * so we only pop the stack if we actually see a snapshot set.  Note that
! 	 * the set of utility commands that do this must be the same set
! 	 * disallowed to run inside a transaction; otherwise, we could be popping
! 	 * a snapshot that belong to some other operation.
! 	 */
! 	if (active_snapshot_set && ActiveSnapshotSet())
! 		PopActiveSnapshot();
  }
  
  /*
*************** PortalRunFetch(Portal portal,
*** 1321,1327 ****
  {
  	long		result;
  	Portal		saveActivePortal;
- 	Snapshot	saveActiveSnapshot;
  	ResourceOwner saveResourceOwner;
  	MemoryContext savePortalContext;
  	MemoryContext oldContext;
--- 1324,1329 ----
*************** PortalRunFetch(Portal portal,
*** 1341,1353 ****
  	 * Set up global portal context pointers.
  	 */
  	saveActivePortal = ActivePortal;
- 	saveActiveSnapshot = ActiveSnapshot;
  	saveResourceOwner = CurrentResourceOwner;
  	savePortalContext = PortalContext;
  	PG_TRY();
  	{
  		ActivePortal = portal;
- 		ActiveSnapshot = NULL;	/* will be set later */
  		CurrentResourceOwner = portal->resowner;
  		PortalContext = PortalGetHeapMemory(portal);
  
--- 1343,1353 ----
*************** PortalRunFetch(Portal portal,
*** 1388,1394 ****
  
  		/* Restore global vars and propagate error */
  		ActivePortal = saveActivePortal;
- 		ActiveSnapshot = saveActiveSnapshot;
  		CurrentResourceOwner = saveResourceOwner;
  		PortalContext = savePortalContext;
  
--- 1388,1393 ----
*************** PortalRunFetch(Portal portal,
*** 1402,1408 ****
  	portal->status = PORTAL_READY;
  
  	ActivePortal = saveActivePortal;
- 	ActiveSnapshot = saveActiveSnapshot;
  	CurrentResourceOwner = saveResourceOwner;
  	PortalContext = savePortalContext;
  
--- 1401,1406 ----
Index: src/backend/utils/adt/txid.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/utils/adt/txid.c,v
retrieving revision 1.6
diff -c -p -r1.6 txid.c
*** src/backend/utils/adt/txid.c	26 Mar 2008 18:48:59 -0000	1.6
--- src/backend/utils/adt/txid.c	4 Apr 2008 22:11:25 -0000
*************** txid_current_snapshot(PG_FUNCTION_ARGS)
*** 362,368 ****
  	TxidEpoch	state;
  	Snapshot	cur;
  
! 	cur = ActiveSnapshot;
  	if (cur == NULL)
  		elog(ERROR, "txid_current_snapshot: ActiveSnapshot == NULL");
  
--- 362,368 ----
  	TxidEpoch	state;
  	Snapshot	cur;
  
! 	cur = GetActiveSnapshot();
  	if (cur == NULL)
  		elog(ERROR, "txid_current_snapshot: ActiveSnapshot == NULL");
  
Index: src/backend/utils/cache/plancache.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/utils/cache/plancache.c,v
retrieving revision 1.17
diff -c -p -r1.17 plancache.c
*** src/backend/utils/cache/plancache.c	26 Mar 2008 18:48:59 -0000	1.17
--- src/backend/utils/cache/plancache.c	4 Apr 2008 23:14:35 -0000
*************** do_planning(List *querytrees, int cursor
*** 550,580 ****
  	/*
  	 * If a snapshot is already set (the normal case), we can just use that
  	 * for planning.  But if it isn't, we have to tell pg_plan_queries to make
! 	 * a snap if it needs one.	In that case we should arrange to reset
! 	 * ActiveSnapshot afterward, to ensure that RevalidateCachedPlan has no
! 	 * caller-visible effects on the snapshot.	Having to replan is an unusual
! 	 * case, and it seems a really bad idea for RevalidateCachedPlan to affect
! 	 * the snapshot only in unusual cases.	(Besides, the snap might have been
! 	 * created in a short-lived context.)
  	 */
! 	if (ActiveSnapshot != NULL)
! 		stmt_list = pg_plan_queries(querytrees, cursorOptions, NULL, false);
! 	else
! 	{
! 		PG_TRY();
! 		{
! 			stmt_list = pg_plan_queries(querytrees, cursorOptions, NULL, true);
! 		}
! 		PG_CATCH();
! 		{
! 			/* Restore global vars and propagate error */
! 			ActiveSnapshot = NULL;
! 			PG_RE_THROW();
! 		}
! 		PG_END_TRY();
! 
! 		ActiveSnapshot = NULL;
! 	}
  
  	return stmt_list;
  }
--- 550,560 ----
  	/*
  	 * If a snapshot is already set (the normal case), we can just use that
  	 * for planning.  But if it isn't, we have to tell pg_plan_queries to make
! 	 * a snap if it needs one.	pg_plan_queries is also responsible for making
! 	 * sure ActiveSnapshot is reset in that case.
  	 */
! 	stmt_list = pg_plan_queries(querytrees, cursorOptions, NULL,
! 								!ActiveSnapshotSet());
  
  	return stmt_list;
  }
Index: src/backend/utils/time/snapmgr.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/utils/time/snapmgr.c,v
retrieving revision 1.1
diff -c -p -r1.1 snapmgr.c
*** src/backend/utils/time/snapmgr.c	26 Mar 2008 18:48:59 -0000	1.1
--- src/backend/utils/time/snapmgr.c	8 Apr 2008 01:36:27 -0000
***************
*** 17,22 ****
--- 17,23 ----
  #include "storage/procarray.h"
  #include "utils/snapmgr.h"
  #include "utils/tqual.h"
+ #include "utils/memutils.h"
  
  
  /*
*************** Snapshot	SerializableSnapshot = NULL;
*** 31,43 ****
  Snapshot	LatestSnapshot = NULL;
  
  /*
-  * This pointer is not maintained by this module, but it's convenient
-  * to declare it here anyway.  Callers typically assign a copy of
-  * GetTransactionSnapshot's result to ActiveSnapshot.
-  */
- Snapshot	ActiveSnapshot = NULL;
- 
- /*
   * These are updated by GetSnapshotData.  We initialize them this way
   * for the convenience of TransactionIdIsInProgress: even in bootstrap
   * mode, we don't want it to say that BootstrapTransactionId is in progress.
--- 32,37 ----
*************** TransactionId TransactionXmin = FirstNor
*** 46,51 ****
--- 40,59 ----
  TransactionId RecentXmin = FirstNormalTransactionId;
  TransactionId RecentGlobalXmin = FirstNormalTransactionId;
  
+ /* ActiveSnapshot is actually a stack of these elements: */
+ typedef struct ActiveSnapshotElt
+ {
+ 	Snapshot	as_snap;
+ 	int			as_level;
+ 	char	   *as_name;
+ 	struct ActiveSnapshotElt *as_next;
+ } ActiveSnapshotElt;
+ 
+ static ActiveSnapshotElt	*ActiveSnapshot = NULL;
+ 
+ /* Memory context where tracked snapshots reside */
+ static MemoryContext		SnapshotContext;
+ 
  
  /*
   * GetTransactionSnapshot
*************** GetLatestSnapshot(void)
*** 95,104 ****
  }
  
  /*
   * CopySnapshot
   *		Copy the given snapshot.
   *
!  * The copy is palloc'd in the current memory context.
   */
  Snapshot
  CopySnapshot(Snapshot snapshot)
--- 103,128 ----
  }
  
  /*
+  * Initialize the private memory context for snapshots
+  */
+ static void
+ SnapshotMemoryInit(void)
+ {
+ 	if (SnapshotContext != NULL)
+ 		return;
+ 
+ 	SnapshotContext = AllocSetContextCreate(TopTransactionContext,
+ 											"Snapshot Context",
+ 											ALLOCSET_DEFAULT_MINSIZE,
+ 											ALLOCSET_DEFAULT_INITSIZE,
+ 											ALLOCSET_DEFAULT_MAXSIZE);
+ }
+ 
+ /*
   * CopySnapshot
   *		Copy the given snapshot.
   *
!  * The copy is palloc'd in SnapshotContext
   */
  Snapshot
  CopySnapshot(Snapshot snapshot)
*************** CopySnapshot(Snapshot snapshot)
*** 107,119 ****
  	Size		subxipoff;
  	Size		size;
  
  	/* We allocate any XID arrays needed in the same palloc block. */
  	size = subxipoff = sizeof(SnapshotData) +
  		snapshot->xcnt * sizeof(TransactionId);
  	if (snapshot->subxcnt > 0)
  		size += snapshot->subxcnt * sizeof(TransactionId);
  
! 	newsnap = (Snapshot) palloc(size);
  	memcpy(newsnap, snapshot, sizeof(SnapshotData));
  
  	/* setup XID array */
--- 131,148 ----
  	Size		subxipoff;
  	Size		size;
  
+ 	Assert(snapshot != InvalidSnapshot);
+ 
+ 	if (SnapshotContext == NULL)
+ 		SnapshotMemoryInit();
+ 
  	/* We allocate any XID arrays needed in the same palloc block. */
  	size = subxipoff = sizeof(SnapshotData) +
  		snapshot->xcnt * sizeof(TransactionId);
  	if (snapshot->subxcnt > 0)
  		size += snapshot->subxcnt * sizeof(TransactionId);
  
! 	newsnap = (Snapshot) MemoryContextAlloc(SnapshotContext, size);
  	memcpy(newsnap, snapshot, sizeof(SnapshotData));
  
  	/* setup XID array */
*************** FreeXactSnapshot(void)
*** 168,172 ****
  	 */
  	SerializableSnapshot = NULL;
  	LatestSnapshot = NULL;
! 	ActiveSnapshot = NULL;		/* just for cleanliness */
  }
--- 197,332 ----
  	 */
  	SerializableSnapshot = NULL;
  	LatestSnapshot = NULL;
! }
! 
! /*
!  * PushActiveSnapshot
!  * 		Set the given snapshot as the current active snapshot
!  */
! void
! PushActiveSnapshot(Snapshot snap)
! {
! 	ActiveSnapshotElt	*newactive;
! 
! 	Assert(snap != InvalidSnapshot);
! 
! 	if (SnapshotContext == NULL)
! 		SnapshotMemoryInit();
! 
! 	newactive = MemoryContextAlloc(SnapshotContext, sizeof(ActiveSnapshotElt));
! 	newactive->as_level = GetCurrentTransactionNestLevel();
! 	newactive->as_snap = CopySnapshot(snap);
! 	newactive->as_next = ActiveSnapshot;
! 
! 	ActiveSnapshot = newactive;
! }
! 
! /*
!  * PopActiveSnapshot
!  * 		Remove the topmost snapshot from the active snapshot stack
!  */
! void
! PopActiveSnapshot(void)
! {
! 	ActiveSnapshotElt	*newstack;
! 
! 	newstack = ActiveSnapshot->as_next;
! 
! 	pfree(ActiveSnapshot->as_snap);
! 	pfree(ActiveSnapshot);
! 
! 	ActiveSnapshot = newstack;
! }
! 
! Snapshot
! GetActiveSnapshot(void)
! {
! 	Assert(ActiveSnapshot != NULL);
! 
! 	return ActiveSnapshot->as_snap;
! }
! 
! bool
! ActiveSnapshotSet(void)
! {
! 	return ActiveSnapshot != NULL;
! }
! 
! /*
!  * Relabel the snapshots set in this subtransaction as though they are owned by
!  * the parent.
!  */
! void
! AtSubCommit_Snapshot(void)
! {
! 	ActiveSnapshotElt	*elt;
! 	int		level ;
! 
! 	if (ActiveSnapshot == NULL)
! 		return;
! 
! 	level = GetCurrentTransactionNestLevel();
! 
! 	for (elt = ActiveSnapshot; elt != NULL; elt = elt->as_next)
! 	{
! 		if (elt->as_level < level)
! 			break;
! 		elt->as_level = level - 1;
! 	}
! }
! 
! /*
!  * AtSubAbort_Snapshot
!  *		Forget the active snapshots set by this subtransaction.
!  */
! void
! AtSubAbort_Snapshot(void)
! {
! 	int		level;
! 
! 	if (ActiveSnapshot == NULL)
! 		return;
! 
! 	level = GetCurrentTransactionNestLevel();
! 
! 	while (ActiveSnapshot && ActiveSnapshot->as_level >= level)
! 	{
! 		ActiveSnapshotElt	*elt;
! 
! 		elt = ActiveSnapshot->as_next;
! 		pfree(ActiveSnapshot->as_snap);
! 		pfree(ActiveSnapshot);
! 		ActiveSnapshot = elt;
! 	}
! }
! 
! /*
!  * AtEOXact_Snapshot
!  * 		Snapshot manager's cleanup function for end of transaction
!  *
!  * We just need to reset our memory context -- the memory itself will be freed
!  * with TopTransactionContext.
!  */
! void
! AtEOXact_Snapshot(bool isCommit)
! {
! 	if (SnapshotContext == NULL)
! 		return;
! 
! 	/*
! 	 * On commit, complain about active snapshots that weren't popped from the
! 	 * stack.
! 	 */
! 	if (isCommit)
! 	{
! 		ActiveSnapshotElt	*elt;
! 
! 		for (elt = ActiveSnapshot; elt != NULL; elt = elt->as_next)
! 		{
! 			elog(WARNING, "snapshot %p still active", elt);
! 		}
! 	}
! 
! 	SnapshotContext = NULL;
! 	ActiveSnapshot = NULL;
  }
Index: src/include/utils/snapmgr.h
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/include/utils/snapmgr.h,v
retrieving revision 1.1
diff -c -p -r1.1 snapmgr.h
*** src/include/utils/snapmgr.h	26 Mar 2008 18:48:59 -0000	1.1
--- src/include/utils/snapmgr.h	8 Apr 2008 01:35:41 -0000
***************
*** 18,24 ****
  
  extern PGDLLIMPORT Snapshot SerializableSnapshot;
  extern PGDLLIMPORT Snapshot LatestSnapshot;
- extern PGDLLIMPORT Snapshot ActiveSnapshot;
  
  extern TransactionId TransactionXmin;
  extern TransactionId RecentXmin;
--- 18,23 ----
*************** extern Snapshot CopySnapshot(Snapshot sn
*** 30,33 ****
--- 29,40 ----
  extern void FreeSnapshot(Snapshot snapshot);
  extern void FreeXactSnapshot(void);
  
+ extern void PushActiveSnapshot(Snapshot snapshot);
+ extern void PopActiveSnapshot(void);
+ extern Snapshot GetActiveSnapshot(void);
+ extern bool ActiveSnapshotSet(void);
+ extern void AtSubCommit_Snapshot(void);
+ extern void AtSubAbort_Snapshot(void);
+ extern void AtEOXact_Snapshot(bool isCommit);
+ 
  #endif /* SNAPMGR_H */
Index: src/pl/plpgsql/src/pl_exec.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/pl/plpgsql/src/pl_exec.c,v
retrieving revision 1.209
diff -c -p -r1.209 pl_exec.c
*** src/pl/plpgsql/src/pl_exec.c	6 Apr 2008 23:43:29 -0000	1.209
--- src/pl/plpgsql/src/pl_exec.c	8 Apr 2008 01:34:35 -0000
*************** exec_eval_simple_expr(PLpgSQL_execstate 
*** 4164,4170 ****
  	CachedPlan *cplan;
  	ParamListInfo paramLI;
  	int			i;
! 	Snapshot	saveActiveSnapshot;
  
  	/*
  	 * Forget it if expression wasn't simple before.
--- 4164,4170 ----
  	CachedPlan *cplan;
  	ParamListInfo paramLI;
  	int			i;
! 	MemoryContext oldcontext;
  
  	/*
  	 * Forget it if expression wasn't simple before.
*************** exec_eval_simple_expr(PLpgSQL_execstate 
*** 4253,4269 ****
  	 * updates made so far by our own function.
  	 */
  	SPI_push();
- 	saveActiveSnapshot = ActiveSnapshot;
- 
- 	PG_TRY();
- 	{
- 		MemoryContext oldcontext;
  
  		oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
  		if (!estate->readonly_func)
  		{
  			CommandCounterIncrement();
! 			ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
  		}
  
  		/*
--- 4253,4264 ----
  	 * updates made so far by our own function.
  	 */
  	SPI_push();
  
  		oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
  		if (!estate->readonly_func)
  		{
  			CommandCounterIncrement();
! 			PushActiveSnapshot(GetTransactionSnapshot());
  		}
  
  		/*
*************** exec_eval_simple_expr(PLpgSQL_execstate 
*** 4274,4289 ****
  							   isNull,
  							   NULL);
  		MemoryContextSwitchTo(oldcontext);
- 	}
- 	PG_CATCH();
- 	{
- 		/* Restore global vars and propagate error */
- 		ActiveSnapshot = saveActiveSnapshot;
- 		PG_RE_THROW();
- 	}
- 	PG_END_TRY();
  
! 	ActiveSnapshot = saveActiveSnapshot;
  	SPI_pop();
  
  	/*
--- 4269,4278 ----
  							   isNull,
  							   NULL);
  		MemoryContextSwitchTo(oldcontext);
  
! 	if (!estate->readonly_func)
! 		PopActiveSnapshot();
! 
  	SPI_pop();
  
  	/*
