Index: src/backend/access/transam/twophase.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/access/transam/twophase.c,v
retrieving revision 1.36
diff -c -p -r1.36 twophase.c
*** src/backend/access/transam/twophase.c	21 Sep 2007 16:32:19 -0000	1.36
--- src/backend/access/transam/twophase.c	24 Oct 2007 14:59:47 -0000
*************** MarkAsPreparing(TransactionId xid, const
*** 283,290 ****
  	gxact->proc.databaseId = databaseid;
  	gxact->proc.roleId = owner;
  	gxact->proc.inCommit = false;
! 	gxact->proc.inVacuum = false;
! 	gxact->proc.isAutovacuum = false;
  	gxact->proc.lwWaiting = false;
  	gxact->proc.lwExclusive = false;
  	gxact->proc.lwWaitLink = NULL;
--- 283,289 ----
  	gxact->proc.databaseId = databaseid;
  	gxact->proc.roleId = owner;
  	gxact->proc.inCommit = false;
! 	gxact->proc.vacuumFlags = 0;
  	gxact->proc.lwWaiting = false;
  	gxact->proc.lwExclusive = false;
  	gxact->proc.lwWaitLink = NULL;
Index: src/backend/commands/analyze.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/analyze.c,v
retrieving revision 1.109
diff -c -p -r1.109 analyze.c
*** src/backend/commands/analyze.c	24 Sep 2007 03:12:23 -0000	1.109
--- src/backend/commands/analyze.c	24 Oct 2007 14:59:47 -0000
***************
*** 31,36 ****
--- 31,37 ----
  #include "parser/parse_relation.h"
  #include "pgstat.h"
  #include "postmaster/autovacuum.h"
+ #include "storage/proc.h"
  #include "utils/acl.h"
  #include "utils/datum.h"
  #include "utils/lsyscache.h"
*************** analyze_rel(Oid relid, VacuumStmt *vacst
*** 201,206 ****
--- 202,212 ----
  		return;
  	}
  
+ 	/* let others know what I'm doing */
+ 	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
+ 	MyProc->vacuumFlags |= PROC_IN_ANALYZE;
+ 	LWLockRelease(ProcArrayLock);
+ 
  	/* measure elapsed time iff autovacuum logging requires it */
  	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
  	{
*************** analyze_rel(Oid relid, VacuumStmt *vacst
*** 484,489 ****
--- 490,503 ----
  							RelationGetRelationName(onerel),
  							pg_rusage_show(&ru0))));
  	}
+ 
+ 	/*
+ 	 * Reset my PGPROC flag.  Note: we need this here, and not in vacuum_rel,
+ 	 * because the vacuum flag is cleared by the end-of-xact code.
+ 	 */
+ 	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
+ 	MyProc->vacuumFlags &= ~PROC_IN_ANALYZE;
+ 	LWLockRelease(ProcArrayLock);
  }
  
  /*
Index: src/backend/commands/vacuum.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/vacuum.c,v
retrieving revision 1.359
diff -c -p -r1.359 vacuum.c
*** src/backend/commands/vacuum.c	20 Sep 2007 17:56:31 -0000	1.359
--- src/backend/commands/vacuum.c	24 Oct 2007 14:59:47 -0000
*************** vacuum_set_xid_limits(int freeze_min_age
*** 660,668 ****
   *		fixed-size never-null columns, but these are.
   *
   *		Another reason for doing it this way is that when we are in a lazy
!  *		VACUUM and have inVacuum set, we mustn't do any updates --- somebody
!  *		vacuuming pg_class might think they could delete a tuple marked with
!  *		xmin = our xid.
   *
   *		This routine is shared by full VACUUM, lazy VACUUM, and stand-alone
   *		ANALYZE.
--- 660,668 ----
   *		fixed-size never-null columns, but these are.
   *
   *		Another reason for doing it this way is that when we are in a lazy
!  *		VACUUM and have PROC_IN_VACUUM set, we mustn't do any updates ---
!  *		somebody vacuuming pg_class might think they could delete a tuple
!  *		marked with xmin = our xid.
   *
   *		This routine is shared by full VACUUM, lazy VACUUM, and stand-alone
   *		ANALYZE.
*************** vacuum_rel(Oid relid, VacuumStmt *vacstm
*** 987,995 ****
  		 * During a lazy VACUUM we do not run any user-supplied functions, and
  		 * so it should be safe to not create a transaction snapshot.
  		 *
! 		 * We can furthermore set the inVacuum flag, which lets other
  		 * concurrent VACUUMs know that they can ignore this one while
! 		 * determining their OldestXmin.  (The reason we don't set inVacuum
  		 * during a full VACUUM is exactly that we may have to run user-
  		 * defined functions for functional indexes, and we want to make sure
  		 * that if they use the snapshot set above, any tuples it requires
--- 987,995 ----
  		 * During a lazy VACUUM we do not run any user-supplied functions, and
  		 * so it should be safe to not create a transaction snapshot.
  		 *
! 		 * We can furthermore set the PROC_IN_VACUUM flag, which lets other
  		 * concurrent VACUUMs know that they can ignore this one while
! 		 * determining their OldestXmin.  (The reason we don't set it
  		 * during a full VACUUM is exactly that we may have to run user-
  		 * defined functions for functional indexes, and we want to make sure
  		 * that if they use the snapshot set above, any tuples it requires
*************** vacuum_rel(Oid relid, VacuumStmt *vacstm
*** 997,1008 ****
  		 * depends on the contents of other tables is arguably broken, but we
  		 * won't break it here by violating transaction semantics.)
  		 *
! 		 * Note: the inVacuum flag remains set until CommitTransaction or
  		 * AbortTransaction.  We don't want to clear it until we reset
  		 * MyProc->xid/xmin, else OldestXmin might appear to go backwards,
  		 * which is probably Not Good.
  		 */
