2929#include "utils/snapmgr.h"
3030#include "utils/syscache.h"
3131
32+ typedef enum
33+ {
34+ ETCS_NEEDS_REBUILD ,
35+ ETCS_REBUILD_STARTED ,
36+ ETCS_VALID
37+ }EventTriggerCacheStateType ;
38+
3239typedef struct
3340{
3441EventTriggerEvent event ;
@@ -37,6 +44,7 @@ typedef struct
3744
3845static HTAB * EventTriggerCache ;
3946static MemoryContext EventTriggerCacheContext ;
47+ static EventTriggerCacheStateType EventTriggerCacheState = ETCS_NEEDS_REBUILD ;
4048
4149static void BuildEventTriggerCache (void );
4250static void InvalidateEventCacheCallback (Datum arg ,
@@ -55,7 +63,7 @@ EventCacheLookup(EventTriggerEvent event)
5563{
5664EventTriggerCacheEntry * entry ;
5765
58- if (EventTriggerCache == NULL )
66+ if (EventTriggerCacheState != ETCS_VALID )
5967BuildEventTriggerCache ();
6068entry = hash_search (EventTriggerCache ,& event ,HASH_FIND ,NULL );
6169return entry != NULL ?entry -> triggerlist :NULL ;
@@ -77,12 +85,9 @@ BuildEventTriggerCache(void)
7785if (EventTriggerCacheContext != NULL )
7886{
7987/*
80- * The cache has been previously built, and subsequently invalidated,
81- * and now we're trying to rebuild it. Normally, there won't be
82- * anything in the context at this point, because the invalidation
83- * will have already reset it. But if the previous attempt to rebuild
84- * the cache failed, then there might be some junk lying around
85- * that we want to reclaim.
88+ * Free up any memory already allocated in EventTriggerCacheContext.
89+ * This can happen either because a previous rebuild failed, or
90+ * because an invalidation happened before the rebuild was complete.
8691 */
8792MemoryContextResetAndDeleteChildren (EventTriggerCacheContext );
8893}
@@ -109,12 +114,10 @@ BuildEventTriggerCache(void)
109114/* Switch to correct memory context. */
110115oldcontext = MemoryContextSwitchTo (EventTriggerCacheContext );
111116
112- /*
113- * Create a new hash table, but don't assign it to the global variable
114- * until it accurately represents the state of the catalogs, so that
115- * if we fail midway through this we won't end up with incorrect cache
116- * contents.
117- */
117+ /* Prevent the memory context from being nuked while we're rebuilding. */
118+ EventTriggerCacheState = ETCS_REBUILD_STARTED ;
119+
120+ /* Create new hash table. */
118121MemSet (& ctl ,0 ,sizeof (ctl ));
119122ctl .keysize = sizeof (EventTriggerEvent );
120123ctl .entrysize = sizeof (EventTriggerCacheEntry );
@@ -195,8 +198,17 @@ BuildEventTriggerCache(void)
195198/* Restore previous memory context. */
196199MemoryContextSwitchTo (oldcontext );
197200
198- /*Cache is now valid . */
201+ /*Install new cache . */
199202EventTriggerCache = cache ;
203+
204+ /*
205+ * If the cache has been invalidated since we entered this routine, we
206+ * still use and return the cache we just finished constructing, to avoid
207+ * infinite loops, but we leave the cache marked stale so that we'll
208+ * rebuild it again on next access. Otherwise, we mark the cache valid.
209+ */
210+ if (EventTriggerCacheState == ETCS_REBUILD_STARTED )
211+ EventTriggerCacheState = ETCS_VALID ;
200212}
201213
202214/*
@@ -238,6 +250,17 @@ DecodeTextArrayToCString(Datum array, char ***cstringp)
238250static void
239251InvalidateEventCacheCallback (Datum arg ,int cacheid ,uint32 hashvalue )
240252{
241- MemoryContextResetAndDeleteChildren (EventTriggerCacheContext );
242- EventTriggerCache = NULL ;
253+ /*
254+ * If the cache isn't valid, then there might be a rebuild in progress,
255+ * so we can't immediately blow it away. But it's advantageous to do
256+ * this when possible, so as to immediately free memory.
257+ */
258+ if (EventTriggerCacheState == ETCS_VALID )
259+ {
260+ MemoryContextResetAndDeleteChildren (EventTriggerCacheContext );
261+ EventTriggerCache = NULL ;
262+ }
263+
264+ /* Mark cache for rebuild. */
265+ EventTriggerCacheState = ETCS_NEEDS_REBUILD ;
243266}