Fix for failure to clean SysCache entry when a relation is deleted
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 4 Jun 1999 02:19:47 +0000 (02:19 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 4 Jun 1999 02:19:47 +0000 (02:19 +0000)
in the same transaction that created it.

src/backend/catalog/heap.c
src/backend/catalog/index.c
src/backend/utils/cache/catcache.c
src/include/utils/catcache.h

index 1898f8f30878c8b0d043355129f5ad35b966a48a..349559cab6d36d62c5a0ae247e0c9502449df64e 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.86 1999/05/26 22:57:39 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.87 1999/06/04 02:19:46 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -812,13 +812,7 @@ heap_create_with_catalog(char *relname,
 
                if (relid != InvalidOid)
                {
-
-                       /*
-                        * This is heavy-handed, but appears necessary bjm 1999/02/01
-                        * SystemCacheRelationFlushed(relid) is not enough either.
-                        */
                        RelationForgetRelation(relid);
-                       ResetSystemCache();
                }
        }
 
index 0c8e9d77b789d299642dc81cee46a084f40b3653..22592e98bd1f38b3f023da9bb8918f6de14c5384 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.76 1999/05/26 22:57:39 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.77 1999/06/04 02:19:47 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -991,13 +991,7 @@ index_create(char *heapRelationName,
 
                if (relid != InvalidOid)
                {
-
-                       /*
-                        * This is heavy-handed, but appears necessary bjm 1999/02/01
-                        * SystemCacheRelationFlushed(relid) is not enough either.
-                        */
                        RelationForgetRelation(relid);
-                       ResetSystemCache();
                }
        }
 
index cc76acdd747a10933b215dad991e834ec2ddcf60..8224c935786e5cbc91b6c8a6fa0bf5217ba00ec8 100644 (file)
@@ -7,11 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.42 1999/05/31 23:48:04 tgl Exp $
- *
- * Notes:
- *             XXX This needs to use exception.h to handle recovery when
- *                             an abort occurs during DisableCache.
+ *       $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.43 1999/06/04 02:19:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -66,10 +62,11 @@ static long comphash(long l, char *v);
 #define CACHE6_elog(a,b,c,d,e,f,g)
 #endif
 
-CatCache   *Caches = NULL;
-GlobalMemory CacheCxt;
+static CatCache   *Caches = NULL; /* head of list of caches */
+
+GlobalMemory CacheCxt;                 /* context in which caches are allocated */
+/* CacheCxt is global because relcache uses it too. */
 
-static int     DisableCache;
 
 /* ----------------
  *             EQPROC is used in CatalogCacheInitializeCache
@@ -559,16 +556,7 @@ ResetSystemCache()
        MemoryContext oldcxt;
        struct catcache *cache;
 
-       /* ----------------
-        *      sanity checks
-        * ----------------
-        */
        CACHE1_elog(DEBUG, "ResetSystemCache called");
-       if (DisableCache)
-       {
-               elog(ERROR, "ResetSystemCache: Called while cache disabled");
-               return;
-       }
 
        /* ----------------
         *      first switch to the cache context so our allocations
@@ -602,11 +590,13 @@ ResetSystemCache()
                        {
                                nextelt = DLGetSucc(elt);
                                CatCacheRemoveCTup(cache, elt);
-                               if (cache->cc_ntup == -1)
-                                       elog(ERROR, "ResetSystemCache: cc_ntup<0 (software error)");
+                               if (cache->cc_ntup < 0)
+                                       elog(NOTICE,
+                                                "ResetSystemCache: cc_ntup<0 (software error)");
                        }
                }
                cache->cc_ntup = 0;             /* in case of WARN error above */
+               cache->busy = false;    /* to recover from recursive-use error */
        }
 
        CACHE1_elog(DEBUG, "end of ResetSystemCache call");
@@ -621,10 +611,18 @@ ResetSystemCache()
 /* --------------------------------
  *             SystemCacheRelationFlushed
  *
- *     RelationFlushRelation() frees some information referenced in the
- *     cache structures. So we get informed when this is done and arrange
- *     for the next SearchSysCache() call that this information is setup
- *     again.
+ *     This is called by RelationFlushRelation() to clear out cached information
+ *     about a relation being dropped.  (This could be a DROP TABLE command,
+ *     or a temp table being dropped at end of transaction, or a table created
+ *     during the current transaction that is being dropped because of abort.)
+ *     Remove all cache entries relevant to the specified relation OID.
+ *
+ *     A special case occurs when relId is itself one of the cacheable system
+ *     tables --- although those'll never be dropped, they can get flushed from
+ *     the relcache (VACUUM causes this, for example).  In that case we need to
+ *     force the next SearchSysCache() call to reinitialize the cache itself,
+ *     because we have info (such as cc_tupdesc) that is pointing at the about-
+ *     to-be-deleted relcache entry.
  * --------------------------------
  */
 void