! 		MyProc->inVacuum = true;
  	}
  
  	/*
--- 997,1010 ----
  		 * depends on the contents of other tables is arguably broken, but we
  		 * won't break it here by violating transaction semantics.)
  		 *
! 		 * Note: this flag remains set until CommitTransaction or
  		 * AbortTransaction.  We don't want to clear it until we reset
  		 * MyProc->xid/xmin, else OldestXmin might appear to go backwards,
  		 * which is probably Not Good.
  		 */
! 		LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
! 		MyProc->vacuumFlags |= PROC_IN_VACUUM;
! 		LWLockRelease(ProcArrayLock);
  	}
  
  	/*
Index: src/backend/postmaster/autovacuum.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/postmaster/autovacuum.c,v
retrieving revision 1.61
diff -c -p -r1.61 autovacuum.c
*** src/backend/postmaster/autovacuum.c	24 Sep 2007 04:12:01 -0000	1.61
--- src/backend/postmaster/autovacuum.c	24 Oct 2007 14:59:47 -0000
*************** typedef struct autovac_table
*** 172,177 ****
--- 172,178 ----
  	int			at_freeze_min_age;
  	int			at_vacuum_cost_delay;
  	int			at_vacuum_cost_limit;
+ 	bool		at_wraparound;
  } autovac_table;
  
  /*-------------
*************** typedef struct autovac_table
*** 182,188 ****
   * wi_links		entry into free list or running list
   * wi_dboid		OID of the database this worker is supposed to work on
   * wi_tableoid	OID of the table currently being vacuumed
!  * wi_workerpid	PID of the running worker, 0 if not yet started
   * wi_launchtime Time at which this worker was launched
   * wi_cost_*	Vacuum cost-based delay parameters current in this worker
   *
--- 183,189 ----
   * wi_links		entry into free list or running list
   * wi_dboid		OID of the database this worker is supposed to work on
   * wi_tableoid	OID of the table currently being vacuumed
!  * wi_proc		pointer to PGPROC of the running worker, NULL if not started
   * wi_launchtime Time at which this worker was launched
   * wi_cost_*	Vacuum cost-based delay parameters current in this worker
   *
*************** typedef struct WorkerInfoData
*** 196,202 ****
  	SHM_QUEUE	wi_links;
  	Oid			wi_dboid;
  	Oid			wi_tableoid;
! 	int			wi_workerpid;
  	TimestampTz	wi_launchtime;
  	int			wi_cost_delay;
  	int			wi_cost_limit;
--- 197,203 ----
  	SHM_QUEUE	wi_links;
  	Oid			wi_dboid;
  	Oid			wi_tableoid;
! 	PGPROC	   *wi_proc;
  	TimestampTz	wi_launchtime;
  	int			wi_cost_delay;
  	int			wi_cost_limit;
*************** static autovac_table *table_recheck_auto
*** 280,286 ****
  static void relation_needs_vacanalyze(Oid relid, Form_pg_autovacuum avForm,
  						  Form_pg_class classForm,
  						  PgStat_StatTabEntry *tabentry, bool *dovacuum,
! 						  bool *doanalyze);
  
  static void autovacuum_do_vac_analyze(Oid relid, bool dovacuum,
  						  bool doanalyze, int freeze_min_age,
--- 281,287 ----
  static void relation_needs_vacanalyze(Oid relid, Form_pg_autovacuum avForm,
  						  Form_pg_class classForm,
  						  PgStat_StatTabEntry *tabentry, bool *dovacuum,
! 						  bool *doanalyze, bool *wraparound);
  
  static void autovacuum_do_vac_analyze(Oid relid, bool dovacuum,
  						  bool doanalyze, int freeze_min_age,
*************** AutoVacLauncherMain(int argc, char *argv
*** 694,700 ****
  					worker = (WorkerInfo) MAKE_PTR(AutoVacuumShmem->av_startingWorker);
  					worker->wi_dboid = InvalidOid;
  					worker->wi_tableoid = InvalidOid;
! 					worker->wi_workerpid = 0;
  					worker->wi_launchtime = 0;
  					worker->wi_links.next = AutoVacuumShmem->av_freeWorkers;
  					AutoVacuumShmem->av_freeWorkers = MAKE_OFFSET(worker);
--- 695,701 ----
  					worker = (WorkerInfo) MAKE_PTR(AutoVacuumShmem->av_startingWorker);
  					worker->wi_dboid = InvalidOid;
  					worker->wi_tableoid = InvalidOid;
! 					worker->wi_proc = NULL;
  					worker->wi_launchtime = 0;
  					worker->wi_links.next = AutoVacuumShmem->av_freeWorkers;
  					AutoVacuumShmem->av_freeWorkers = MAKE_OFFSET(worker);
*************** do_start_worker(void)
*** 1198,1204 ****
  		AutoVacuumShmem->av_freeWorkers = worker->wi_links.next;
  
  		worker->wi_dboid = avdb->adw_datid;
! 		worker->wi_workerpid = 0;
  		worker->wi_launchtime = GetCurrentTimestamp();
  
  		AutoVacuumShmem->av_startingWorker = sworker;
--- 1199,1205 ----
  		AutoVacuumShmem->av_freeWorkers = worker->wi_links.next;
  
  		worker->wi_dboid = avdb->adw_datid;
! 		worker->wi_proc = NULL;
  		worker->wi_launchtime = GetCurrentTimestamp();
  
  		AutoVacuumShmem->av_startingWorker = sworker;
*************** AutoVacWorkerMain(int argc, char *argv[]
*** 1542,1548 ****
  	{
  		MyWorkerInfo = (WorkerInfo) MAKE_PTR(AutoVacuumShmem->av_startingWorker);
  		dbid = MyWorkerInfo->wi_dboid;
! 		MyWorkerInfo->wi_workerpid = MyProcPid;
  
  		/* insert into the running list */
  		SHMQueueInsertBefore(&AutoVacuumShmem->av_runningWorkers, 
--- 1543,1549 ----
  	{
  		MyWorkerInfo = (WorkerInfo) MAKE_PTR(AutoVacuumShmem->av_startingWorker);
  		dbid = MyWorkerInfo->wi_dboid;
! 		MyWorkerInfo->wi_proc = MyProc;
  
  		/* insert into the running list */
  		SHMQueueInsertBefore(&AutoVacuumShmem->av_runningWorkers, 
*************** FreeWorkerInfo(int code, Datum arg)
*** 1637,1643 ****
  		MyWorkerInfo->wi_links.next = AutoVacuumShmem->av_freeWorkers;
  		MyWorkerInfo->wi_dboid = InvalidOid;
  		MyWorkerInfo->wi_tableoid = InvalidOid;
! 		MyWorkerInfo->wi_workerpid = 0;
  		MyWorkerInfo->wi_launchtime = 0;
  		MyWorkerInfo->wi_cost_delay = 0;
  		MyWorkerInfo->wi_cost_limit = 0;
--- 1638,1644 ----
  		MyWorkerInfo->wi_links.next = AutoVacuumShmem->av_freeWorkers;
  		MyWorkerInfo->wi_dboid = InvalidOid;
  		MyWorkerInfo->wi_tableoid = InvalidOid;
! 		MyWorkerInfo->wi_proc = NULL;
  		MyWorkerInfo->wi_launchtime = 0;
  		MyWorkerInfo->wi_cost_delay = 0;
  		MyWorkerInfo->wi_cost_limit = 0;
*************** autovac_balance_cost(void)
*** 1701,1707 ****
  									   offsetof(WorkerInfoData, wi_links));
  	while (worker)
  	{
! 		if (worker->wi_workerpid != 0 &&
  			worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0)
  			cost_total +=
  				(double) worker->wi_cost_limit_base / worker->wi_cost_delay;
--- 1702,1708 ----
  									   offsetof(WorkerInfoData, wi_links));
  	while (worker)
  	{
! 		if (worker->wi_proc != NULL &&
  			worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0)
  			cost_total +=
  				(double) worker->wi_cost_limit_base / worker->wi_cost_delay;
*************** autovac_balance_cost(void)
*** 1724,1730 ****
  									   offsetof(WorkerInfoData, wi_links));
  	while (worker)
  	{
! 		if (worker->wi_workerpid != 0 &&
  			worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0)
  		{
  			int     limit = (int)
--- 1725,1731 ----
  									   offsetof(WorkerInfoData, wi_links));
  	while (worker)
  	{
! 		if (worker->wi_proc != NULL &&
  			worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0)
  		{
  			int     limit = (int)
*************** autovac_balance_cost(void)
*** 1737,1743 ****
  			worker->wi_cost_limit = Max(Min(limit, worker->wi_cost_limit_base), 1);
  
  			elog(DEBUG2, "autovac_balance_cost(pid=%u db=%u, rel=%u, cost_limit=%d, cost_delay=%d)",
! 				 worker->wi_workerpid, worker->wi_dboid,
  				 worker->wi_tableoid, worker->wi_cost_limit, worker->wi_cost_delay);
  		}
  
--- 1738,1744 ----
  			worker->wi_cost_limit = Max(Min(limit, worker->wi_cost_limit_base), 1);
  
  			elog(DEBUG2, "autovac_balance_cost(pid=%u db=%u, rel=%u, cost_limit=%d, cost_delay=%d)",
! 				 worker->wi_proc->pid, worker->wi_dboid,
  				 worker->wi_tableoid, worker->wi_cost_limit, worker->wi_cost_delay);
  		}
  
*************** next_worker:
*** 2062,2086 ****
  		VacuumCostDelay = tab->at_vacuum_cost_delay;
  		VacuumCostLimit = tab->at_vacuum_cost_limit;
  
! 		/*
! 		 * Advertise my cost delay parameters for the balancing algorithm, and
! 		 * do a balance
! 		 */
  		LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
  		MyWorkerInfo->wi_cost_delay = tab->at_vacuum_cost_delay;
  		MyWorkerInfo->wi_cost_limit = tab->at_vacuum_cost_limit;
  		MyWorkerInfo->wi_cost_limit_base = tab->at_vacuum_cost_limit;
  		autovac_balance_cost();
  		LWLockRelease(AutovacuumLock);
  
  		/* clean up memory before each iteration */
  		MemoryContextResetAndDeleteChildren(PortalContext);
  
  		/*
! 		 * We will abort vacuuming the current table if we are interrupted, and
! 		 * continue with the next one in schedule; but if anything else
! 		 * happens, we will do our usual error handling which is to cause the
! 		 * worker process to exit.
  		 */
  		PG_TRY();
  		{
--- 2063,2097 ----
  		VacuumCostDelay = tab->at_vacuum_cost_delay;
  		VacuumCostLimit = tab->at_vacuum_cost_limit;
  
! 		/* Last fixups before actually starting to work */
  		LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
+ 
+ 		/* advertise my cost delay parameters for the balancing algorithm */
  		MyWorkerInfo->wi_cost_delay = tab->at_vacuum_cost_delay;
  		MyWorkerInfo->wi_cost_limit = tab->at_vacuum_cost_limit;
  		MyWorkerInfo->wi_cost_limit_base = tab->at_vacuum_cost_limit;
+ 
+ 		/* do a balance */
  		autovac_balance_cost();
+ 
+ 		/* done */
  		LWLockRelease(AutovacuumLock);
  
  		/* clean up memory before each iteration */
  		MemoryContextResetAndDeleteChildren(PortalContext);
  
+ 		/* set the "vacuum for wraparound" flag in PGPROC */
+ 		if (tab->at_wraparound)
+ 		{
+ 			LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
+ 			MyProc->vacuumFlags |= PROC_VACUUM_FOR_WRAPAROUND;
+ 			LWLockRelease(ProcArrayLock);
+ 		}
+ 
  		/*
! 		 * We will abort vacuuming the current table if something errors out,
! 		 * and continue with the next one in schedule; in particular, this
! 		 * happens if we are interrupted with SIGINT.
  		 */
  		PG_TRY();
  		{
*************** next_worker:
*** 2094,2132 ****
  		}
  		PG_CATCH();
  		{
- 			ErrorData	   *errdata;
- 
- 			MemoryContextSwitchTo(TopTransactionContext);
- 			errdata = CopyErrorData();
- 
  			/*
! 			 * If we errored out due to a cancel request, abort and restart the
! 			 * transaction and go to the next table.  Otherwise rethrow the
! 			 * error so that the outermost handler deals with it.
  			 */
! 			if (errdata->sqlerrcode == ERRCODE_QUERY_CANCELED)
! 			{
! 				HOLD_INTERRUPTS();
! 				elog(LOG, "cancelling autovacuum of table \"%s.%s.%s\"",
! 					 get_database_name(MyDatabaseId),
! 					 get_namespace_name(get_rel_namespace(tab->at_relid)),
! 					 get_rel_name(tab->at_relid));
! 
! 				AbortOutOfAnyTransaction();
! 				FlushErrorState();
! 				MemoryContextResetAndDeleteChildren(PortalContext);
! 
! 				/* restart our transaction for the following operations */
! 				StartTransactionCommand();
! 				RESUME_INTERRUPTS();
! 			}
  			else
! 				PG_RE_THROW();
  		}
  		PG_END_TRY();
  
  		/* be tidy */
  		pfree(tab);
  	}
  
  	/*
--- 2105,2153 ----
  		}
  		PG_CATCH();
  		{
  			/*
! 			 * Abort the transaction, start a new one, and proceed with the
! 			 * next table in our list.
  			 */
! 			HOLD_INTERRUPTS();
! 			if (tab->at_dovacuum)
! 				errcontext("automatic vacuum of table \"%s.%s.%s\"",
! 						   get_database_name(MyDatabaseId),
! 						   get_namespace_name(get_rel_namespace(tab->at_relid)),
! 						   get_rel_name(tab->at_relid));
  			else
! 				errcontext("automatic analyze of table \"%s.%s.%s\"",
! 						   get_database_name(MyDatabaseId),
! 						   get_namespace_name(get_rel_namespace(tab->at_relid)),
! 						   get_rel_name(tab->at_relid));
! 			EmitErrorReport();
! 
! 			/* this resets the PGPROC flags too */
! 			AbortOutOfAnyTransaction();
! 			FlushErrorState();
! 			MemoryContextResetAndDeleteChildren(PortalContext);
! 
! 			/* restart our transaction for the following operations */
! 			StartTransactionCommand();
! 			RESUME_INTERRUPTS();
  		}
  		PG_END_TRY();
  
+ 		/* reset my PGPROC flag */
+ 		if (tab->at_wraparound)
+ 		{
+ 			LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
+ 			MyProc->vacuumFlags &= ~PROC_VACUUM_FOR_WRAPAROUND;
+ 			LWLockRelease(ProcArrayLock);
+ 		}
+ 
  		/* be tidy */
  		pfree(tab);
+ 
+ 		/* remove my info from shared memory */
+ 		LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
+ 		MyWorkerInfo->wi_tableoid = InvalidOid;
+ 		LWLockRelease(AutovacuumLock);
  	}
  
  	/*
*************** relation_check_autovac(Oid relid, Form_p
*** 2214,2222 ****
  {
  	bool	dovacuum;
  	bool	doanalyze;
  
  	relation_needs_vacanalyze(relid, avForm, classForm, tabentry,
! 							  &dovacuum, &doanalyze);
  
  	if (classForm->relkind == RELKIND_TOASTVALUE)
  	{
--- 2235,2244 ----
  {
  	bool	dovacuum;
  	bool	doanalyze;
+ 	bool	dummy;
  
  	relation_needs_vacanalyze(relid, avForm, classForm, tabentry,
! 							  &dovacuum, &doanalyze, &dummy);
  
  	if (classForm->relkind == RELKIND_TOASTVALUE)
  	{
*************** table_recheck_autovac(Oid relid)
*** 2263,2268 ****
--- 2285,2292 ----
  	bool		doit = false;
  	PgStat_StatDBEntry *shared;
  	PgStat_StatDBEntry *dbentry;
+ 	bool		wraparound,
+ 				toast_wraparound = false;
  
  	/* use fresh stats */
  	autovac_refresh_stats();
*************** table_recheck_autovac(Oid relid)
*** 2289,2295 ****
  										 shared, dbentry);
  
  	relation_needs_vacanalyze(relid, avForm, classForm, tabentry,
! 							  &dovacuum, &doanalyze);
  
  	/* OK, it needs vacuum by itself */
  	if (dovacuum)
