Index: src/backend/postmaster/autovacuum.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/postmaster/autovacuum.c,v
retrieving revision 1.39
diff -c -p -r1.39 autovacuum.c
*** src/backend/postmaster/autovacuum.c	27 Mar 2007 20:36:03 -0000	1.39
--- src/backend/postmaster/autovacuum.c	27 Mar 2007 20:43:31 -0000
*************** typedef struct autovac_dbase
*** 91,96 ****
--- 91,103 ----
  	PgStat_StatDBEntry *ad_entry;
  } autovac_dbase;
  
+ /* struct to keep track of tables to vacuum and/or analyze, in 1st pass */
+ typedef struct av_relation
+ {
+ 	Oid		ar_relid;
+ 	Oid		ar_toastrelid;
+ } av_relation;
+ 
  /* struct to keep track of tables to vacuum and/or analyze, after rechecking */
  typedef struct autovac_table
  {
*************** NON_EXEC_STATIC void AutoVacLauncherMain
*** 121,137 ****
  static void do_start_worker(void);
  static void do_autovacuum(Oid dbid);
  static List *autovac_get_database_list(void);
! static void test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry,
! 					 Form_pg_class classForm,
! 					 Form_pg_autovacuum avForm,
! 					 List **vacuum_tables,
! 					 List **toast_table_ids);
  static void autovacuum_do_vac_analyze(Oid relid, bool dovacuum,
  						  bool doanalyze, int freeze_min_age);
  static HeapTuple get_pg_autovacuum_tuple_relid(Relation avRel, Oid relid);
  static PgStat_StatTabEntry *get_pgstat_tabentry_relid(Oid relid, bool isshared,
  						  PgStat_StatDBEntry *shared,
  						  PgStat_StatDBEntry *dbentry);
  static void autovac_report_activity(VacuumStmt *vacstmt, Oid relid);
  static void avl_sighup_handler(SIGNAL_ARGS);
  static void avlauncher_shutdown(SIGNAL_ARGS);
--- 128,152 ----
  static void do_start_worker(void);
  static void do_autovacuum(Oid dbid);
  static List *autovac_get_database_list(void);
! 
! static void relation_check_autovac(Oid relid, Form_pg_class classForm,
! 					   Form_pg_autovacuum avForm, PgStat_StatTabEntry *tabentry,
! 					   List **table_oids, List **table_toast_list,
! 					   List **toast_oids);
! static autovac_table *table_recheck_autovac(Oid relid,
! 											PgStat_StatDBEntry *shared,
! 											PgStat_StatDBEntry *dbentry);
  static void autovacuum_do_vac_analyze(Oid relid, bool dovacuum,
  						  bool doanalyze, int freeze_min_age);
  static HeapTuple get_pg_autovacuum_tuple_relid(Relation avRel, Oid relid);
  static PgStat_StatTabEntry *get_pgstat_tabentry_relid(Oid relid, bool isshared,
  						  PgStat_StatDBEntry *shared,
  						  PgStat_StatDBEntry *dbentry);
+ static void relation_needs_vacanalyze(Oid relid, Form_pg_autovacuum avForm,
+ 						  Form_pg_class classForm,
+ 						  PgStat_StatTabEntry *tabentry, bool *dovacuum,
+ 						  bool *doanalyze);
+ static HeapTuple get_pg_autovacuum_tuple_relid(Relation avRel, Oid relid);
  static void autovac_report_activity(VacuumStmt *vacstmt, Oid relid);
  static void avl_sighup_handler(SIGNAL_ARGS);
  static void avlauncher_shutdown(SIGNAL_ARGS);
*************** do_autovacuum(Oid dbid)
*** 872,879 ****
  	HeapTuple	tuple;
  	HeapScanDesc relScan;
  	Form_pg_database dbForm;
! 	List	   *vacuum_tables = NIL;
! 	List	   *toast_table_ids = NIL;
  	ListCell   *cell;
  	PgStat_StatDBEntry *shared;
  	PgStat_StatDBEntry *dbentry;
--- 887,895 ----
  	HeapTuple	tuple;
  	HeapScanDesc relScan;
  	Form_pg_database dbForm;
! 	List	   *table_oids = NIL;
! 	List	   *toast_oids = NIL;
! 	List	   *table_toast_list = NIL;
  	ListCell   *cell;
  	PgStat_StatDBEntry *shared;
  	PgStat_StatDBEntry *dbentry;
*************** do_autovacuum(Oid dbid)
*** 979,986 ****
  		tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared,
  											 shared, dbentry);
  
