*
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.312 2010/08/13 20:10:52 rhaas Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.313 2010/09/02 03:16:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 #include "utils/tqual.h"
-#include "utils/typcache.h"
 
 
 /*
 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.
         */
        if (!rebuild)
        {
-               /* Flush any rowtype cache entry */
-               flush_rowtype_cache(old_reltype);
-
                /* Remove it from the hash table */
                RelationCacheDelete(relation);
 
                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);
 
                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
 
  *       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.
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.32 2010/02/14 18:42:17 rhaas Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.33 2010/09/02 03:16:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "utils/builtins.h"
+#include "utils/inval.h"
 #include "utils/lsyscache.h"
 #include "utils/rel.h"
 #include "utils/syscache.h"
 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
                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);
+
                /* Also make sure CacheMemoryContext exists */
                if (!CacheMemoryContext)
                        CreateCacheMemoryContext();
 }
 
 /*
- * flush_rowtype_cache
+ * TypeCacheRelCallback
+ *             Relcache inval callback function
+ *
+ * Delete the cached tuple descriptor (if any) for the given rel's composite
+ * type, or for all composite types if relid == InvalidOid.
  *
- * 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.
+ * 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 */
-
-       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);
+       /* 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->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;
+               }
+       }
 }