Tom Lane wrote:
> Alvaro Herrera <[EMAIL PROTECTED]> writes:
> > There's two things I'm not happy about in this patch:
> 
> > 1. it uses a List to keep the mapping of heap<->toast Oids.  This is
> > needed to be able to fetch the main rel's pg_autovacuum entry to process
> > the toast table.  This incurs in O(n^2) behavior.
> 
> Use a dynahash table instead?

Right, the attached patch does that.

Note that this patch allows a toast table to be vacuumed by the user:

alvherre=# vacuum pg_toast.pg_toast_39970;
VACUUM

I don't have a problem with that, but if anyone thinks this is not a
good idea, please speak up.

-- 
Alvaro Herrera                                http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
Index: src/backend/commands/vacuum.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/vacuum.c,v
retrieving revision 1.375
diff -c -p -r1.375 vacuum.c
*** src/backend/commands/vacuum.c	5 Jun 2008 15:47:32 -0000	1.375
--- src/backend/commands/vacuum.c	8 Aug 2008 16:42:02 -0000
*************** static BufferAccessStrategy vac_strategy
*** 213,220 ****
  static List *get_rel_oids(Oid relid, const RangeVar *vacrel,
  			 const char *stmttype);
  static void vac_truncate_clog(TransactionId frozenXID);
! static void vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind,
! 					   bool for_wraparound);
  static void full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt);
  static void scan_heap(VRelStats *vacrelstats, Relation onerel,
  		  VacPageList vacuum_pages, VacPageList fraged_pages);
--- 213,220 ----
  static List *get_rel_oids(Oid relid, const RangeVar *vacrel,
  			 const char *stmttype);
  static void vac_truncate_clog(TransactionId frozenXID);
! static void vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast,
! 		   bool for_wraparound);
  static void full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt);
  static void scan_heap(VRelStats *vacrelstats, Relation onerel,
  		  VacPageList vacuum_pages, VacPageList fraged_pages);
*************** static Size PageGetFreeSpaceWithFillFact
*** 268,273 ****
--- 268,276 ----
   * OID to be processed, and vacstmt->relation is ignored.  (The non-invalid
   * case is currently only used by autovacuum.)
   *
+  * do_toast is passed as FALSE by autovacuum, because it processes TOAST
+  * tables separately.
+  *
   * for_wraparound is used by autovacuum to let us know when it's forcing
   * a vacuum for wraparound, which should not be auto-cancelled.
   *
*************** static Size PageGetFreeSpaceWithFillFact
*** 281,287 ****
   * at transaction commit.
   */
  void
