diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index ee6eef1a02..a196ca7a09 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.266.2.9 2010/04/14 21:31:27 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.266.2.10 2010/09/02 03:17:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -65,7 +65,6 @@ #include "utils/relcache.h" #include "utils/resowner.h" #include "utils/syscache.h" -#include "utils/typcache.h" /* @@ -1774,8 +1773,6 @@ RelationDestroyRelation(Relation relation) static void RelationClearRelation(Relation relation, bool rebuild) { - Oid old_reltype = relation->rd_rel->reltype; - /* * As per notes above, a rel to be rebuilt MUST have refcnt > 0; while * of course it would be a bad idea to blow away one with nonzero refcnt. @@ -1851,9 +1848,6 @@ RelationClearRelation(Relation relation, bool rebuild) */ if (!rebuild) { - /* Flush any rowtype cache entry */ - flush_rowtype_cache(old_reltype); - /* Remove it from the hash table */ RelationCacheDelete(relation); @@ -1901,7 +1895,6 @@ RelationClearRelation(Relation relation, bool rebuild) if (newrel == NULL) { /* Should only get here if relation was deleted */ - flush_rowtype_cache(old_reltype); RelationCacheDelete(relation); RelationDestroyRelation(relation); elog(ERROR, "relation %u deleted while still in use", save_relid); @@ -1909,8 +1902,6 @@ RelationClearRelation(Relation relation, bool rebuild) keep_tupdesc = equalTupleDescs(relation->rd_att, newrel->rd_att); keep_rules = equalRuleLocks(relation->rd_rules, newrel->rd_rules); - if (!keep_tupdesc) - flush_rowtype_cache(old_reltype); /* * Perform swapping of the relcache entry contents. Within this diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c index 8f92ed48a3..d56d3e9062 100644 --- a/src/backend/utils/cache/typcache.c +++ b/src/backend/utils/cache/typcache.c @@ -4,14 +4,14 @@ * POSTGRES type cache code * * The type cache exists to speed lookup of certain information about data - * types that is not directly available from a type's pg_type row. In - * particular, we use a type's default btree opclass, or the default hash + * types that is not directly available from a type's pg_type row. For + * example, we use a type's default btree opclass, or the default hash * opclass if no btree opclass exists, to determine which operators should * be used for grouping and sorting the type (GROUP BY, ORDER BY ASC/DESC). * * Several seemingly-odd choices have been made to support use of the type * cache by the generic array comparison routines array_eq() and array_cmp(). - * Because these routines are used as index support operations, they cannot + * Because those routines are used as index support operations, they cannot * leak memory. To allow them to execute efficiently, all information that * either of them would like to re-use across calls is made available in the * type cache. @@ -36,7 +36,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.27 2008/01/01 19:45:53 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.27.2.1 2010/09/02 03:17:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -48,6 +48,7 @@ #include "catalog/pg_type.h" #include "commands/defrem.h" #include "utils/builtins.h" +#include "utils/inval.h" #include "utils/lsyscache.h" #include "utils/syscache.h" #include "utils/typcache.h" @@ -85,6 +86,8 @@ static TupleDesc *RecordCacheArray = NULL; static int32 RecordCacheArrayLen = 0; /* allocated length of array */ static int32 NextRecordTypmod = 0; /* number of entries used */ +static void TypeCacheRelCallback(Datum arg, Oid relid); + /* * lookup_type_cache @@ -117,6 +120,9 @@ lookup_type_cache(Oid type_id, int flags) ctl.hash = oid_hash; TypeCacheHash = hash_create("Type information cache", 64, &ctl, HASH_ELEM | HASH_FUNCTION); + + /* Also set up a callback for relcache SI invalidations */ + CacheRegisterRelcacheCallback(TypeCacheRelCallback, (Datum) 0); } /* Try to look up an existing entry */ @@ -496,36 +502,51 @@ assign_record_type_typmod(TupleDesc tupDesc) } /* - * flush_rowtype_cache + * TypeCacheRelCallback + * Relcache inval callback function * - * If a typcache entry exists for a rowtype, delete the entry's cached - * tuple descriptor link. This is called from relcache.c when a cached - * relation tupdesc is about to be dropped. + * Delete the cached tuple descriptor (if any) for the given rel's composite + * type, or for all composite types if relid == InvalidOid. + * + * This is called when a relcache invalidation event occurs for the given + * relid. We must scan the whole typcache hash since we don't know the + * type OID corresponding to the relid. We could do a direct search if this + * were a syscache-flush callback on pg_type, but then we would need all + * ALTER-TABLE-like commands that could modify a rowtype to issue syscache + * invals against the rel's pg_type OID. The extra SI signaling could very + * well cost more than we'd save, since in most usages there are not very + * many entries in a backend's typcache. The risk of bugs-of-omission seems + * high, too. + * + * Another possibility, with only localized impact, is to maintain a second + * hashtable that indexes composite-type typcache entries by their typrelid. + * But it's still not clear it's worth the trouble. */ -void -flush_rowtype_cache(Oid type_id) +static void +TypeCacheRelCallback(Datum arg, Oid relid) { + HASH_SEQ_STATUS status; TypeCacheEntry *typentry; - if (TypeCacheHash == NULL) - return; /* no table, so certainly no entry */ + /* TypeCacheHash must exist, else this callback wouldn't be registered */ + hash_seq_init(&status, TypeCacheHash); + while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL) + { + if (typentry->tupDesc == NULL) + continue; /* not composite, or tupdesc hasn't been requested */ - typentry = (TypeCacheEntry *) hash_search(TypeCacheHash, - (void *) &type_id, - HASH_FIND, NULL); - if (typentry == NULL) - return; /* no matching entry */ - if (typentry->tupDesc == NULL) - return; /* tupdesc hasn't been requested */ - - /* - * Release our refcount and free the tupdesc if none remain. (Can't use - * DecrTupleDescRefCount because this reference is not logged in current - * resource owner.) - */ - Assert(typentry->tupDesc->tdrefcount > 0); - if (--typentry->tupDesc->tdrefcount == 0) - FreeTupleDesc(typentry->tupDesc); - - typentry->tupDesc = NULL; + /* Delete if match, or if we're zapping all composite types */ + if (relid == typentry->typrelid || relid == InvalidOid) + { + /* + * Release our refcount, and free the tupdesc if none remain. + * (Can't use DecrTupleDescRefCount because this reference is not + * logged in current resource owner.) + */ + Assert(typentry->tupDesc->tdrefcount > 0); + if (--typentry->tupDesc->tdrefcount == 0) + FreeTupleDesc(typentry->tupDesc); + typentry->tupDesc = NULL; + } + } } diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h index 5807b428ca..4ec97beb62 100644 --- a/src/include/utils/typcache.h +++ b/src/include/utils/typcache.h @@ -9,7 +9,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/typcache.h,v 1.16 2008/01/01 19:45:59 momjian Exp $ + * $PostgreSQL: pgsql/src/include/utils/typcache.h,v 1.16.2.1 2010/09/02 03:17:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -86,6 +86,4 @@ extern TupleDesc lookup_rowtype_tupdesc_copy(Oid type_id, int32 typmod); extern void assign_record_type_typmod(TupleDesc tupDesc); -extern void flush_rowtype_cache(Oid type_id); - #endif /* TYPCACHE_H */