]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/cache/typcache.c
Remove cvs keywords from all files.
[postgresql] / src / backend / utils / cache / typcache.c
index af03e2a7b0bdfcf1e95a2618d137823c3193bfde..200961448903b12cabb11b895629404ca0b3d90a 100644 (file)
@@ -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.
  * entry, since that may need to change as a consequence of ALTER TABLE.
  *
  *
- * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.17 2006/02/10 19:01:12 tgl Exp $
+ *       src/backend/utils/cache/typcache.c
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
-#include "access/heapam.h"
 #include "access/hash.h"
+#include "access/heapam.h"
 #include "access/nbtree.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "utils/builtins.h"
-#include "utils/catcache.h"
-#include "utils/hsearch.h"
+#include "utils/inval.h"
 #include "utils/lsyscache.h"
+#include "utils/rel.h"
 #include "utils/syscache.h"
 #include "utils/typcache.h"
 
@@ -87,6 +87,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
@@ -110,15 +112,19 @@ lookup_type_cache(Oid type_id, int flags)
                /* First time through: initialize the hash table */
                HASHCTL         ctl;
 
-               if (!CacheMemoryContext)
-                       CreateCacheMemoryContext();
-
                MemSet(&ctl, 0, sizeof(ctl));
                ctl.keysize = sizeof(Oid);
                ctl.entrysize = sizeof(TypeCacheEntry);
                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);
+
+               /* Also make sure CacheMemoryContext exists */
+               if (!CacheMemoryContext)
+                       CreateCacheMemoryContext();
        }
 
        /* Try to look up an existing entry */
@@ -135,9 +141,7 @@ lookup_type_cache(Oid type_id, int flags)
                HeapTuple       tp;
                Form_pg_type typtup;
 
-               tp = SearchSysCache(TYPEOID,
-                                                       ObjectIdGetDatum(type_id),
-                                                       0, 0, 0);
+               tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_id));
                if (!HeapTupleIsValid(tp))
                        elog(ERROR, "cache lookup failed for type %u", type_id);
                typtup = (Form_pg_type) GETSTRUCT(tp);
@@ -167,17 +171,30 @@ lookup_type_cache(Oid type_id, int flags)
        /* If we haven't already found the opclass, try to do so */
        if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_LT_OPR | TYPECACHE_GT_OPR |
                                  TYPECACHE_CMP_PROC |