! vacuum(VacuumStmt *vacstmt, Oid relid,
  	   BufferAccessStrategy bstrategy, bool for_wraparound, bool isTopLevel)
  {
  	const char *stmttype = vacstmt->vacuum ? "VACUUM" : "ANALYZE";
--- 284,290 ----
   * at transaction commit.
   */
  void
! vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
  	   BufferAccessStrategy bstrategy, bool for_wraparound, bool isTopLevel)
  {
  	const char *stmttype = vacstmt->vacuum ? "VACUUM" : "ANALYZE";
*************** vacuum(VacuumStmt *vacstmt, Oid relid,
*** 433,439 ****
  			Oid			relid = lfirst_oid(cur);
  
  			if (vacstmt->vacuum)
! 				vacuum_rel(relid, vacstmt, RELKIND_RELATION, for_wraparound);
  
  			if (vacstmt->analyze)
  			{
--- 436,442 ----
  			Oid			relid = lfirst_oid(cur);
  
  			if (vacstmt->vacuum)
! 				vacuum_rel(relid, vacstmt, do_toast, for_wraparound);
  
  			if (vacstmt->analyze)
  			{
*************** vac_truncate_clog(TransactionId frozenXI
*** 975,982 ****
   *		At entry and exit, we are not inside a transaction.
   */
  static void
! vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind,
! 		   bool for_wraparound)
  {
  	LOCKMODE	lmode;
  	Relation	onerel;
--- 978,984 ----
   *		At entry and exit, we are not inside a transaction.
   */
  static void
! vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound)
  {
  	LOCKMODE	lmode;
  	Relation	onerel;
*************** vacuum_rel(Oid relid, VacuumStmt *vacstm
*** 1013,1020 ****
  		 * by autovacuum; it's used to avoid cancelling a vacuum that was
  		 * invoked in an emergency.
  		 *
! 		 * 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.
  		 */
--- 1015,1022 ----
  		 * by autovacuum; it's used to avoid cancelling a vacuum that was
  		 * invoked in an emergency.
  		 *
! 		 * Note: these flags remain set until CommitTransaction or
! 		 * AbortTransaction.  We don't want to clear them until we reset
  		 * MyProc->xid/xmin, else OldestXmin might appear to go backwards,
  		 * which is probably Not Good.
  		 */
*************** vacuum_rel(Oid relid, VacuumStmt *vacstm
*** 1087,1096 ****
  	}
  
  	/*
! 	 * Check that it's a plain table; we used to do this in get_rel_oids() but
! 	 * seems safer to check after we've locked the relation.
  	 */
! 	if (onerel->rd_rel->relkind != expected_relkind)
  	{
  		ereport(WARNING,
  				(errmsg("skipping \"%s\" --- cannot vacuum indexes, views, or special system tables",
--- 1089,1099 ----
  	}
  
  	/*
! 	 * Check that it's a vacuumable table; we used to do this in get_rel_oids()
! 	 * but seems safer to check after we've locked the relation.
  	 */
! 	if (onerel->rd_rel->relkind != RELKIND_RELATION &&
! 		onerel->rd_rel->relkind != RELKIND_TOASTVALUE)
  	{
  		ereport(WARNING,
  				(errmsg("skipping \"%s\" --- cannot vacuum indexes, views, or special system tables",
*************** vacuum_rel(Oid relid, VacuumStmt *vacstm
*** 1132,1140 ****
  	LockRelationIdForSession(&onerelid, lmode);
  
  	/*
! 	 * Remember the relation's TOAST relation for later
  	 */
! 	toast_relid = onerel->rd_rel->reltoastrelid;
  
  	/*
  	 * Switch to the table owner's userid, so that any index functions are
--- 1135,1147 ----
  	LockRelationIdForSession(&onerelid, lmode);
  
  	/*
! 	 * Remember the relation's TOAST relation for later, if the caller asked
! 	 * us to process it.
  	 */
! 	if (do_toast)
! 		toast_relid = onerel->rd_rel->reltoastrelid;
! 	else
! 		toast_relid = InvalidOid;
  
  	/*
  	 * Switch to the table owner's userid, so that any index functions are
*************** vacuum_rel(Oid relid, VacuumStmt *vacstm
*** 1173,1179 ****
  	 * totally unimportant for toast relations.
  	 */
  	if (toast_relid != InvalidOid)
! 		vacuum_rel(toast_relid, vacstmt, RELKIND_TOASTVALUE, for_wraparound);
  
  	/*
  	 * Now release the session-level lock on the master table.
--- 1180,1186 ----
  	 * totally unimportant for toast relations.
  	 */
  	if (toast_relid != InvalidOid)
! 		vacuum_rel(toast_relid, vacstmt, false, for_wraparound);
  
  	/*
  	 * Now release the session-level lock on the master table.
Index: src/backend/postmaster/autovacuum.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/postmaster/autovacuum.c,v
retrieving revision 1.83
diff -c -p -r1.83 autovacuum.c
*** src/backend/postmaster/autovacuum.c	23 Jul 2008 20:20:10 -0000	1.83
--- src/backend/postmaster/autovacuum.c	8 Aug 2008 20:33:24 -0000
***************
*** 93,98 ****
--- 93,99 ----
  #include "storage/procarray.h"
  #include "storage/sinvaladt.h"
  #include "tcop/tcopprot.h"
+ #include "utils/dynahash.h"
  #include "utils/flatfiles.h"
  #include "utils/fmgroids.h"
  #include "utils/lsyscache.h"
*************** typedef struct avw_dbase
*** 161,168 ****
  /* 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 */
--- 162,169 ----
  /* struct to keep track of tables to vacuum and/or analyze, in 1st pass */
  typedef struct av_relation
  {
+ 	Oid			ar_toastrelid; /* must be first -- used as hash key */
  	Oid			ar_relid;
  } av_relation;
  
  /* struct to keep track of tables to vacuum and/or analyze, after rechecking */
*************** static void autovac_balance_cost(void);
*** 279,285 ****
  static void do_autovacuum(void);
  static void FreeWorkerInfo(int code, Datum arg);
  
! static autovac_table *table_recheck_autovac(Oid relid);
  static void relation_needs_vacanalyze(Oid relid, Form_pg_autovacuum avForm,
  						  Form_pg_class classForm,
  						  PgStat_StatTabEntry *tabentry, bool *dovacuum,
--- 280,286 ----
  static void do_autovacuum(void);
  static void FreeWorkerInfo(int code, Datum arg);
  
! static autovac_table *table_recheck_autovac(Oid relid, HTAB *table_toast_map);
  static void relation_needs_vacanalyze(Oid relid, Form_pg_autovacuum avForm,
  						  Form_pg_class classForm,
  						  PgStat_StatTabEntry *tabentry, bool *dovacuum,
*************** static void relation_needs_vacanalyze(Oi
*** 287,293 ****
  
  static void autovacuum_do_vac_analyze(autovac_table *tab,
  						  BufferAccessStrategy bstrategy);
! 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);
--- 288,295 ----
  
  static void autovacuum_do_vac_analyze(autovac_table *tab,
  						  BufferAccessStrategy bstrategy);
! static HeapTuple get_pg_autovacuum_tuple_relid(Relation avRel, Oid relid,
! 											   HTAB *table_toast_map);
  static PgStat_StatTabEntry *get_pgstat_tabentry_relid(Oid relid, bool isshared,
  						  PgStat_StatDBEntry *shared,
  						  PgStat_StatDBEntry *dbentry);
*************** do_autovacuum(void)
*** 1821,1832 ****
  	HeapScanDesc relScan;
  	Form_pg_database dbForm;
  	List	   *table_oids = NIL;
! 	List	   *toast_oids = NIL;
! 	List	   *table_toast_list = NIL;
  	ListCell   *volatile cell;
  	PgStat_StatDBEntry *shared;
  	PgStat_StatDBEntry *dbentry;
  	BufferAccessStrategy bstrategy;
  
  	/*
  	 * StartTransactionCommand and CommitTransactionCommand will automatically
--- 1823,1835 ----
  	HeapScanDesc relScan;
  	Form_pg_database dbForm;
  	List	   *table_oids = NIL;
! 	HASHCTL		ctl;
! 	HTAB	   *table_toast_map;
  	ListCell   *volatile cell;
  	PgStat_StatDBEntry *shared;
  	PgStat_StatDBEntry *dbentry;
  	BufferAccessStrategy bstrategy;
+ 	ScanKeyData	key;
  
  	/*
  	 * StartTransactionCommand and CommitTransactionCommand will automatically
*************** do_autovacuum(void)
*** 1884,1908 ****
  	classRel = heap_open(RelationRelationId, AccessShareLock);
  	avRel = heap_open(AutovacuumRelationId, AccessShareLock);
  
  	/*
! 	 * Scan pg_class and determine which tables to vacuum.
  	 *
! 	 * The stats subsystem collects stats for toast tables independently of
! 	 * the stats for their parent tables.  We need to check those stats since
! 	 * in cases with short, wide tables there might be proportionally much
! 	 * more activity in the toast table than in its parent.
  	 *
! 	 * Since we can only issue VACUUM against the parent table, we need to
! 	 * transpose a decision to vacuum a toast table into a decision to vacuum
! 	 * its parent.	There's no point in considering ANALYZE on a toast table,
! 	 * either.	To support this, we keep a list of OIDs of toast tables that
! 	 * need vacuuming alongside the list of regular tables.  Regular tables
! 	 * will be entered into the table list even if they appear not to need
! 	 * vacuuming; we go back and re-mark them after finding all the vacuumable
! 	 * toast tables.
  	 */
! 	relScan = heap_beginscan(classRel, SnapshotNow, 0, NULL);
  
  	while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
  	{
  		Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
--- 1887,1928 ----
  	classRel = heap_open(RelationRelationId, AccessShareLock);
  	avRel = heap_open(AutovacuumRelationId, AccessShareLock);
  
+ 	/* create hash table for toast <-> main relid mapping */
+ 	MemSet(&ctl, 0, sizeof(ctl));
+ 	ctl.keysize = sizeof(Oid);
+ 	ctl.entrysize = sizeof(Oid) * 2;
+ 	ctl.hash = oid_hash;
+ 
+ 	table_toast_map = hash_create("TOAST to main relid map",
+ 								  100,
+ 								  &ctl,
+ 								  HASH_ELEM | HASH_FUNCTION);
+ 
  	/*
! 	 * Scan pg_class to determine which tables to vacuum.
  	 *
! 	 * We do this in two passes: on the first one we collect the list of
! 	 * plain relations, and on the second one we collect TOAST tables.
! 	 * The reason for doing the second pass is that during it we want to use
! 	 * the main relation's pg_autovacuum entry if the TOAST table does not have
! 	 * any, and we cannot obtain it unless we know beforehand what's the main
! 	 * table OID.
  	 *
! 	 * We need to check TOAST tables separately because in cases with short,
! 	 * wide tables there might be proportionally much more activity in the
! 	 * TOAST table than in its parent.
  	 */
! 	ScanKeyInit(&key,
! 				Anum_pg_class_relkind,
! 				BTEqualStrategyNumber, F_CHAREQ,
! 				CharGetDatum(RELKIND_RELATION));
! 
! 	relScan = heap_beginscan(classRel, SnapshotNow, 1, &key);
  
+ 	/*
+ 	 * On the first pass, we collect main tables to vacuum, and also the
+ 	 * main table relid to TOAST relid mapping.
+ 	 */
  	while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
  	{
  		Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
*************** do_autovacuum(void)
*** 1915,1929 ****
  		bool		wraparound;
  		int			backendID;
  
- 		/* Consider only regular and toast tables. */
- 		if (classForm->relkind != RELKIND_RELATION &&
- 			classForm->relkind != RELKIND_TOASTVALUE)
- 			continue;
- 
  		relid = HeapTupleGetOid(tuple);
  
  		/* Fetch the pg_autovacuum tuple for the relation, if any */
! 		avTup = get_pg_autovacuum_tuple_relid(avRel, relid);
  		if (HeapTupleIsValid(avTup))
  			avForm = (Form_pg_autovacuum) GETSTRUCT(avTup);
  
--- 1935,1944 ----
  		bool		wraparound;
  		int			backendID;
  
  		relid = HeapTupleGetOid(tuple);
  
  		/* Fetch the pg_autovacuum tuple for the relation, if any */
! 		avTup = get_pg_autovacuum_tuple_relid(avRel, relid, NULL);
  		if (HeapTupleIsValid(avTup))
  			avForm = (Form_pg_autovacuum) GETSTRUCT(avTup);
  
*************** do_autovacuum(void)
*** 1952,1958 ****
  				 * vacuum for wraparound, forcibly drop it.  Otherwise just
  				 * log a complaint.
  				 */
! 				if (wraparound && classForm->relkind == RELKIND_RELATION)
  				{
  					ObjectAddress object;
  
--- 1967,1973 ----
  				 * vacuum for wraparound, forcibly drop it.  Otherwise just
  				 * log a complaint.
  				 */
! 				if (wraparound)
  				{
  					ObjectAddress object;
  
*************** do_autovacuum(void)
*** 1976,2040 ****
  				}
  			}
  		}
! 		else if (classForm->relkind == RELKIND_RELATION)
  		{
  			/* Plain relations that need work are added to table_oids */
  			if (dovacuum || doanalyze)
  				table_oids = lappend_oid(table_oids, relid);
! 			else if (OidIsValid(classForm->reltoastrelid))
  			{
! 				/*
! 				 * If it doesn't appear to need vacuuming, but it has a toast
! 				 * table, remember the association to revisit below.
! 				 */
! 				av_relation *rel = palloc(sizeof(av_relation));
  
! 				rel->ar_relid = relid;
! 				rel->ar_toastrelid = classForm->reltoastrelid;
  
! 				table_toast_list = lappend(table_toast_list, rel);
  			}
  		}
- 		else
- 		{
- 			/* TOAST relations that need vacuum are added to toast_oids */
- 			if (dovacuum)
- 				toast_oids = lappend_oid(toast_oids, relid);
- 		}
  
  		if (HeapTupleIsValid(avTup))
  			heap_freetuple(avTup);
  	}
  
  	heap_endscan(relScan);
- 	heap_close(avRel, AccessShareLock);
- 	heap_close(classRel, AccessShareLock);
  
! 	/*
! 	 * Add to the list of tables to vacuum, the OIDs of the tables that
! 	 * correspond to the saved OIDs of toast tables needing vacuum.
! 	 */
! 	foreach(cell, toast_oids)
  	{
! 		Oid			toastoid = lfirst_oid(cell);
! 		ListCell   *cell2;
  
! 		foreach(cell2, table_toast_list)
! 		{
! 			av_relation *ar = lfirst(cell2);
  
! 			if (ar->ar_toastrelid == toastoid)
! 			{
! 				table_oids = lappend_oid(table_oids, ar->ar_relid);
! 				break;
! 			}
! 		}
  	}
  
! 	list_free_deep(table_toast_list);
! 	table_toast_list = NIL;
! 	list_free(toast_oids);
! 	toast_oids = NIL;
  
  	/*
  	 * Create a buffer access strategy object for VACUUM to use.  We want to
--- 1991,2078 ----
  				}
  			}
  		}
! 		else
  		{
  			/* Plain relations that need work are added to table_oids */
  			if (dovacuum || doanalyze)
  				table_oids = lappend_oid(table_oids, relid);
! 
! 			/*
! 			 * Remember the association for the second pass.  Note: we must do
! 			 * this even if the table is going to be vacuumed, because we
! 			 * don't automatically vacuum toast tables along the parent table.
! 			 */
! 			if (OidIsValid(classForm->reltoastrelid))
  			{
! 				av_relation *hentry;
! 				bool		found;
  
! 				hentry = hash_search(table_toast_map,
! 									 (void *) &classForm->reltoastrelid,
! 									 HASH_ENTER, &found);
  
! 				if (!found)
! 				{
! 					/* hash_search already filled in the key */
! 					hentry->ar_relid = relid;
! 				}
  			}
  		}
  
  		if (HeapTupleIsValid(avTup))
  			heap_freetuple(avTup);
  	}
  
  	heap_endscan(relScan);
  
! 	/* second pass: check TOAST tables */
! 	ScanKeyInit(&key,
! 				Anum_pg_class_relkind,
! 				BTEqualStrategyNumber, F_CHAREQ,
! 				CharGetDatum(RELKIND_TOASTVALUE));
! 
! 	relScan = heap_beginscan(classRel, SnapshotNow, 1, &key);
! 	while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
  	{
! 		Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
! 		Form_pg_autovacuum avForm = NULL;
! 		PgStat_StatTabEntry *tabentry;
! 		HeapTuple   avTup;
! 		Oid         relid;
! 		bool		dovacuum;
! 		bool		doanalyze;
! 		bool		wraparound;
  
! 		/*
! 		 * Skip temp tables (i.e. those in temp namespaces).  We cannot safely
! 		 * process other backends' temp tables.
! 		 */
! 		if (isAnyTempNamespace(classForm->relnamespace))
! 			continue;
  
! 		relid = HeapTupleGetOid(tuple);
! 
! 		/* Fetch the pg_autovacuum tuple for this rel */
! 		avTup = get_pg_autovacuum_tuple_relid(avRel, relid, table_toast_map);
! 
! 		if (HeapTupleIsValid(avTup))
! 			avForm = (Form_pg_autovacuum) GETSTRUCT(avTup);
! 
! 		/* Fetch the pgstat entry for this table */
! 		tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared,
! 											 shared, dbentry);
! 
! 		relation_needs_vacanalyze(relid, avForm, classForm, tabentry,
! 								  &dovacuum, &doanalyze, &wraparound);
! 
! 		/* ignore analyze for toast tables */
! 		if (dovacuum)
! 			table_oids = lappend_oid(table_oids, relid);
  	}
  
! 	heap_endscan(relScan);
! 	heap_close(avRel, AccessShareLock);
! 	heap_close(classRel, AccessShareLock);
  
  	/*
  	 * Create a buffer access strategy object for VACUUM to use.  We want to
*************** do_autovacuum(void)
*** 2118,2124 ****
  		 * vacuumed in the last 500ms (PGSTAT_STAT_INTERVAL).  This is a bug.
  		 */
  		MemoryContextSwitchTo(AutovacMemCxt);
! 		tab = table_recheck_autovac(relid);
  		if (tab == NULL)
  		{
  			/* someone else vacuumed the table */
--- 2156,2162 ----
  		 * vacuumed in the last 500ms (PGSTAT_STAT_INTERVAL).  This is a bug.
  		 */
  		MemoryContextSwitchTo(AutovacMemCxt);
! 		tab = table_recheck_autovac(relid, table_toast_map);
  		if (tab == NULL)
  		{
  			/* someone else vacuumed the table */
*************** deleted:
*** 2244,2252 ****
  /*
   * Returns a copy of the pg_autovacuum tuple for the given relid, or NULL if
   * there isn't any.  avRel is pg_autovacuum, already open and suitably locked.
   */
  static HeapTuple
! get_pg_autovacuum_tuple_relid(Relation avRel, Oid relid)
  {
  	ScanKeyData entry[1];
  	SysScanDesc avScan;
--- 2282,2295 ----
  /*
   * Returns a copy of the pg_autovacuum tuple for the given relid, or NULL if
   * there isn't any.  avRel is pg_autovacuum, already open and suitably locked.
+  *
+  * If table_toast_map is not null, use it to find an alternative OID with which
+  * to search a pg_autovacuum entry, if the passed relid does not yield one
+  * directly.
   */
  static HeapTuple
! get_pg_autovacuum_tuple_relid(Relation avRel, Oid relid,
! 							  HTAB *table_toast_map)
  {
  	ScanKeyData entry[1];
  	SysScanDesc avScan;
*************** get_pg_autovacuum_tuple_relid(Relation a
*** 2267,2272 ****
--- 2310,2328 ----
  
  	systable_endscan(avScan);
  
+ 	if (!HeapTupleIsValid(avTup) && table_toast_map != NULL)
+ 	{
+ 		av_relation		*hentry;
+ 		bool		found;
+ 
+ 		hentry = hash_search(table_toast_map, (void *) &relid,
+ 							 HASH_FIND, &found);
+ 		if (found)
+ 			/* avoid second recursion */
+ 			avTup = get_pg_autovacuum_tuple_relid(avRel, hentry->ar_relid,
+ 												  NULL);
+ 	}
+ 
  	return avTup;
  }
  
*************** get_pgstat_tabentry_relid(Oid relid, boo
*** 2297,2310 ****
  /*
   * 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.
   *
   * Note that the returned autovac_table does not have the name fields set.
   */
  static autovac_table *
! table_recheck_autovac(Oid relid)
  {
  	Form_pg_autovacuum avForm = NULL;
  	Form_pg_class classForm;
--- 2353,2365 ----
  /*
   * table_recheck_autovac
   *
!  * Recheck whether a table still needs vacuum or analyze.  Return value is a
!  * valid autovac_table pointer if it does, NULL otherwise.
   *
   * Note that the returned autovac_table does not have the name fields set.
   */
  static autovac_table *
! table_recheck_autovac(Oid relid, HTAB *table_toast_map)
  {
  	Form_pg_autovacuum avForm = NULL;
  	Form_pg_class classForm;
*************** table_recheck_autovac(Oid relid)
*** 2315,2325 ****
  	bool		doanalyze;
  	autovac_table *tab = NULL;
  	PgStat_StatTabEntry *tabentry;
- 	bool		doit = false;
  	PgStat_StatDBEntry *shared;
  	PgStat_StatDBEntry *dbentry;
! 	bool		wraparound,
! 				toast_wraparound = false;
  
  	/* use fresh stats */
  	autovac_refresh_stats();
--- 2370,2378 ----
  	bool		doanalyze;
  	autovac_table *tab = NULL;
  	PgStat_StatTabEntry *tabentry;
  	PgStat_StatDBEntry *shared;
  	PgStat_StatDBEntry *dbentry;
! 	bool		wraparound;
  
  	/* use fresh stats */
  	autovac_refresh_stats();
*************** table_recheck_autovac(Oid relid)
*** 2335,2343 ****
  		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);
  
--- 2388,2402 ----
  		return NULL;
  	classForm = (Form_pg_class) GETSTRUCT(classTup);
  
! 	/*
! 	 * Fetch the pg_autovacuum entry, if any.  For a toast table, also try the
! 	 * main rel's pg_autovacuum entry if there isn't one for the TOAST table
! 	 * itself.
! 	 */
  	avRel = heap_open(AutovacuumRelationId, AccessShareLock);
! 	avTup = get_pg_autovacuum_tuple_relid(avRel, relid,
! 			classForm->relkind == RELKIND_TOASTVALUE ? table_toast_map : NULL);
! 
  	if (HeapTupleIsValid(avTup))
  		avForm = (Form_pg_autovacuum) GETSTRUCT(avTup);
  
*************** table_recheck_autovac(Oid relid)
*** 2348,2398 ****
  	relation_needs_vacanalyze(relid, avForm, classForm, tabentry,
  							  &dovacuum, &doanalyze, &wraparound);
  
! 	/* 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;
! 			bool		toast_wraparound;
! 			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,
! 									  &toast_wraparound);
! 			/* 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;
--- 2407,2418 ----
  	relation_needs_vacanalyze(relid, avForm, classForm, tabentry,
  							  &dovacuum, &doanalyze, &wraparound);
  
! 	/* ignore ANALYZE for toast tables */
! 	if (classForm->relkind == RELKIND_TOASTVALUE)
! 		doanalyze = false;
  
! 	/* OK, it needs something done */
! 	if (doanalyze || dovacuum)
  	{
  		int			freeze_min_age;
  		int			vac_cost_limit;
*************** table_recheck_autovac(Oid relid)
*** 2439,2445 ****
  		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;
  		tab->at_relname = NULL;
  		tab->at_nspname = NULL;
  		tab->at_datname = NULL;
--- 2459,2465 ----
  		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;
  		tab->at_relname = NULL;
  		tab->at_nspname = NULL;
  		tab->at_datname = NULL;
*************** autovacuum_do_vac_analyze(autovac_table 
*** 2633,2639 ****
  	/* Let pgstat know what we're doing */
  	autovac_report_activity(tab);
  
! 	vacuum(&vacstmt, tab->at_relid, bstrategy, tab->at_wraparound, true);
  }
  
  /*
--- 2653,2659 ----
  	/* Let pgstat know what we're doing */
  	autovac_report_activity(tab);
  
! 	vacuum(&vacstmt, tab->at_relid, false, bstrategy, tab->at_wraparound, true);
  }
  
  /*
Index: src/backend/tcop/utility.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/tcop/utility.c,v
retrieving revision 1.295
diff -c -p -r1.295 utility.c
*** src/backend/tcop/utility.c	18 Jul 2008 20:26:06 -0000	1.295
--- src/backend/tcop/utility.c	8 Aug 2008 15:51:22 -0000
*************** ProcessUtility(Node *parsetree,
*** 836,842 ****
  			break;
  
  		case T_VacuumStmt:
! 			vacuum((VacuumStmt *) parsetree, InvalidOid, NULL, false,
  				   isTopLevel);
  			break;
  
--- 836,842 ----
  			break;
  
  		case T_VacuumStmt:
! 			vacuum((VacuumStmt *) parsetree, InvalidOid, true, NULL, false,
  				   isTopLevel);
  			break;
  
Index: src/include/commands/vacuum.h
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/include/commands/vacuum.h,v
retrieving revision 1.79
diff -c -p -r1.79 vacuum.h
*** src/include/commands/vacuum.h	1 Jul 2008 10:33:09 -0000	1.79
--- src/include/commands/vacuum.h	8 Aug 2008 15:51:22 -0000
*************** extern int	vacuum_freeze_min_age;
*** 125,131 ****
  
  
  /* in commands/vacuum.c */
! extern void vacuum(VacuumStmt *vacstmt, Oid relid,
  	   BufferAccessStrategy bstrategy, bool for_wraparound, bool isTopLevel);
  extern void vac_open_indexes(Relation relation, LOCKMODE lockmode,
  				 int *nindexes, Relation **Irel);
--- 125,131 ----
  
  
  /* in commands/vacuum.c */
! extern void vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
  	   BufferAccessStrategy bstrategy, bool for_wraparound, bool isTopLevel);
  extern void vac_open_indexes(Relation relation, LOCKMODE lockmode,
  				 int *nindexes, Relation **Irel);
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to