diff options
Diffstat (limited to 'src/backend/utils/cache/relcache.c')
-rw-r--r-- | src/backend/utils/cache/relcache.c | 421 |
1 files changed, 259 insertions, 162 deletions
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 4a6c86d84d9..7c993d3d73f 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.88 2000/01/29 19:51:59 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.89 2000/01/31 04:35:52 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,7 +20,6 @@ * RelationIdGetRelation - get a reldesc by relation id * RelationNameGetRelation - get a reldesc by relation name * RelationClose - close an open relation - * RelationRebuildRelation - rebuild relation information * * NOTES * This file is in the process of being cleaned up @@ -37,7 +36,6 @@ #include "postgres.h" -#include "utils/builtins.h" #include "access/genam.h" #include "access/heapam.h" #include "access/istrat.h" @@ -52,50 +50,45 @@ #include "catalog/pg_rewrite.h" #include "catalog/pg_type.h" #include "catalog/pg_variable.h" +#include "commands/trigger.h" #include "lib/hasht.h" #include "miscadmin.h" +#include "storage/bufmgr.h" #include "storage/smgr.h" +#include "utils/builtins.h" #include "utils/catcache.h" #include "utils/relcache.h" #include "utils/temprel.h" -static void RelationClearRelation(Relation relation, bool rebuildIt); -static void RelationFlushRelation(Relation *relationPtr, - bool onlyFlushReferenceCountZero); -static Relation RelationNameCacheGetRelation(const char *relationName); -static void RelationCacheAbortWalker(Relation *relationPtr, - int dummy); -static void init_irels(void); -static void write_irels(void); - -/* ---------------- - * externs - * ---------------- - */ -extern bool AMI_OVERRIDE; /* XXX style */ -extern GlobalMemory CacheCxt; /* from utils/cache/catcache.c */ - /* ---------------- * hardcoded tuple descriptors. see lib/backend/catalog/pg_attribute.h * ---------------- */ -FormData_pg_attribute Desc_pg_class[Natts_pg_class] = {Schema_pg_class}; -FormData_pg_attribute Desc_pg_attribute[Natts_pg_attribute] = {Schema_pg_attribute}; -FormData_pg_attribute Desc_pg_proc[Natts_pg_proc] = {Schema_pg_proc}; -FormData_pg_attribute Desc_pg_type[Natts_pg_type] = {Schema_pg_type}; -FormData_pg_attribute Desc_pg_variable[Natts_pg_variable] = {Schema_pg_variable}; -FormData_pg_attribute Desc_pg_log[Natts_pg_log] = {Schema_pg_log}; +static FormData_pg_attribute Desc_pg_class[Natts_pg_class] = {Schema_pg_class}; +static FormData_pg_attribute Desc_pg_attribute[Natts_pg_attribute] = {Schema_pg_attribute}; +static FormData_pg_attribute Desc_pg_proc[Natts_pg_proc] = {Schema_pg_proc}; +static FormData_pg_attribute Desc_pg_type[Natts_pg_type] = {Schema_pg_type}; +static FormData_pg_attribute Desc_pg_variable[Natts_pg_variable] = {Schema_pg_variable}; +static FormData_pg_attribute Desc_pg_log[Natts_pg_log] = {Schema_pg_log}; /* ---------------- - * global variables + * Hash tables that index the relation cache * * Relations are cached two ways, by name and by id, * thus there are two hash tables for referencing them. * ---------------- */ -HTAB *RelationNameCache; -HTAB *RelationIdCache; +static HTAB *RelationNameCache; +static HTAB *RelationIdCache; + +/* + * newlyCreatedRelns - + * relations created during this transaction. We need to keep track of + * these. + */ +static List *newlyCreatedRelns = NULL; + /* ---------------- * RelationBuildDescInfo exists so code can be shared @@ -207,8 +200,17 @@ do { \ } while(0) /* non-export function prototypes */ + +static void RelationClearRelation(Relation relation, bool rebuildIt); +static void RelationFlushRelation(Relation *relationPtr, + int skipLocalRelations); +static Relation RelationNameCacheGetRelation(const char *relationName); +static void RelationCacheAbortWalker(Relation *relationPtr, int dummy); +static void init_irels(void); +static void write_irels(void); + static void formrdesc(char *relationName, u_int natts, - FormData_pg_attribute *att); + FormData_pg_attribute *att); static HeapTuple ScanPgRelation(RelationBuildDescInfo buildinfo); static HeapTuple scan_pg_rel_seq(RelationBuildDescInfo buildinfo); @@ -227,16 +229,6 @@ static void IndexedAccessMethodInitialize(Relation relation); static void AttrDefaultFetch(Relation relation); static void RelCheckFetch(Relation relation); -extern void RelationBuildTriggers(Relation relation); -extern void FreeTriggerDesc(Relation relation); - -/* - * newlyCreatedRelns - - * relations created during this transaction. We need to keep track of - * these. - */ -static List *newlyCreatedRelns = NULL; - /* ---------------------------------------------------------------- * RelationIdGetRelation() and RelationNameGetRelation() * support functions @@ -632,22 +624,20 @@ RelationBuildRuleLock(Relation relation) ObjectIdGetDatum(RelationGetRelid(relation))); /* ---------------- - * open pg_attribute and begin a scan + * open pg_rewrite and begin a scan * ---------------- */ pg_rewrite_desc = heap_openr(RewriteRelationName, AccessShareLock); pg_rewrite_scan = heap_beginscan(pg_rewrite_desc, 0, SnapshotNow, 1, &key); pg_rewrite_tupdesc = RelationGetDescr(pg_rewrite_desc); - /* ---------------- - * add attribute data to relation->rd_att - * ---------------- - */ while (HeapTupleIsValid(pg_rewrite_tuple = heap_getnext(pg_rewrite_scan, 0))) { bool isnull; Datum ruleaction; - Datum rule_evqual_string; + Datum rule_evqual; + char *ruleaction_str; + char *rule_evqual_str; RewriteRule *rule; rule = (RewriteRule *) palloc(sizeof(RewriteRule)); @@ -665,24 +655,27 @@ RelationBuildRuleLock(Relation relation) &isnull); ruleaction = heap_getattr(pg_rewrite_tuple, - Anum_pg_rewrite_ev_action, pg_rewrite_tupdesc, + Anum_pg_rewrite_ev_action, + pg_rewrite_tupdesc, &isnull); - rule_evqual_string = heap_getattr(pg_rewrite_tuple, - Anum_pg_rewrite_ev_qual, pg_rewrite_tupdesc, - &isnull); - - ruleaction = PointerGetDatum(textout((text *) DatumGetPointer(ruleaction))); - rule_evqual_string = PointerGetDatum(textout((text *) DatumGetPointer(rule_evqual_string))); - - rule->actions = (List *) stringToNode(DatumGetPointer(ruleaction)); - rule->qual = (Node *) stringToNode(DatumGetPointer(rule_evqual_string)); - - rules[numlocks++] = rule; - if (numlocks == maxlocks) + ruleaction_str = textout((text *) DatumGetPointer(ruleaction)); + rule->actions = (List *) stringToNode(ruleaction_str); + pfree(ruleaction_str); + + rule_evqual = heap_getattr(pg_rewrite_tuple, + Anum_pg_rewrite_ev_qual, + pg_rewrite_tupdesc, + &isnull); + rule_evqual_str = textout((text *) DatumGetPointer(rule_evqual)); + rule->qual = (Node *) stringToNode(rule_evqual_str); + pfree(rule_evqual_str); + + if (numlocks >= maxlocks) { maxlocks *= 2; rules = (RewriteRule **) repalloc(rules, sizeof(RewriteRule *) * maxlocks); } + rules[numlocks++] = rule; } /* ---------------- @@ -701,7 +694,91 @@ RelationBuildRuleLock(Relation relation) rulelock->rules = rules; relation->rd_rules = rulelock; - return; +} + +/* -------------------------------- + * FreeRuleLock + * + * Release the storage used for a set of rewrite rules. + * + * Probably this should be in the rules code someplace... + * -------------------------------- + */ +static void +FreeRuleLock(RuleLock *rlock) +{ + int i; + + if (rlock == NULL) + return; + for (i = 0; i < rlock->numLocks; i++) + { + RewriteRule *rule = rlock->rules[i]; + +#if 0 /* does freefuncs.c still work? Not sure */ + freeObject(rule->actions); + freeObject(rule->qual); +#endif + pfree(rule); + } + pfree(rlock->rules); + pfree(rlock); +} + +/* -------------------------------- + * equalRuleLocks + * + * Determine whether two RuleLocks are equivalent + * + * Probably this should be in the rules code someplace... + * -------------------------------- + */ +static bool +equalRuleLocks(RuleLock *rlock1, RuleLock *rlock2) +{ + int i, + j; + + if (rlock1 != NULL) + { + if (rlock2 == NULL) + return false; + if (rlock1->numLocks != rlock2->numLocks) + return false; + for (i = 0; i < rlock1->numLocks; i++) + { + RewriteRule *rule1 = rlock1->rules[i]; + RewriteRule *rule2 = NULL; + + /* + * We can't assume that the rules are always read from + * pg_rewrite in the same order; so use the rule OIDs to + * identify the rules to compare. (We assume here that the + * same OID won't appear twice in either ruleset.) + */ + for (j = 0; j < rlock2->numLocks; j++) + { + rule2 = rlock2->rules[j]; + if (rule1->ruleId == rule2->ruleId) + break; + } + if (j >= rlock2->numLocks) + return false; + if (rule1->event != rule2->event) + return false; + if (rule1->attrno != rule2->attrno) + return false; + if (rule1->isInstead != rule2->isInstead) + return false; + if (! equal(rule1->qual, rule2->qual)) + return false; + if (! equal(rule1->actions, rule2->actions)) + return false; + } + } + else if (rlock2 != NULL) + return false; + return true; } @@ -800,7 +877,7 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo, * ---------------- */ if (OidIsValid(relam)) - relation->rd_am = (Form_pg_am) AccessMethodObjectIdGetForm(relam); + relation->rd_am = AccessMethodObjectIdGetForm(relam); /* ---------------- * initialize the tuple descriptor (relation->rd_att). @@ -1213,6 +1290,9 @@ RelationClose(Relation relation) * usually used when we are notified of a change to an open relation * (one with refcount > 0). However, this routine just does whichever * it's told to do; callers must determine which they want. + * + * If we detect a change in the relation's TupleDesc or trigger data + * while rebuilding, we complain unless refcount is 0. * -------------------------------- */ static void @@ -1252,26 +1332,53 @@ RelationClearRelation(Relation relation, bool rebuildIt) /* Clear out catcache's entries for this relation */ SystemCacheRelationFlushed(RelationGetRelid(relation)); - /* Free all the subsidiary data structures of the relcache entry */ - FreeTupleDesc(relation->rd_att); - FreeTriggerDesc(relation); - pfree(RelationGetForm(relation)); + /* + * Free all the subsidiary data structures of the relcache entry. + * We cannot free rd_att if we are trying to rebuild the entry, + * however, because pointers to it may be cached in various places. + * The trigger manager might also have pointers into the trigdesc, + * and the rule manager might have pointers into the rewrite rules. + * So to begin with, we can only get rid of these fields: + */ + if (relation->rd_am) + pfree(relation->rd_am); + if (relation->rd_rel) + pfree(relation->rd_rel); + if (relation->rd_istrat) + pfree(relation->rd_istrat); + if (relation->rd_support) + pfree(relation->rd_support); /* * If we're really done with the relcache entry, blow it away. * But if someone is still using it, reconstruct the whole deal * without moving the physical RelationData record (so that the - * someone's pointer is still valid). Must preserve ref count - * and myxactonly flag, too. + * someone's pointer is still valid). */ if (! rebuildIt) { + /* ok to zap remaining substructure */ + FreeTupleDesc(relation->rd_att); + FreeRuleLock(relation->rd_rules); + FreeTriggerDesc(relation->trigdesc); pfree(relation); } else { - uint16 old_refcnt = relation->rd_refcnt; - bool old_myxactonly = relation->rd_myxactonly; + /* + * When rebuilding an open relcache entry, must preserve ref count + * and myxactonly flag. Also attempt to preserve the tupledesc, + * rewrite rules, and trigger substructures in place. + * Furthermore we save/restore rd_nblocks (in case it is a local + * relation) *and* call RelationGetNumberOfBlocks (in case it isn't). + */ + uint16 old_refcnt = relation->rd_refcnt; + bool old_myxactonly = relation->rd_myxactonly; + TupleDesc old_att = relation->rd_att; + RuleLock *old_rules = relation->rd_rules; + TriggerDesc *old_trigdesc = relation->trigdesc; + int old_nblocks = relation->rd_nblocks; + bool relDescChanged = false; RelationBuildDescInfo buildinfo; buildinfo.infotype = INFO_RELID; @@ -1280,12 +1387,54 @@ RelationClearRelation(Relation relation, bool rebuildIt) if (RelationBuildDesc(buildinfo, relation) != relation) { /* Should only get here if relation was deleted */ + FreeTupleDesc(old_att); + FreeRuleLock(old_rules); + FreeTriggerDesc(old_trigdesc); pfree(relation); elog(ERROR, "RelationClearRelation: relation %u deleted while still in use", buildinfo.i.info_id); } RelationSetReferenceCount(relation, old_refcnt); relation->rd_myxactonly = old_myxactonly; + if (equalTupleDescs(old_att, relation->rd_att)) + { + FreeTupleDesc(relation->rd_att); + relation->rd_att = old_att; + } + else + { + FreeTupleDesc(old_att); + relDescChanged = true; + } + if (equalRuleLocks(old_rules, relation->rd_rules)) + { + FreeRuleLock(relation->rd_rules); + relation->rd_rules = old_rules; + } + else + { + FreeRuleLock(old_rules); + relDescChanged = true; + } + if (equalTriggerDescs(old_trigdesc, relation->trigdesc)) + { + FreeTriggerDesc(relation->trigdesc); + relation->trigdesc = old_trigdesc; + } + else + { + FreeTriggerDesc(old_trigdesc); + relDescChanged = true; + } + relation->rd_nblocks = old_nblocks; + /* this is kind of expensive, but I think we must do it in case + * relation has been truncated... + */ + relation->rd_nblocks = RelationGetNumberOfBlocks(relation); + + if (relDescChanged && ! RelationHasReferenceCountZero(relation)) + elog(ERROR, "RelationClearRelation: relation %u modified while in use", + buildinfo.i.info_id); } MemoryContextSwitchTo(oldcxt); @@ -1295,32 +1444,40 @@ RelationClearRelation(Relation relation, bool rebuildIt) * RelationFlushRelation * * Rebuild the relation if it is open (refcount > 0), else blow it away. - * Setting onlyFlushReferenceCountZero to FALSE overrides refcount check. - * This is currently only used to process SI invalidation notifications. + * If skipLocalRelations is TRUE, xact-local relations are ignored + * (which is useful when processing SI cache reset, since xact-local + * relations could not be targets of notifications from other backends). + * * The peculiar calling convention (pointer to pointer to relation) * is needed so that we can use this routine as a hash table walker. * -------------------------------- */ static void RelationFlushRelation(Relation *relationPtr, - bool onlyFlushReferenceCountZero) + int skipLocalRelations) { Relation relation = *relationPtr; + bool rebuildIt; - /* - * Do nothing to transaction-local relations, since they cannot be - * subjects of SI notifications from other backends. - */ if (relation->rd_myxactonly) - return; + { + if (skipLocalRelations) + return; /* don't touch local rels if so commanded */ + /* + * Local rels should always be rebuilt, not flushed; the relcache + * entry must live until RelationPurgeLocalRelation(). + */ + rebuildIt = true; + } + else + { + /* + * Nonlocal rels can be dropped from the relcache if not open. + */ + rebuildIt = ! RelationHasReferenceCountZero(relation); + } - /* - * Zap it. Rebuild if it has nonzero ref count and we did not get - * the override flag. - */ - RelationClearRelation(relation, - (onlyFlushReferenceCountZero && - ! RelationHasReferenceCountZero(relation))); + RelationClearRelation(relation, rebuildIt); } /* -------------------------------- @@ -1374,20 +1531,15 @@ RelationForgetRelation(Oid rid) } /* -------------------------------- - * RelationRebuildRelation - - * - * Force a relcache entry to be rebuilt from catalog entries. - * This is needed, eg, after modifying an attribute of the rel. - * -------------------------------- - */ -void -RelationRebuildRelation(Relation relation) -{ - RelationClearRelation(relation, true); -} - -/* -------------------------------- * RelationIdInvalidateRelationCacheByRelationId + * + * This routine is invoked for SI cache flush messages. + * + * We used to skip local relations, on the grounds that they could + * not be targets of cross-backend SI update messages; but it seems + * safer to process them, so that our *own* SI update messages will + * have the same effects during CommandCounterIncrement for both + * local and nonlocal relations. * -------------------------------- */ void @@ -1397,36 +1549,8 @@ RelationIdInvalidateRelationCacheByRelationId(Oid relationId) RelationIdCacheLookup(relationId, relation); - /* - * "local" relations are invalidated by RelationPurgeLocalRelation. - * (This is to make LocalBufferSync's life easier: want the descriptor - * to hang around for a while. In fact, won't we want this for - * BufferSync also? But I'll leave it for now since I don't want to - * break anything.) - ay 3/95 - */ - if (PointerIsValid(relation) && !relation->rd_myxactonly) - { -#if 1 - /* - * Seems safest just to NEVER flush rels with positive refcounts. - * I think the code only had that proviso as a rather lame method of - * cleaning up unused relcache entries that had dangling refcounts - * (following elog(ERROR) with an open rel). Now we rely on - * RelationCacheAbort to clean up dangling refcounts, so there's no - * good reason to ever risk flushing a rel with positive refcount. - * IMHO anyway --- tgl 1/29/00. - */ - RelationFlushRelation(&relation, true); -#else - /* - * The boolean onlyFlushReferenceCountZero in RelationFlushReln() - * should be set to true when we are incrementing the command - * counter and to false when we are starting a new xaction. This - * can be determined by checking the current xaction status. - */ - RelationFlushRelation(&relation, CurrentXactInProgress()); -#endif - } + if (PointerIsValid(relation)) + RelationFlushRelation(&relation, false); } #if NOT_USED @@ -1448,7 +1572,7 @@ RelationFlushIndexes(Relation *r, if (relation->rd_rel->relkind == RELKIND_INDEX && /* XXX style */ (!OidIsValid(accessMethodId) || relation->rd_rel->relam == accessMethodId)) - RelationFlushRelation(&relation, true); + RelationFlushRelation(&relation, false); } #endif @@ -1477,37 +1601,19 @@ RelationIdInvalidateRelationCacheByAccessMethodId(Oid accessMethodId) /* * RelationCacheInvalidate - * - * Will blow away either all the cached relation descriptors or - * those that have a zero reference count. - * - * CAUTION: this is only called with onlyFlushReferenceCountZero=true - * at present, so that relation descriptors with positive refcounts - * are rebuilt rather than clobbered. It would only be safe to use a - * "false" parameter in a totally idle backend with no open relations. + * Blow away cached relation descriptors that have zero reference counts, + * and rebuild those with positive reference counts. * * This is currently used only to recover from SI message buffer overflow, - * so we do not blow away transaction-local relations; they cannot be - * targets of SI updates. + * so we do not touch transaction-local relations; they cannot be targets + * of cross-backend SI updates (and our own updates now go through a + * separate linked list that isn't limited by the SI message buffer size). */ void -RelationCacheInvalidate(bool onlyFlushReferenceCountZero) +RelationCacheInvalidate(void) { HashTableWalk(RelationNameCache, (HashtFunc) RelationFlushRelation, - onlyFlushReferenceCountZero); - - if (!onlyFlushReferenceCountZero) - { - /* - * Debugging check: what's left should be transaction-local relations - * plus nailed-in reldescs. There should be 6 hardwired heaps - * + 3 hardwired indices == 9 total. - */ - int numRels = length(newlyCreatedRelns) + 9; - - Assert(RelationNameCache->hctl->nkeys == numRels); - Assert(RelationIdCache->hctl->nkeys == numRels); - } + (int) true); } /* @@ -1672,8 +1778,6 @@ RelationInitialize(void) * initialize the cache with pre-made relation descriptors * for some of the more important system relations. These * relations should always be in the cache. - * - * NB: if you change this list, fix the count in RelationCacheInvalidate! * ---------------- */ formrdesc(RelationRelationName, Natts_pg_class, Desc_pg_class); @@ -2008,7 +2112,7 @@ init_irels(void) } /* oh, for god's sake... */ -#define SMD(i) strat[0].strategyMapData[i].entry[0] +#define SMD(i) strat->strategyMapData[i].entry[0] /* have to reinit the function pointers in the strategy maps */ for (i = 0; i < am->amstrategies * relform->relnatts; i++) @@ -2038,11 +2142,6 @@ init_irels(void) write_irels(); return; } - - /* - * p += sizeof(IndexStrategy); ((RegProcedure **) p) = support; - */ - ird->rd_support = support; RelationInitLockInfo(ird); @@ -2085,8 +2184,6 @@ write_irels(void) * relation searches -- a necessary step, since we're trying to * instantiate the index relation descriptors here. Once we have the * descriptors, nail them into cache so we never lose them. - * - * NB: if you change this list, fix the count in RelationCacheInvalidate! */ oldmode = GetProcessingMode(); |