--- 2313,2319 ----
  										 shared, dbentry);
  
  	relation_needs_vacanalyze(relid, avForm, classForm, tabentry,
! 							  &dovacuum, &doanalyze, &wraparound);
  
  	/* OK, it needs vacuum by itself */
  	if (dovacuum)
*************** table_recheck_autovac(Oid relid)
*** 2307,2312 ****
--- 2331,2337 ----
  		{
  			bool			toast_dovacuum;
  			bool			toast_doanalyze;
+ 			bool			toast_wraparound;
  			Form_pg_class	toastClassForm;
  			PgStat_StatTabEntry *toasttabentry;
  
*************** table_recheck_autovac(Oid relid)
*** 2316,2324 ****
  													  shared, dbentry);
  
  			/* note we use the pg_autovacuum entry for the main table */
! 			relation_needs_vacanalyze(toastrelid, avForm, toastClassForm,
! 									  toasttabentry, &toast_dovacuum,
! 									  &toast_doanalyze);
  			/* we only consider VACUUM for toast tables */
  			if (toast_dovacuum)
  			{
--- 2341,2350 ----
  													  shared, dbentry);
  
  			/* note we use the pg_autovacuum entry for the main table */
! 			relation_needs_vacanalyze(toastrelid, avForm,
! 									  toastClassForm, toasttabentry,
! 									  &toast_dovacuum, &toast_doanalyze,
! 									  &toast_wraparound);
  			/* we only consider VACUUM for toast tables */
  			if (toast_dovacuum)
  			{
*************** table_recheck_autovac(Oid relid)
*** 2380,2385 ****
--- 2406,2412 ----
  		tab->at_freeze_min_age = freeze_min_age;
  		tab->at_vacuum_cost_limit = vac_cost_limit;
  		tab->at_vacuum_cost_delay = vac_cost_delay;
+ 		tab->at_wraparound = wraparound | toast_wraparound;
  	}
  
  	heap_close(avRel, AccessShareLock);