-                                 TYPECACHE_EQ_OPR_FINFO | TYPECACHE_CMP_PROC_FINFO)) &&
-               typentry->btree_opc == InvalidOid)
+                                 TYPECACHE_EQ_OPR_FINFO | TYPECACHE_CMP_PROC_FINFO |
+                                 TYPECACHE_BTREE_OPFAMILY)) &&
+               typentry->btree_opf == InvalidOid)
        {
-               typentry->btree_opc = GetDefaultOpClass(type_id,
-                                                                                               BTREE_AM_OID);
+               Oid                     opclass;
+
+               opclass = GetDefaultOpClass(type_id, BTREE_AM_OID);
+               if (OidIsValid(opclass))
+               {
+                       typentry->btree_opf = get_opclass_family(opclass);
+                       typentry->btree_opintype = get_opclass_input_type(opclass);
+               }
                /* Only care about hash opclass if no btree opclass... */
-               if (typentry->btree_opc == InvalidOid)
+               if (typentry->btree_opf == InvalidOid)
                {
-                       if (typentry->hash_opc == InvalidOid)
-                               typentry->hash_opc = GetDefaultOpClass(type_id,
-                                                                                                          HASH_AM_OID);
+                       if (typentry->hash_opf == InvalidOid)
+                       {
+                               opclass = GetDefaultOpClass(type_id, HASH_AM_OID);
+                               if (OidIsValid(opclass))
+                               {
+                                       typentry->hash_opf = get_opclass_family(opclass);
+                                       typentry->hash_opintype = get_opclass_input_type(opclass);
+                               }
+                       }
                }
                else
                {
@@ -195,45 +212,50 @@ lookup_type_cache(Oid type_id, int flags)
        if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
                typentry->eq_opr == InvalidOid)
        {
-               if (typentry->btree_opc != InvalidOid)
-                       typentry->eq_opr = get_opclass_member(typentry->btree_opc,
-                                                                                                 InvalidOid,
-                                                                                                 BTEqualStrategyNumber);
+               if (typentry->btree_opf != InvalidOid)
+                       typentry->eq_opr = get_opfamily_member(typentry->btree_opf,
+                                                                                                  typentry->btree_opintype,
+                                                                                                  typentry->btree_opintype,
+                                                                                                  BTEqualStrategyNumber);
                if (typentry->eq_opr == InvalidOid &&
-                       typentry->hash_opc != InvalidOid)
-                       typentry->eq_opr = get_opclass_member(typentry->hash_opc,
-                                                                                                 InvalidOid,
-                                                                                                 HTEqualStrategyNumber);
+                       typentry->hash_opf != InvalidOid)
+                       typentry->eq_opr = get_opfamily_member(typentry->hash_opf,
+                                                                                                  typentry->hash_opintype,
+                                                                                                  typentry->hash_opintype,
+                                                                                                  HTEqualStrategyNumber);
        }
        if ((flags & TYPECACHE_LT_OPR) && typentry->lt_opr == InvalidOid)
        {
-               if (typentry->btree_opc != InvalidOid)
-                       typentry->lt_opr = get_opclass_member(typentry->btree_opc,
-                                                                                                 InvalidOid,
-                                                                                                 BTLessStrategyNumber);
+               if (typentry->btree_opf != InvalidOid)
+                       typentry->lt_opr = get_opfamily_member(typentry->btree_opf,
+                                                                                                  typentry->btree_opintype,
+                                                                                                  typentry->btree_opintype,
+                                                                                                  BTLessStrategyNumber);
        }
        if ((flags & TYPECACHE_GT_OPR) && typentry->gt_opr == InvalidOid)
        {
-               if (typentry->btree_opc != InvalidOid)
-                       typentry->gt_opr = get_opclass_member(typentry->btree_opc,
-                                                                                                 InvalidOid,
-                                                                                                 BTGreaterStrategyNumber);
+               if (typentry->btree_opf != InvalidOid)
+                       typentry->gt_opr = get_opfamily_member(typentry->btree_opf,
+                                                                                                  typentry->btree_opintype,
+                                                                                                  typentry->btree_opintype,
+                                                                                                  BTGreaterStrategyNumber);
        }
        if ((flags & (TYPECACHE_CMP_PROC | TYPECACHE_CMP_PROC_FINFO)) &&
                typentry->cmp_proc == InvalidOid)
        {
-               if (typentry->btree_opc != InvalidOid)
-                       typentry->cmp_proc = get_opclass_proc(typentry->btree_opc,
-                                                                                                 InvalidOid,
-                                                                                                 BTORDER_PROC);
+               if (typentry->btree_opf != InvalidOid)
+                       typentry->cmp_proc = get_opfamily_proc(typentry->btree_opf,
+                                                                                                  typentry->btree_opintype,
+                                                                                                  typentry->btree_opintype,
+                                                                                                  BTORDER_PROC);
        }
 
        /*
         * Set up fmgr lookup info as requested
         *
         * Note: we tell fmgr the finfo structures live in CacheMemoryContext,
-        * which is not quite right (they're really in DynaHashContext) but this
-        * will do for our purposes.
+        * which is not quite right (they're really in the hash table's private
+        * memory context) but this will do for our purposes.
         */
        if ((flags & TYPECACHE_EQ_OPR_FINFO) &&
                typentry->eq_opr_finfo.fn_oid == InvalidOid &&
@@ -259,7 +281,7 @@ lookup_type_cache(Oid type_id, int flags)
         */
        if ((flags & TYPECACHE_TUPDESC) &&
                typentry->tupDesc == NULL &&
-               typentry->typtype == 'c')
+               typentry->typtype == TYPTYPE_COMPOSITE)
        {
                Relation        rel;
 
@@ -270,12 +292,16 @@ lookup_type_cache(Oid type_id, int flags)
                Assert(rel->rd_rel->reltype == typentry->type_id);
 
                /*
-                * Notice that we simply store a link to the relcache's tupdesc. Since
-                * we are relying on relcache to detect cache flush events, there's
-                * not a lot of point to maintaining an independent copy.
+                * Link to the tupdesc and increment its refcount (we assert it's a
+                * refcounted descriptor).      We don't use IncrTupleDescRefCount() for
+                * this, because the reference mustn't be entered in the current
+                * resource owner; it can outlive the current query.
                 */
                typentry->tupDesc = RelationGetDescr(rel);
 
+               Assert(typentry->tupDesc->tdrefcount > 0);
+               typentry->tupDesc->tdrefcount++;
+
                relation_close(rel, AccessShareLock);
        }
 
