I wrote:
> I'll get you a real fix as soon as I can, but might not be till
> tomorrow.

The attached patch (against 8.4.x) fixes the problem as far as I can
tell.  Please test.

                        regards, tom lane

Index: src/backend/utils/cache/relcache.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v
retrieving revision 1.287
diff -c -r1.287 relcache.c
*** src/backend/utils/cache/relcache.c	11 Jun 2009 14:49:05 -0000	1.287
--- src/backend/utils/cache/relcache.c	25 Sep 2009 17:32:02 -0000
***************
*** 1386,1392 ****
  	 *
  	 * The data we insert here is pretty incomplete/bogus, but it'll serve to
  	 * get us launched.  RelationCacheInitializePhase2() will read the real
! 	 * data from pg_class and replace what we've done here.
  	 */
  	relation->rd_rel = (Form_pg_class) palloc0(CLASS_TUPLE_SIZE);
  
--- 1386,1394 ----
  	 *
  	 * The data we insert here is pretty incomplete/bogus, but it'll serve to
  	 * get us launched.  RelationCacheInitializePhase2() will read the real
! 	 * data from pg_class and replace what we've done here.  Note in particular
! 	 * that relowner is left as zero; this cues RelationCacheInitializePhase2
! 	 * that the real data isn't there yet.
  	 */
  	relation->rd_rel = (Form_pg_class) palloc0(CLASS_TUPLE_SIZE);
  
***************
*** 2603,2619 ****
  	 * rows and replace the fake entries with them. Also, if any of the
  	 * relcache entries have rules or triggers, load that info the hard way
  	 * since it isn't recorded in the cache file.
  	 */
  	hash_seq_init(&status, RelationIdCache);
  
  	while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
  	{
  		Relation	relation = idhentry->reldesc;
  
  		/*
  		 * If it's a faked-up entry, read the real pg_class tuple.
  		 */
! 		if (needNewCacheFile && relation->rd_isnailed)
  		{
  			HeapTuple	htup;
  			Form_pg_class relp;
--- 2605,2635 ----
  	 * rows and replace the fake entries with them. Also, if any of the
  	 * relcache entries have rules or triggers, load that info the hard way
  	 * since it isn't recorded in the cache file.
+ 	 *
+ 	 * Whenever we access the catalogs to read data, there is a possibility
+ 	 * of a shared-inval cache flush causing relcache entries to be removed.
+ 	 * Since hash_seq_search only guarantees to still work after the *current*
+ 	 * entry is removed, it's unsafe to continue the hashtable scan afterward.
+ 	 * We handle this by restarting the scan from scratch after each access.
+ 	 * This is theoretically O(N^2), but the number of entries that actually
+ 	 * need to be fixed is small enough that it doesn't matter.
  	 */
  	hash_seq_init(&status, RelationIdCache);
  
  	while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
  	{
  		Relation	relation = idhentry->reldesc;
+ 		bool		restart = false;
+ 
+ 		/*
+ 		 * Make sure *this* entry doesn't get flushed while we work with it.
+ 		 */
+ 		RelationIncrementReferenceCount(relation);
  
  		/*
  		 * If it's a faked-up entry, read the real pg_class tuple.
  		 */
! 		if (relation->rd_rel->relowner == InvalidOid)
  		{
  			HeapTuple	htup;
  			Form_pg_class relp;
***************
*** 2630,2636 ****
  			 * Copy tuple to relation->rd_rel. (See notes in
  			 * AllocateRelationDesc())
  			 */
- 			Assert(relation->rd_rel != NULL);
  			memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE);
  
  			/* Update rd_options while we have the tuple */
--- 2646,2651 ----
***************
*** 2639,2660 ****
  			RelationParseRelOptions(relation, htup);
  
  			/*
! 			 * Also update the derived fields in rd_att.
  			 */
! 			relation->rd_att->tdtypeid = relp->reltype;
! 			relation->rd_att->tdtypmod = -1;	/* unnecessary, but... */
! 			relation->rd_att->tdhasoid = relp->relhasoids;
  
  			ReleaseSysCache(htup);
  		}
  
  		/*
  		 * Fix data that isn't saved in relcache cache file.
  		 */
  		if (relation->rd_rel->relhasrules && relation->rd_rules == NULL)
  			RelationBuildRuleLock(relation);
  		if (relation->rd_rel->relhastriggers && relation->trigdesc == NULL)
  			RelationBuildTriggers(relation);
  	}
  
  	/*
--- 2654,2710 ----
  			RelationParseRelOptions(relation, htup);
  
  			/*
! 			 * Check the values in rd_att were set up correctly.  (We cannot
! 			 * just copy them over now: formrdesc must have set up the
! 			 * rd_att data correctly to start with, because it may already
! 			 * have been copied into one or more catcache entries.)
  			 */
! 			Assert(relation->rd_att->tdtypeid == relp->reltype);
! 			Assert(relation->rd_att->tdtypmod == -1);
! 			Assert(relation->rd_att->tdhasoid == relp->relhasoids);
  
  			ReleaseSysCache(htup);
+ 
+ 			/* relowner had better be OK now, else we'll loop forever */
+ 			if (relation->rd_rel->relowner == InvalidOid)
+ 				elog(ERROR, "invalid relowner in pg_class entry for \"%s\"",
+ 					 RelationGetRelationName(relation));
+ 
+ 			restart = true;
  		}
  
  		/*
  		 * Fix data that isn't saved in relcache cache file.
+ 		 *
+ 		 * relhasrules or relhastriggers could possibly be wrong or out of
+ 		 * date.  If we don't actually find any rules or triggers, clear the
+ 		 * local copy of the flag so that we don't get into an infinite loop
+ 		 * here.  We don't make any attempt to fix the pg_class entry, though.
  		 */
  		if (relation->rd_rel->relhasrules && relation->rd_rules == NULL)
+ 		{
  			RelationBuildRuleLock(relation);
+ 			if (relation->rd_rules == NULL)
+ 				relation->rd_rel->relhasrules = false;
+ 			restart = true;
+ 		}
  		if (relation->rd_rel->relhastriggers && relation->trigdesc == NULL)
+ 		{
  			RelationBuildTriggers(relation);
+ 			if (relation->trigdesc == NULL)
+ 				relation->rd_rel->relhastriggers = false;
+ 			restart = true;
+ 		}
+ 
+ 		/* Release hold on the relation */
+ 		RelationDecrementReferenceCount(relation);
+ 
+ 		/* Now, restart the hashtable scan if needed */
+ 		if (restart)
+ 		{
+ 			hash_seq_term(&status);
+ 			hash_seq_init(&status, RelationIdCache);
+ 		}
  	}
  
  	/*
-- 
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

Reply via email to