! 		test_rel_for_autovac(relid, tabentry, classForm, avForm,
! 							 &vacuum_tables, &toast_table_ids);
  
  		if (HeapTupleIsValid(avTup))
  			heap_freetuple(avTup);
--- 995,1002 ----
  		tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared,
  											 shared, dbentry);
  
! 		relation_check_autovac(relid, classForm, avForm, tabentry,
! 							   &table_oids, &table_toast_list, &toast_oids);
  
  		if (HeapTupleIsValid(avTup))
  			heap_freetuple(avTup);
*************** do_autovacuum(Oid dbid)
*** 990,1028 ****
  	heap_close(avRel, AccessShareLock);
  	heap_close(classRel, AccessShareLock);
  
! 	/*
! 	 * Perform operations on collected tables.
! 	 */
! 	foreach(cell, vacuum_tables)
  	{
! 		autovac_table *tab = lfirst(cell);
  
  		CHECK_FOR_INTERRUPTS();
  
  		/*
! 		 * Check to see if we need to force vacuuming of this table because
! 		 * its toast table needs it.
  		 */
! 		if (OidIsValid(tab->at_toastrelid) && !tab->at_dovacuum &&
! 			list_member_oid(toast_table_ids, tab->at_toastrelid))
  		{
! 			tab->at_dovacuum = true;
! 			elog(DEBUG2, "autovac: VACUUM %u because of TOAST table",
! 				 tab->at_relid);
! 		}
! 
! 		/* Otherwise, ignore table if it needs no work */
! 		if (!tab->at_dovacuum && !tab->at_doanalyze)
  			continue;
  
  		/* Set the vacuum cost parameters for this table */
  		VacuumCostDelay = tab->at_vacuum_cost_delay;
  		VacuumCostLimit = tab->at_vacuum_cost_limit;
  
  		autovacuum_do_vac_analyze(tab->at_relid,
  								  tab->at_dovacuum,
  								  tab->at_doanalyze,
  								  tab->at_freeze_min_age);
  	}
  
  	/*
--- 1006,1070 ----
  	heap_close(avRel, AccessShareLock);
  	heap_close(classRel, AccessShareLock);
  
! 	/* mix the TOAST table oids into the plain table OID list */
! 	if (list_length(toast_oids) > 0)
  	{
! 		foreach(cell, table_toast_list)
! 		{
! 			av_relation *ar = lfirst(cell);
! 
! 			if (list_member_oid(toast_oids, ar->ar_toastrelid))
! 				table_oids = lappend_oid(table_oids, ar->ar_relid);
! 		}
! 	}
! 	list_free_deep(table_toast_list);
! 	table_toast_list = NIL;
! 	list_free(toast_oids);
! 	toast_oids = NIL;
! 
! 	/* Perform operations on collected tables. */
! 	foreach(cell, table_oids)
! 	{
! 		Oid		relid = lfirst_oid(cell);
! 		autovac_table *tab;
  
  		CHECK_FOR_INTERRUPTS();
  
  		/*
! 		 * Check whether pgstat data still says we need to vacuum this table.
! 		 * It could have changed if other worker processed the table while we
! 		 * weren't looking; and pg_autovacuum parameters could have changed
! 		 * as well.
! 		 *
! 		 * We need fresh pgstat data for this.
! 		 *
! 		 * FIXME we ignore the possibility that the table was finished being
! 		 * vacuumed in the last 500ms (PGSTAT_STAT_INTERVAL).  This is a bug.
  		 */
! 		pgstat_clear_snapshot();
! 		tab = table_recheck_autovac(relid, shared, dbentry);
! 		if (tab == NULL)
  		{
! 			/* someone else vacuumed the table */
  			continue;
+ 		}
+ 		/* Ok, good to go! */
  
  		/* Set the vacuum cost parameters for this table */
  		VacuumCostDelay = tab->at_vacuum_cost_delay;
  		VacuumCostLimit = tab->at_vacuum_cost_limit;
  
+ 		elog(DEBUG2, "autovac: will%s%s %s",
+ 			 (tab->at_dovacuum ? " VACUUM" : ""),
+ 			 (tab->at_doanalyze ? " ANALYZE" : ""),
+ 			 get_rel_name(relid));
+ 
  		autovacuum_do_vac_analyze(tab->at_relid,
  								  tab->at_dovacuum,
  								  tab->at_doanalyze,
  								  tab->at_freeze_min_age);
+ 		/* be tidy */
+ 		pfree(tab);
  	}
  
  	/*
*************** get_pgstat_tabentry_relid(Oid relid, boo
*** 1089,1098 ****
  }
  
  /*
!  * test_rel_for_autovac
   *
!  * Check whether a table needs to be vacuumed or analyzed.	Add it to the
!  * appropriate output list if so.
   *
   * A table needs to be vacuumed if the number of dead tuples exceeds a
   * threshold.  This threshold is calculated as
--- 1131,1329 ----
  }
  
  /*
!  * relation_check_autovac
!  *
!  * For a given relation (either a plain table or TOAST table), check whether it
!  * needs vacuum or analyze.
!  *
!  * Plain tables that need either are added to the table_list.  TOAST tables
!  * that need vacuum are added to toast_list.  Plain tables that don't need
!  * either but which have a TOAST table are added, as a struct, to
!  * table_toast_list.  The latter is to allow appending the OIDs of the plain
!  * tables whose TOAST table needs vacuuming into the plain tables list, which
!  * allows us to substantially reduce the number of "rechecks" that we need to
!  * do later on.
!  */
! static void
! relation_check_autovac(Oid relid, Form_pg_class classForm,
! 					   Form_pg_autovacuum avForm, PgStat_StatTabEntry *tabentry,
! 					   List **table_oids, List **table_toast_list,
! 					   List **toast_oids)
! {
! 	bool	dovacuum;
! 	bool	doanalyze;
! 
! 	relation_needs_vacanalyze(relid, avForm, classForm, tabentry,
! 							  &dovacuum, &doanalyze);
! 
! 	if (classForm->relkind == RELKIND_TOASTVALUE)
! 	{
! 		if (dovacuum)
! 			*toast_oids = lappend_oid(*toast_oids, relid);
! 	}
! 	else
! 	{
! 		Assert(classForm->relkind == RELKIND_RELATION);
! 
! 		if (dovacuum || doanalyze)
! 			*table_oids = lappend_oid(*table_oids, relid);
! 		else if (OidIsValid(classForm->reltoastrelid))
! 		{
! 			av_relation	   *rel = palloc(sizeof(av_relation));
! 
! 			rel->ar_relid = relid;
! 			rel->ar_toastrelid = classForm->reltoastrelid;
! 
! 			*table_toast_list = lappend(*table_toast_list, rel);
! 		}
! 	}
! }
! 
! /*
!  * table_recheck_autovac
!  *
!  * Recheck whether a plain table still needs vacuum or analyze; be it because
!  * it does directly, or because its TOAST table does.  Return value is a valid
!  * autovac_table pointer if it does, NULL otherwise.
!  */
! static autovac_table *
! table_recheck_autovac(Oid relid, PgStat_StatDBEntry *shared, PgStat_StatDBEntry *dbentry)
! {
! 	Form_pg_autovacuum avForm = NULL;
! 	Form_pg_class classForm;
! 	HeapTuple	classTup;
! 	HeapTuple	avTup;
! 	Relation	avRel;
! 	bool		dovacuum;
! 	bool		doanalyze;
! 	autovac_table *tab = NULL;
! 	PgStat_StatTabEntry *tabentry;
! 	bool		doit = false;
! 
! 	/* fetch the relation's relcache entry */
! 	classTup = SearchSysCacheCopy(RELOID,
! 							  ObjectIdGetDatum(relid),
! 							  0, 0, 0);
! 	if (!HeapTupleIsValid(classTup))
! 		return NULL;
! 	classForm = (Form_pg_class) GETSTRUCT(classTup);
! 
! 	/* fetch the pg_autovacuum entry, if any */
! 	avRel = heap_open(AutovacuumRelationId, AccessShareLock);
! 	avTup = get_pg_autovacuum_tuple_relid(avRel, relid);
! 	if (HeapTupleIsValid(avTup))
! 		avForm = (Form_pg_autovacuum) GETSTRUCT(avTup);
! 
! 	/* fetch the pgstat table entry */
! 	tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared,
! 										 shared, dbentry);
! 
! 	relation_needs_vacanalyze(relid, avForm, classForm, tabentry,
! 							  &dovacuum, &doanalyze);
! 
! 	/* OK, it needs vacuum by itself */
! 	if (dovacuum)
! 		doit = true;
! 	/* it doesn't need vacuum, but what about it's TOAST table? */
! 	else if (OidIsValid(classForm->reltoastrelid))
! 	{
! 		Oid		toastrelid = classForm->reltoastrelid;
! 		HeapTuple	toastClassTup;
! 
! 		toastClassTup = SearchSysCacheCopy(RELOID,
! 										   ObjectIdGetDatum(toastrelid),
! 										   0, 0, 0);
! 		if (HeapTupleIsValid(toastClassTup))
! 		{
! 			bool			toast_dovacuum;
! 			bool			toast_doanalyze;
! 			Form_pg_class	toastClassForm;
! 			PgStat_StatTabEntry *toasttabentry;
! 
! 			toastClassForm = (Form_pg_class) GETSTRUCT(toastClassTup);
! 			toasttabentry = get_pgstat_tabentry_relid(toastrelid,
! 													  toastClassForm->relisshared,
! 													  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)
! 			{
! 				dovacuum = true;
! 				doit = true;
! 			}
! 
! 			heap_freetuple(toastClassTup);
! 		}
! 	}
! 
! 	if (doanalyze)
! 		doit = true;
! 
! 	if (doit)
! 	{
! 		int			freeze_min_age;
! 		int			vac_cost_limit;
! 		int			vac_cost_delay;
! 
! 		/*
! 		 * Calculate the vacuum cost parameters and the minimum freeze age.  If
! 		 * there is a tuple in pg_autovacuum, use it; else, use the GUC
! 		 * defaults.  Note that the fields may contain "-1" (or indeed any
! 		 * negative value), which means use the GUC defaults for each setting.
! 		 */
! 		if (avForm != NULL)
! 		{
! 			vac_cost_limit = (avForm->vac_cost_limit >= 0) ?
! 				avForm->vac_cost_limit :
! 				((autovacuum_vac_cost_limit >= 0) ?
! 				 autovacuum_vac_cost_limit : VacuumCostLimit);
! 
! 			vac_cost_delay = (avForm->vac_cost_delay >= 0) ?
! 				avForm->vac_cost_delay :
! 				((autovacuum_vac_cost_delay >= 0) ?
! 				 autovacuum_vac_cost_delay : VacuumCostDelay);
! 
! 			freeze_min_age = (avForm->freeze_min_age >= 0) ?
! 				avForm->freeze_min_age : default_freeze_min_age;
! 		}
! 		else
! 		{
! 			vac_cost_limit = (autovacuum_vac_cost_limit >= 0) ?
! 				autovacuum_vac_cost_limit : VacuumCostLimit;
! 
! 			vac_cost_delay = (autovacuum_vac_cost_delay >= 0) ?
! 				autovacuum_vac_cost_delay : VacuumCostDelay;
! 
! 			freeze_min_age = default_freeze_min_age;
! 		}
! 
! 		tab = palloc(sizeof(autovac_table));
! 		tab->at_relid = relid;
! 		tab->at_dovacuum = dovacuum;
! 		tab->at_doanalyze = doanalyze;
! 		tab->at_freeze_min_age = freeze_min_age;
! 		tab->at_vacuum_cost_limit = vac_cost_limit;
! 		tab->at_vacuum_cost_delay = vac_cost_delay;
! 	}
! 
! 	heap_close(avRel, AccessShareLock);
! 	if (HeapTupleIsValid(avTup))
! 		heap_freetuple(avTup);
! 	heap_freetuple(classTup);
! 
! 	return tab;
! }
! 
! /*
!  * 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
   * threshold.  This threshold is calculated as
*************** get_pgstat_tabentry_relid(Oid relid, boo
*** 1119,1134 ****
   * autovacuum_vacuum_scale_factor GUC variable.  Ditto for analyze.
   */
  static void
