diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/utils/cache/relcache.c | 57 |
1 files changed, 52 insertions, 5 deletions
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index cdfc2bf21e9..779117db6a2 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.190.2.1 2008/02/27 17:45:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.190.2.2 2009/09/26 18:25:35 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1393,8 +1393,10 @@ formrdesc(const char *relationName, * initialize relation tuple form * * 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. + * 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); @@ -2408,17 +2410,31 @@ RelationCacheInitializePhase2(void) * pg_class 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 (needNewCacheFile && relation->rd_isnailed) + if (relation->rd_rel->relowner == InvalidOid) { HeapTuple htup; Form_pg_class relp; @@ -2435,20 +2451,51 @@ RelationCacheInitializePhase2(void) * 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); 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 reltriggers 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->reltriggers > 0 && relation->trigdesc == NULL) + { RelationBuildTriggers(relation); + if (relation->trigdesc == NULL) + relation->rd_rel->reltriggers = 0; + 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); + } } } |