@@ -283,29 +309,13 @@ lookup_type_cache(Oid type_id, int flags)
 }
 
 /*
- * lookup_rowtype_tupdesc
- *
- * Given a typeid/typmod that should describe a known composite type,
- * return the tuple descriptor for the type.  Will ereport on failure.
- *
- * Note: returned TupleDesc points to cached copy; caller must copy it
- * if intending to scribble on it or keep a reference for a long time.
- */
-TupleDesc
-lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
-{
-       return lookup_rowtype_tupdesc_noerror(type_id, typmod, false);
-}
-
-/*
- * lookup_rowtype_tupdesc_noerror
+ * lookup_rowtype_tupdesc_internal --- internal routine to lookup a rowtype
  *
- * As above, but if the type is not a known composite type and noError
- * is true, returns NULL instead of ereport'ing.  (Note that if a bogus
- * type_id is passed, you'll get an ereport anyway.)
+ * Same API as lookup_rowtype_tupdesc_noerror, but the returned tupdesc
+ * hasn't had its refcount bumped.
  */
-TupleDesc
-lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError)
+static TupleDesc
+lookup_rowtype_tupdesc_internal(Oid type_id, int32 typmod, bool noError)
 {
        if (type_id != RECORDOID)
        {
@@ -339,6 +349,59 @@ lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError)
        }
 }
 
+/*
+ * lookup_rowtype_tupdesc
+ *
+ * Given a typeid/typmod that should describe a known composite type,
+ * return the tuple descriptor for the type.  Will ereport on failure.
+ *
+ * Note: on success, we increment the refcount of the returned TupleDesc,
+ * and log the reference in CurrentResourceOwner.  Caller should call
+ * ReleaseTupleDesc or DecrTupleDescRefCount when done using the tupdesc.
+ */
+TupleDesc
+lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
+{
+       TupleDesc       tupDesc;
+
+       tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, false);
+       IncrTupleDescRefCount(tupDesc);
+       return tupDesc;
+}
+
+/*
+ * lookup_rowtype_tupdesc_noerror
+ *
+ * As above, but if the type is not a known composite type and noError
+ * is true, returns NULL instead of ereport'ing.  (Note that if a bogus
+ * type_id is passed, you'll get an ereport anyway.)
+ */
+TupleDesc
+lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError)
+{
+       TupleDesc       tupDesc;
+
+       tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, noError);
+       if (tupDesc != NULL)
+               IncrTupleDescRefCount(tupDesc);
+       return tupDesc;
+}
+
+/*
+ * lookup_rowtype_tupdesc_copy
+ *
+ * Like lookup_rowtype_tupdesc(), but the returned TupleDesc has been
+ * copied into the CurrentMemoryContext and is not reference-counted.
+ */
+TupleDesc
+lookup_rowtype_tupdesc_copy(Oid type_id, int32 typmod)
+{
+       TupleDesc       tmp;
+
+       tmp = lookup_rowtype_tupdesc_internal(type_id, typmod, false);
+       return CreateTupleDescCopyConstr(tmp);
+}
+
 
 /*
  * assign_record_type_typmod
@@ -366,15 +429,16 @@ assign_record_type_typmod(TupleDesc tupDesc)
                /* First time through: initialize the hash table */
                HASHCTL         ctl;
 
-               if (!CacheMemoryContext)
-                       CreateCacheMemoryContext();
-
                MemSet(&ctl, 0, sizeof(ctl));
                ctl.keysize = REC_HASH_KEYS * sizeof(Oid);
                ctl.entrysize = sizeof(RecordCacheEntry);
                ctl.hash = tag_hash;
                RecordCacheHash = hash_create("Record information cache", 64,
                                                                          &ctl, HASH_ELEM | HASH_FUNCTION);
+
+               /* Also make sure CacheMemoryContext exists */
+               if (!CacheMemoryContext)
+                       CreateCacheMemoryContext();
        }
 
        /* Find or create a hashtable entry for this hash class */
@@ -425,6 +489,8 @@ assign_record_type_typmod(TupleDesc tupDesc)
        /* if fail in subrs, no damage except possibly some wasted memory... */
        entDesc = CreateTupleDescCopy(tupDesc);
        recentry->tupdescs = lcons(entDesc, recentry->tupdescs);
+       /* mark it as a reference-counted tupdesc */
+       entDesc->tdrefcount = 1;
        /* now it's safe to advance NextRecordTypmod */
        newtypmod = NextRecordTypmod++;
        entDesc->tdtypmod = newtypmod;
@@ -437,25 +503,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 */
-
-       typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
-                                                                                         (void *) &type_id,
-                                                                                         HASH_FIND, NULL);
-       if (typentry == NULL)
-               return;                                 /* no matching 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->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;
+               }
+       }
 }