@@ -632,6 +630,18 @@ SystemCacheRelationFlushed(Oid relId)
 {
        struct catcache *cache;
 
+       /*
+        * XXX Ideally we'd search the caches and just zap entries that actually
+        * refer to the indicated relation.  For now, we take the brute-force
+        * approach: just flush the caches entirely.
+        */
+       ResetSystemCache();
+
+       /*
+        * If relcache is dropping a system relation's cache entry, mark the
+        * associated cache structures invalid, so we can rebuild them from
+        * scratch (not just repopulate them) next time they are used.
+        */
        for (cache = Caches; PointerIsValid(cache); cache = cache->cc_next)
        {
                if (cache->relationId == relId)
@@ -746,6 +756,7 @@ InitSysCache(char *relname,
        cp->cc_indname = indname;
        cp->cc_tupdesc = (TupleDesc) NULL;
        cp->id = id;
+       cp->busy = false;
        cp->cc_maxtup = MAXTUP;
        cp->cc_size = NCCBUCK;
        cp->cc_nkeys = nkeys;
@@ -902,19 +913,23 @@ SearchSysCache(struct catcache * cache,
        /* ----------------
         *      Tuple was not found in cache, so we have to try and
         *      retrieve it directly from the relation.  If it's found,
-        *      we add it to the cache.  We must avoid recursion here,
-        *      so we disable cache operations.  If operations are
-        *      currently disabled and we couldn't find the requested item
-        *      in the cache, then this may be a recursive request, and we
-        *      abort with an error.
+        *      we add it to the cache.
+        *
+        *      To guard against possible infinite recursion, we mark this cache
+        *      "busy" while trying to load a new entry for it.  It is OK to
+        *      recursively invoke SearchSysCache for a different cache, but
+        *      a recursive call for the same cache will error out.  (We could
+        *      store the specific key(s) being looked for, and consider only
+        *      a recursive request for the same key to be an error, but this
+        *      simple scheme is sufficient for now.)
         * ----------------
         */
 
-       if (DisableCache)
+       if (cache->busy)
        {
-               elog(ERROR, "SearchSysCache: Called while cache disabled");
-               return (HeapTuple) NULL;
+               elog(ERROR, "SearchSysCache: recursive use of cache %d", cache->id);
        }
+       cache->busy = true;
 
        /* ----------------
         *      open the relation associated with the cache
@@ -925,10 +940,9 @@ SearchSysCache(struct catcache * cache,
                                RelationGetRelationName(relation));
 
        /* ----------------
-        *      DisableCache and then switch to the cache memory context.
+        *      Switch to the cache memory context.
         * ----------------
         */
-       DisableCache = 1;
 
        if (!CacheCxt)
                CacheCxt = CreateGlobalMemory("Cache");
@@ -1011,7 +1025,7 @@ SearchSysCache(struct catcache * cache,
                MemoryContextSwitchTo((MemoryContext) CacheCxt);
        }
 
-       DisableCache = 0;
+       cache->busy = false;
 
        /* ----------------
         *      scan is complete.  if tup is valid, we copy it and add the copy to
@@ -1046,7 +1060,8 @@ SearchSysCache(struct catcache * cache,
                DLAddHead(cache->cc_cache[hash], elt);
 
                /* ----------------
-                *      deal with hash bucket overflow
+                *      If we've exceeded the desired size of this cache,
+                *      throw away the least recently used entry.
                 * ----------------
                 */
                if (++cache->cc_ntup > cache->cc_maxtup)
@@ -1056,13 +1071,12 @@ SearchSysCache(struct catcache * cache,
                        elt = DLGetTail(cache->cc_lrulist);
                        ct = (CatCTup *) DLE_VAL(elt);
 
-                       if (ct != nct)
+                       if (ct != nct)          /* shouldn't be possible, but be safe... */
                        {
                                CACHE2_elog(DEBUG, "SearchSysCache(%s): Overflow, LRU removal",
                                                        RelationGetRelationName(relation));
 
                                CatCacheRemoveCTup(cache, elt);
-
                        }
                }
 
index aaf9156deca416fc4eda9681b08db2ed287f78c8..240deeb65fa9541fbc7a4a4ad452f29fb464993f 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catcache.h,v 1.14 1999/02/13 23:22:16 momjian Exp $
+ * $Id: catcache.h,v 1.15 1999/06/04 02:19:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 typedef struct catctup
 {
        HeapTuple       ct_tup;                 /* A pointer to a tuple                 */
-       Dlelem     *ct_node;            /* points to LRU list is the CatCTup is in
-                                                                * the cache, else, points to the cache if
-                                                                * the CatCTup is in LRU list */
+       /* Each tuple in the cache has two catctup items, one in the LRU list
+        * and one in the hashbucket list for its hash value.  ct_node in each
+        * one points to the other one.
+        */
+       Dlelem     *ct_node;            /* the other catctup for this tuple */
 } CatCTup;
 
 /* voodoo constants */
@@ -46,6 +48,7 @@ typedef struct catcache
        HeapTuple       (*cc_iscanfunc) ();             /* index scanfunction */
        TupleDesc       cc_tupdesc;             /* tuple descriptor from reldesc */
        int                     id;                             /* XXX could be improved -hirohama */
+       bool            busy;                   /* for detecting recursive lookups */
        short           cc_ntup;                /* # of tuples in this cache    */
        short           cc_maxtup;              /* max # of tuples allowed (LRU) */
        short           cc_nkeys;
@@ -55,12 +58,11 @@ typedef struct catcache
        ScanKeyData cc_skey[4];
        struct catcache *cc_next;
        Dllist     *cc_lrulist;         /* LRU list, most recent first */
-       Dllist     *cc_cache[NCCBUCK + 1];
+       Dllist     *cc_cache[NCCBUCK + 1]; /* hash buckets */
 } CatCache;
 
 #define InvalidCatalogCacheId  (-1)
 
-extern struct catcache *Caches;
 extern GlobalMemory CacheCxt;
 
 extern void CatalogCacheIdInvalidate(int cacheId, Index hashIndex,