PostgreSQL Source Code  git master
evtcache.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * evtcache.c
4  * Special-purpose cache for event trigger data.
5  *
6  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * IDENTIFICATION
10  * src/backend/utils/cache/evtcache.c
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15 
16 #include "access/genam.h"
17 #include "access/htup_details.h"
18 #include "access/relation.h"
20 #include "catalog/pg_type.h"
21 #include "commands/trigger.h"
22 #include "tcop/cmdtag.h"
23 #include "utils/array.h"
24 #include "utils/builtins.h"
25 #include "utils/catcache.h"
26 #include "utils/evtcache.h"
27 #include "utils/hsearch.h"
28 #include "utils/inval.h"
29 #include "utils/memutils.h"
30 #include "utils/rel.h"
31 #include "utils/snapmgr.h"
32 #include "utils/syscache.h"
33 
34 typedef enum
35 {
40 
41 typedef struct
42 {
46 
50 
51 static void BuildEventTriggerCache(void);
53  int cacheid, uint32 hashvalue);
55 
56 /*
57  * Search the event cache by trigger event.
58  *
59  * Note that the caller had better copy any data it wants to keep around
60  * across any operation that might touch a system catalog into some other
61  * memory context, since a cache reset could blow the return value away.
62  */
63 List *
65 {
67 
70  entry = hash_search(EventTriggerCache, &event, HASH_FIND, NULL);
71  return entry != NULL ? entry->triggerlist : NIL;
72 }
73 
74 /*
75  * Rebuild the event trigger cache.
76  */
77 static void
79 {
80  HASHCTL ctl;
81  HTAB *cache;
82  MemoryContext oldcontext;
83  Relation rel;
84  Relation irel;
85  SysScanDesc scan;
86 
87  if (EventTriggerCacheContext != NULL)
88  {
89  /*
90  * Free up any memory already allocated in EventTriggerCacheContext.
91  * This can happen either because a previous rebuild failed, or
92  * because an invalidation happened before the rebuild was complete.
93  */
94  MemoryContextResetAndDeleteChildren(EventTriggerCacheContext);
95  }
96  else
97  {
98  /*
99  * This is our first time attempting to build the cache, so we need to
100  * set up the memory context and register a syscache callback to
101  * capture future invalidation events.
102  */
103  if (CacheMemoryContext == NULL)
105  EventTriggerCacheContext =
107  "EventTriggerCache",
111  (Datum) 0);
112  }
113 
114  /* Switch to correct memory context. */
115  oldcontext = MemoryContextSwitchTo(EventTriggerCacheContext);
116 
117  /* Prevent the memory context from being nuked while we're rebuilding. */
119 
120  /* Create new hash table. */
121  MemSet(&ctl, 0, sizeof(ctl));
122  ctl.keysize = sizeof(EventTriggerEvent);
123  ctl.entrysize = sizeof(EventTriggerCacheEntry);
125  cache = hash_create("Event Trigger Cache", 32, &ctl,
127 
128  /*
129  * Prepare to scan pg_event_trigger in name order.
130  */
131  rel = relation_open(EventTriggerRelationId, AccessShareLock);
133  scan = systable_beginscan_ordered(rel, irel, NULL, 0, NULL);
134 
135  /*
136  * Build a cache item for each pg_event_trigger tuple, and append each one
137  * to the appropriate cache entry.
138  */
139  for (;;)
140  {
141  HeapTuple tup;
143  char *evtevent;
144  EventTriggerEvent event;
145  EventTriggerCacheItem *item;
146  Datum evttags;
147  bool evttags_isnull;
148  EventTriggerCacheEntry *entry;
149  bool found;
150 
151  /* Get next tuple. */
153  if (!HeapTupleIsValid(tup))
154  break;
155 
156  /* Skip trigger if disabled. */
157  form = (Form_pg_event_trigger) GETSTRUCT(tup);
158  if (form->evtenabled == TRIGGER_DISABLED)
159  continue;
160 
161  /* Decode event name. */
162  evtevent = NameStr(form->evtevent);
163  if (strcmp(evtevent, "ddl_command_start") == 0)
164  event = EVT_DDLCommandStart;
165  else if (strcmp(evtevent, "ddl_command_end") == 0)
166  event = EVT_DDLCommandEnd;
167  else if (strcmp(evtevent, "sql_drop") == 0)
168  event = EVT_SQLDrop;
169  else if (strcmp(evtevent, "table_rewrite") == 0)
170  event = EVT_TableRewrite;
171  else
172  continue;
173 
174  /* Allocate new cache item. */
175  item = palloc0(sizeof(EventTriggerCacheItem));
176  item->fnoid = form->evtfoid;
177  item->enabled = form->evtenabled;
178 
179  /* Decode and sort tags array. */
180  evttags = heap_getattr(tup, Anum_pg_event_trigger_evttags,
181  RelationGetDescr(rel), &evttags_isnull);
182  if (!evttags_isnull)
183  item->tagset = DecodeTextArrayToBitmapset(evttags);
184 
185  /* Add to cache entry. */
186  entry = hash_search(cache, &event, HASH_ENTER, &found);
187  if (found)
188  entry->triggerlist = lappend(entry->triggerlist, item);
189  else
190  entry->triggerlist = list_make1(item);
191  }
192 
193  /* Done with pg_event_trigger scan. */
197 
198  /* Restore previous memory context. */
199  MemoryContextSwitchTo(oldcontext);
200 
201  /* Install new cache. */
202  EventTriggerCache = 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  */
212 }
213 
214 /*
215  * Decode text[] to a Bitmapset of CommandTags.
216  *
217  * We could avoid a bit of overhead here if we were willing to duplicate some
218  * of the logic from deconstruct_array, but it doesn't seem worth the code
219  * complexity.
220  */
221 static Bitmapset *
223 {
224  ArrayType *arr = DatumGetArrayTypeP(array);
225  Datum *elems;
226  Bitmapset *bms;
227  int i;
228  int nelems;
229 
230  if (ARR_NDIM(arr) != 1 || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != TEXTOID)
231  elog(ERROR, "expected 1-D text array");
232  deconstruct_array(arr, TEXTOID, -1, false, TYPALIGN_INT,
233  &elems, NULL, &nelems);
234 
235  for (bms = NULL, i = 0; i < nelems; ++i)
236  {
237  char *str = TextDatumGetCString(elems[i]);
238 
239  bms = bms_add_member(bms, GetCommandTagEnum(str));
240  pfree(str);
241  }
242 
243  pfree(elems);
244 
245  return bms;
246 }
247 
248 /*
249  * Flush all cache entries when pg_event_trigger is updated.
250  *
251  * This should be rare enough that we don't need to be very granular about
252  * it, so we just blow away everything, which also avoids the possibility of
253  * memory leaks.
254  */
255 static void
257 {
258  /*
259  * If the cache isn't valid, then there might be a rebuild in progress, so
260  * we can't immediately blow it away. But it's advantageous to do this
261  * when possible, so as to immediately free memory.
262  */
264  {
265  MemoryContextResetAndDeleteChildren(EventTriggerCacheContext);
266  EventTriggerCache = NULL;
267  }
268 
269  /* Mark cache for rebuild. */
271 }
#define NIL
Definition: pg_list.h:65
static void InvalidateEventCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
Definition: evtcache.c:256
#define AllocSetContextCreate
Definition: memutils.h:170
static HTAB * EventTriggerCache
Definition: evtcache.c:47
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
#define HASH_CONTEXT
Definition: hsearch.h:91
#define HASH_ELEM
Definition: hsearch.h:85
MemoryContext hcxt
Definition: hsearch.h:77
#define RelationGetDescr(relation)
Definition: rel.h:483
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define AccessShareLock
Definition: lockdefs.h:36
Size entrysize
Definition: hsearch.h:72
static MemoryContext EventTriggerCacheContext
Definition: evtcache.c:48
#define MemSet(start, val, len)
Definition: c.h:1004
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:919
HeapTuple systable_getnext_ordered(SysScanDesc sysscan, ScanDirection direction)
Definition: genam.c:681
EventTriggerEvent
Definition: evtcache.h:20
#define list_make1(x1)
Definition: pg_list.h:206
Definition: dynahash.c:218
void pfree(void *pointer)
Definition: mcxt.c:1057
#define ERROR
Definition: elog.h:43
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:48
static Bitmapset * DecodeTextArrayToBitmapset(Datum array)
Definition: evtcache.c:222
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:192
static void BuildEventTriggerCache(void)
Definition: evtcache.c:78
unsigned int uint32
Definition: c.h:429
#define ARR_HASNULL(a)
Definition: array.h:279
static EventTriggerCacheStateType EventTriggerCacheState
Definition: evtcache.c:49
List * lappend(List *list, void *datum)
Definition: list.c:321
#define heap_getattr(tup, attnum, tupleDesc, isnull)
Definition: htup_details.h:762
#define MemoryContextResetAndDeleteChildren(ctx)
Definition: memutils.h:67
#define HASH_BLOBS
Definition: hsearch.h:86
#define TextDatumGetCString(d)
Definition: builtins.h:87
void CacheRegisterSyscacheCallback(int cacheid, SyscacheCallbackFunction func, Datum arg)
Definition: inval.c:1434
void * palloc0(Size size)
Definition: mcxt.c:981
#define TRIGGER_DISABLED
Definition: trigger.h:151
HTAB * hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
Definition: dynahash.c:326
uintptr_t Datum
Definition: postgres.h:367
EventTriggerCacheStateType
Definition: evtcache.c:34
void systable_endscan_ordered(SysScanDesc sysscan)
Definition: genam.c:706
Size keysize
Definition: hsearch.h:71
#define EventTriggerNameIndexId
EventTriggerEvent event
Definition: evtcache.c:43
FormData_pg_event_trigger * Form_pg_event_trigger
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
void CreateCacheMemoryContext(void)
Definition: catcache.c:620
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:736
#define ARR_NDIM(a)
Definition: array.h:278
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:158
CommandTag GetCommandTagEnum(const char *commandname)
Definition: cmdtag.c:74
Bitmapset * tagset
Definition: evtcache.h:32
void deconstruct_array(ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3483
SysScanDesc systable_beginscan_ordered(Relation heapRelation, Relation indexRelation, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:616
#define elog(elevel,...)
Definition: elog.h:228
int i
List * EventCacheLookup(EventTriggerEvent event)
Definition: evtcache.c:64
#define NameStr(name)
Definition: c.h:677
void * arg
Definition: pg_list.h:50
#define ARR_ELEMTYPE(a)
Definition: array.h:280
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:132
MemoryContext CacheMemoryContext
Definition: mcxt.c:47
#define DatumGetArrayTypeP(X)
Definition: array.h:249