! test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry,
! 					 Form_pg_class classForm,
! 					 Form_pg_autovacuum avForm,
! 					 List **vacuum_tables,
! 					 List **toast_table_ids)
  {
- 	bool		force_vacuum;
- 	bool		dovacuum;
- 	bool		doanalyze;
  	float4		reltuples;		/* pg_class.reltuples */
  	/* constants from pg_autovacuum or GUC variables */
  	int			vac_base_thresh,
  				anl_base_thresh;
--- 1350,1365 ----
   * autovacuum_vacuum_scale_factor GUC variable.  Ditto for analyze.
   */
  static void
! relation_needs_vacanalyze(Oid relid,
! 						  Form_pg_autovacuum avForm,
! 						  Form_pg_class classForm,
! 						  PgStat_StatTabEntry *tabentry,
! 						  /* output params below */
! 						  bool *dovacuum,
! 						  bool *doanalyze)
  {
  	float4		reltuples;		/* pg_class.reltuples */
+ 	bool		force_vacuum;
  	/* constants from pg_autovacuum or GUC variables */
  	int			vac_base_thresh,
  				anl_base_thresh;
*************** test_rel_for_autovac(Oid relid, PgStat_S
*** 1140,1157 ****
  	/* number of vacuum (resp. analyze) tuples at this time */
  	float4		vactuples,
  				anltuples;
- 	/* freeze parameters */
- 	int			freeze_min_age;
- 	int			freeze_max_age;
  	TransactionId xidForceLimit;
! 	/* cost-based vacuum delay parameters */
! 	int			vac_cost_limit;
! 	int			vac_cost_delay;
  
  	/*
! 	 * If there is a tuple in pg_autovacuum, use it; else, use the GUC
! 	 * defaults.  Note that the fields may contain "-1" (or indeed any
! 	 * negative value), which means use the GUC defaults for each setting.
  	 */
  	if (avForm != NULL)
  	{
--- 1371,1387 ----
  	/* number of vacuum (resp. analyze) tuples at this time */
  	float4		vactuples,
  				anltuples;
  	TransactionId xidForceLimit;
! 	int			freeze_max_age;
! 
! 	AssertArg(classForm != NULL);
! 	AssertArg(OidIsValid(relid));
  
  	/*
! 	 * Determine vacuum/analyze equation parameters.  If there is a tuple in
! 	 * pg_autovacuum, use it; else, use the GUC defaults.  Note that the fields
! 	 * may contain "-1" (or indeed any negative value), which means use the GUC
! 	 * defaults for each setting.
  	 */
  	if (avForm != NULL)
  	{
*************** test_rel_for_autovac(Oid relid, PgStat_S
*** 1165,1185 ****
  		anl_base_thresh = (avForm->anl_base_thresh >= 0) ?
  			avForm->anl_base_thresh : autovacuum_anl_thresh;
  
- 		freeze_min_age = (avForm->freeze_min_age >= 0) ?
- 			avForm->freeze_min_age : default_freeze_min_age;
  		freeze_max_age = (avForm->freeze_max_age >= 0) ?
  			Min(avForm->freeze_max_age, autovacuum_freeze_max_age) :
  			autovacuum_freeze_max_age;
- 
- 		vac_cost_limit = (avForm->vac_cost_limit >= 0) ?
- 			avForm->vac_cost_limit :
- 			((autovacuum_vac_cost_limit >= 0) ?
- 			 autovacuum_vac_cost_limit : VacuumCostLimit);
- 
- 		vac_cost_delay = (avForm->vac_cost_delay >= 0) ?
- 			avForm->vac_cost_delay :
- 			((autovacuum_vac_cost_delay >= 0) ?
- 			 autovacuum_vac_cost_delay : VacuumCostDelay);
  	}
  	else
  	{
--- 1395,1403 ----
*************** test_rel_for_autovac(Oid relid, PgStat_S
*** 1189,1202 ****
  		anl_scale_factor = autovacuum_anl_scale;
  		anl_base_thresh = autovacuum_anl_thresh;
  
- 		freeze_min_age = default_freeze_min_age;
  		freeze_max_age = autovacuum_freeze_max_age;
- 
- 		vac_cost_limit = (autovacuum_vac_cost_limit >= 0) ?
- 			autovacuum_vac_cost_limit : VacuumCostLimit;
- 
- 		vac_cost_delay = (autovacuum_vac_cost_delay >= 0) ?
- 			autovacuum_vac_cost_delay : VacuumCostDelay;
  	}
  
  	/* Force vacuum if table is at risk of wraparound */
--- 1407,1413 ----
*************** test_rel_for_autovac(Oid relid, PgStat_S
*** 1209,1290 ****
  
  	/* User disabled it in pg_autovacuum?  (But ignore if at risk) */
  	if (avForm && !avForm->enabled && !force_vacuum)
- 		return;
- 
- 	if (PointerIsValid(tabentry))
  	{
! 		reltuples = classForm->reltuples;
! 		vactuples = tabentry->n_dead_tuples;
! 		anltuples = tabentry->n_live_tuples + tabentry->n_dead_tuples -
! 			tabentry->last_anl_tuples;
! 
! 		vacthresh = (float4) vac_base_thresh + vac_scale_factor * reltuples;
! 		anlthresh = (float4) anl_base_thresh + anl_scale_factor * reltuples;
! 
! 		/*
! 		 * Note that we don't need to take special consideration for stat
! 		 * reset, because if that happens, the last vacuum and analyze counts
! 		 * will be reset too.
! 		 */
! 		elog(DEBUG3, "%s: vac: %.0f (threshold %.0f), anl: %.0f (threshold %.0f)",
! 			 NameStr(classForm->relname),
! 			 vactuples, vacthresh, anltuples, anlthresh);
! 
! 		/* Determine if this table needs vacuum or analyze. */
! 		dovacuum = force_vacuum || (vactuples > vacthresh);
! 		doanalyze = (anltuples > anlthresh);
  	}
  	else
  	{
! 		/*
! 		 * Skip a table not found in stat hash, unless we have to force
! 		 * vacuum for anti-wrap purposes.  If it's not acted upon, there's
! 		 * no need to vacuum it.
! 		 */
! 		dovacuum = force_vacuum;
! 		doanalyze = false;
  	}
  
  	/* ANALYZE refuses to work with pg_statistics */
  	if (relid == StatisticRelationId)
! 		doanalyze = false;
! 
! 	Assert(CurrentMemoryContext == AutovacMemCxt);
! 
! 	if (classForm->relkind == RELKIND_RELATION)
! 	{
! 		if (dovacuum || doanalyze)
! 			elog(DEBUG2, "autovac: will%s%s %s",
! 				 (dovacuum ? " VACUUM" : ""),
! 				 (doanalyze ? " ANALYZE" : ""),
! 				 NameStr(classForm->relname));
! 
! 		/*
! 		 * we must record tables that have a toast table, even if we currently
! 		 * don't think they need vacuuming.
! 		 */
! 		if (dovacuum || doanalyze || OidIsValid(classForm->reltoastrelid))
! 		{
! 			autovac_table *tab;
! 
! 			tab = (autovac_table *) palloc(sizeof(autovac_table));
! 			tab->at_relid = relid;
! 			tab->at_toastrelid = classForm->reltoastrelid;
! 			tab->at_dovacuum = dovacuum;
! 			tab->at_doanalyze = doanalyze;
! 			tab->at_freeze_min_age = freeze_min_age;
! 			tab->at_vacuum_cost_limit = vac_cost_limit;
! 			tab->at_vacuum_cost_delay = vac_cost_delay;
! 
! 			*vacuum_tables = lappend(*vacuum_tables, tab);
! 		}
! 	}
! 	else
! 	{
! 		Assert(classForm->relkind == RELKIND_TOASTVALUE);
! 		if (dovacuum)
! 			*toast_table_ids = lappend_oid(*toast_table_ids, relid);
! 	}
  }
  
  /*
--- 1420,1471 ----
  
  	/* User disabled it in pg_autovacuum?  (But ignore if at risk) */
  	if (avForm && !avForm->enabled && !force_vacuum)
  	{
! 		*doanalyze = false;
! 		*dovacuum = false;
! 		return;
  	}
  	else
  	{
! 		if (PointerIsValid(tabentry))
! 		{
! 			reltuples = classForm->reltuples;
! 			vactuples = tabentry->n_dead_tuples;
! 			anltuples = tabentry->n_live_tuples + tabentry->n_dead_tuples -
! 				tabentry->last_anl_tuples;
! 
! 			vacthresh = (float4) vac_base_thresh + vac_scale_factor * reltuples;
! 			anlthresh = (float4) anl_base_thresh + anl_scale_factor * reltuples;
! 
! 			/*
! 			 * Note that we don't need to take special consideration for stat
! 			 * reset, because if that happens, the last vacuum and analyze
! 			 * counts will be reset too.
! 			 */
! 			elog(DEBUG3,
! 				 "%s: vac: %.0f (threshold %.0f), anl: %.0f (threshold %.0f)",
! 				 NameStr(classForm->relname),
! 				 vactuples, vacthresh, anltuples, anlthresh);
! 
! 			/* Determine if this table needs vacuum or analyze. */
! 			*dovacuum = force_vacuum || (vactuples > vacthresh);
! 			*doanalyze = (anltuples > anlthresh);
! 		}
! 		else
! 		{
! 			/*
! 			 * Skip a table not found in stat hash, unless we have to force
! 			 * vacuum for anti-wrap purposes.  If it's not acted upon, there's
! 			 * no need to vacuum it.
! 			 */
! 			*dovacuum = force_vacuum;
! 			*doanalyze = false;
! 		}
  	}
  
  	/* ANALYZE refuses to work with pg_statistics */
  	if (relid == StatisticRelationId)
! 		*doanalyze = false;
  }
  
  /*