*************** table_recheck_autovac(Oid relid)
*** 2394,2400 ****
   * relation_needs_vacanalyze
   *
   * Check whether a relation needs to be vacuumed or analyzed; return each into
!  * "dovacuum" and "doanalyze", respectively.  avForm and tabentry can be NULL,
   * classForm shouldn't.
   *
   * A table needs to be vacuumed if the number of dead tuples exceeds a
--- 2421,2428 ----
   * relation_needs_vacanalyze
   *
   * Check whether a relation needs to be vacuumed or analyzed; return each into
!  * "dovacuum" and "doanalyze", respectively.  Also return whether the vacuum is
!  * being forced because of Xid wraparound.  avForm and tabentry can be NULL,
   * classForm shouldn't.
   *
   * A table needs to be vacuumed if the number of dead tuples exceeds a
*************** relation_needs_vacanalyze(Oid relid,
*** 2428,2434 ****
  						  PgStat_StatTabEntry *tabentry,
  						  /* output params below */
  						  bool *dovacuum,
! 						  bool *doanalyze)
  {
  	bool		force_vacuum;
  	float4		reltuples;		/* pg_class.reltuples */
--- 2456,2463 ----
  						  PgStat_StatTabEntry *tabentry,
  						  /* output params below */
  						  bool *dovacuum,
! 						  bool *doanalyze,
! 						  bool *wraparound)
  {
  	bool		force_vacuum;
  	float4		reltuples;		/* pg_class.reltuples */
*************** relation_needs_vacanalyze(Oid relid,
*** 2490,2495 ****
--- 2519,2525 ----
  	force_vacuum = (TransactionIdIsNormal(classForm->relfrozenxid) &&
  					TransactionIdPrecedes(classForm->relfrozenxid,
  										  xidForceLimit));
+ 	*wraparound = force_vacuum;
  
  	/* User disabled it in pg_autovacuum?  (But ignore if at risk) */
  	if (avForm && !avForm->enabled && !force_vacuum)
Index: src/backend/storage/ipc/procarray.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/storage/ipc/procarray.c,v
retrieving revision 1.35
diff -c -p -r1.35 procarray.c
*** src/backend/storage/ipc/procarray.c	23 Sep 2007 18:50:38 -0000	1.35
--- src/backend/storage/ipc/procarray.c	24 Oct 2007 14:59:47 -0000
*************** ProcArrayEndTransaction(PGPROC *proc, Tr
*** 242,248 ****
  		proc->xid = InvalidTransactionId;
  		proc->lxid = InvalidLocalTransactionId;
  		proc->xmin = InvalidTransactionId;
! 		proc->inVacuum = false;			/* must be cleared with xid/xmin */
  		proc->inCommit = false;			/* be sure this is cleared in abort */
  
  		/* Clear the subtransaction-XID cache too while holding the lock */
--- 242,249 ----
  		proc->xid = InvalidTransactionId;
  		proc->lxid = InvalidLocalTransactionId;
  		proc->xmin = InvalidTransactionId;
! 		/* must be cleared with xid/xmin: */
! 		proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
  		proc->inCommit = false;			/* be sure this is cleared in abort */
  
  		/* Clear the subtransaction-XID cache too while holding the lock */
*************** ProcArrayEndTransaction(PGPROC *proc, Tr
*** 267,273 ****
  
  		proc->lxid = InvalidLocalTransactionId;
  		proc->xmin = InvalidTransactionId;
! 		proc->inVacuum = false;			/* must be cleared with xid/xmin */
  		proc->inCommit = false;			/* be sure this is cleared in abort */
  
  		Assert(proc->subxids.nxids == 0);
--- 268,275 ----
  
  		proc->lxid = InvalidLocalTransactionId;
  		proc->xmin = InvalidTransactionId;
! 		/* must be cleared with xid/xmin: */
! 		proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
  		proc->inCommit = false;			/* be sure this is cleared in abort */
  
  		Assert(proc->subxids.nxids == 0);
*************** ProcArrayClearTransaction(PGPROC *proc)
*** 296,303 ****
  	proc->xid = InvalidTransactionId;
  	proc->lxid = InvalidLocalTransactionId;
  	proc->xmin = InvalidTransactionId;
! 	proc->inVacuum = false;			/* redundant, but just in case */
! 	proc->inCommit = false;			/* ditto */
  
  	/* Clear the subtransaction-XID cache too */
  	proc->subxids.nxids = 0;
--- 298,307 ----
  	proc->xid = InvalidTransactionId;
  	proc->lxid = InvalidLocalTransactionId;
  	proc->xmin = InvalidTransactionId;
! 
! 	/* redundant, but just in case */
! 	proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
! 	proc->inCommit = false;
  
  	/* Clear the subtransaction-XID cache too */
  	proc->subxids.nxids = 0;
*************** TransactionIdIsActive(TransactionId xid)
*** 546,552 ****
   * If allDbs is TRUE then all backends are considered; if allDbs is FALSE
   * then only backends running in my own database are considered.
   *
!  * If ignoreVacuum is TRUE then backends with inVacuum set are ignored.
   *
   * This is used by VACUUM to decide which deleted tuples must be preserved
   * in a table.	allDbs = TRUE is needed for shared relations, but allDbs =
--- 550,557 ----
   * If allDbs is TRUE then all backends are considered; if allDbs is FALSE
   * then only backends running in my own database are considered.
   *
!  * If ignoreVacuum is TRUE then backends with the PROC_IN_VACUUM flag set are
!  * ignored.
   *
   * This is used by VACUUM to decide which deleted tuples must be preserved
   * in a table.	allDbs = TRUE is needed for shared relations, but allDbs =
*************** GetOldestXmin(bool allDbs, bool ignoreVa
*** 586,592 ****
  	{
  		volatile PGPROC	   *proc = arrayP->procs[index];
  
! 		if (ignoreVacuum && proc->inVacuum)
  			continue;
  
  		if (allDbs || proc->databaseId == MyDatabaseId)
--- 591,597 ----
  	{
  		volatile PGPROC	   *proc = arrayP->procs[index];
  
! 		if (ignoreVacuum && (proc->vacuumFlags & PROC_IN_VACUUM))
  			continue;
  
  		if (allDbs || proc->databaseId == MyDatabaseId)
*************** GetSnapshotData(Snapshot snapshot, bool 
*** 723,729 ****
  		TransactionId xid;
  
  		/* Ignore procs running LAZY VACUUM */
! 		if (proc->inVacuum)
  			continue;
  
  		/* Update globalxmin to be the smallest valid xmin */
--- 728,734 ----
  		TransactionId xid;
  
  		/* Ignore procs running LAZY VACUUM */
! 		if (proc->vacuumFlags & PROC_IN_VACUUM)
  			continue;
  
  		/* Update globalxmin to be the smallest valid xmin */
*************** CheckOtherDBBackends(Oid databaseId)
*** 1193,1199 ****
  
  			found = true;
  
! 			if (proc->isAutovacuum)
  			{
  				/* an autovacuum --- send it SIGTERM before sleeping */
  				int		autopid = proc->pid;
--- 1198,1204 ----
  
  			found = true;
  
! 			if (proc->vacuumFlags & PROC_IS_AUTOVACUUM)
  			{
  				/* an autovacuum --- send it SIGTERM before sleeping */
  				int		autopid = proc->pid;
Index: src/backend/storage/lmgr/proc.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/storage/lmgr/proc.c,v
retrieving revision 1.194
diff -c -p -r1.194 proc.c
*** src/backend/storage/lmgr/proc.c	8 Sep 2007 20:31:15 -0000	1.194
--- src/backend/storage/lmgr/proc.c	24 Oct 2007 14:59:47 -0000
*************** InitProcess(void)
*** 291,298 ****
  	MyProc->databaseId = InvalidOid;
  	MyProc->roleId = InvalidOid;
  	MyProc->inCommit = false;
! 	MyProc->inVacuum = false;
! 	MyProc->isAutovacuum = IsAutoVacuumWorkerProcess();
  	MyProc->lwWaiting = false;
  	MyProc->lwExclusive = false;
  	MyProc->lwWaitLink = NULL;
--- 291,298 ----
  	MyProc->databaseId = InvalidOid;
  	MyProc->roleId = InvalidOid;
  	MyProc->inCommit = false;
! 	if (IsAutoVacuumWorkerProcess())
! 		MyProc->vacuumFlags |= PROC_IS_AUTOVACUUM;
  	MyProc->lwWaiting = false;
  	MyProc->lwExclusive = false;
  	MyProc->lwWaitLink = NULL;
*************** InitAuxiliaryProcess(void)
*** 429,436 ****
  	MyProc->databaseId = InvalidOid;
  	MyProc->roleId = InvalidOid;
  	MyProc->inCommit = false;
! 	MyProc->inVacuum = false;
! 	MyProc->isAutovacuum = IsAutoVacuumLauncherProcess(); /* is this needed? */
  	MyProc->lwWaiting = false;
  	MyProc->lwExclusive = false;
  	MyProc->lwWaitLink = NULL;
--- 429,436 ----
  	MyProc->databaseId = InvalidOid;
  	MyProc->roleId = InvalidOid;
  	MyProc->inCommit = false;
! 	/* we don't set the "is autovacuum" flag in the launcher */
! 	MyProc->vacuumFlags = 0;
  	MyProc->lwWaiting = false;
  	MyProc->lwExclusive = false;
  	MyProc->lwWaitLink = NULL;
Index: src/include/storage/proc.h
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/include/storage/proc.h,v
retrieving revision 1.100
diff -c -p -r1.100 proc.h
*** src/include/storage/proc.h	5 Sep 2007 18:10:48 -0000	1.100
--- src/include/storage/proc.h	24 Oct 2007 14:59:47 -0000
*************** struct XidCache
*** 38,43 ****
--- 38,52 ----
  	TransactionId xids[PGPROC_MAX_CACHED_SUBXIDS];
  };
  
+ /* Flags for PGPROC->vacuumFlags */
+ #define		PROC_IS_AUTOVACUUM	0x01	/* is it an autovac worker? */
+ #define		PROC_IN_VACUUM		0x02	/* currently running lazy vacuum */
+ #define		PROC_IN_ANALYZE		0x04	/* currently running analyze */
+ #define		PROC_VACUUM_FOR_WRAPAROUND 0x08 /* set by autovac only */
+ 
+ /* flags reset at EOXact */
+ #define		PROC_VACUUM_STATE_MASK (0x0E)
+ 
  /*
   * Each backend has a PGPROC struct in shared memory.  There is also a list of
   * currently-unused PGPROC structs that will be reallocated to new backends.
*************** struct PGPROC
*** 82,93 ****
  
  	bool		inCommit;		/* true if within commit critical section */
  
- 	bool		inVacuum;		/* true if current xact is a LAZY VACUUM */
- 	bool		isAutovacuum;	/* true if it's autovacuum */
- 
  	/* Info about LWLock the process is currently waiting for, if any. */
  	bool		lwWaiting;		/* true if waiting for an LW lock */
  	bool		lwExclusive;	/* true if waiting for exclusive access */
  	struct PGPROC *lwWaitLink;	/* next waiter for same LW lock */
  
  	/* Info about lock the process is currently waiting for, if any. */
--- 91,101 ----
  
  	bool		inCommit;		/* true if within commit critical section */
  
  	/* Info about LWLock the process is currently waiting for, if any. */
  	bool		lwWaiting;		/* true if waiting for an LW lock */
  	bool		lwExclusive;	/* true if waiting for exclusive access */
+ 
+ 	uint8		vacuumFlags;	/* vacuum-related flags, see above */
  	struct PGPROC *lwWaitLink;	/* next waiter for same LW lock */
  
  	/* Info about lock the process is currently waiting for, if any. */
