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-2025, 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/syscache.h"
32
33typedef enum
34{
39
40typedef struct
41{
45
49
50static void BuildEventTriggerCache(void);
52 int cacheid, uint32 hashvalue);
54
55/*
56 * Search the event cache by trigger event.
57 *
58 * Note that the caller had better copy any data it wants to keep around
59 * across any operation that might touch a system catalog into some other
60 * memory context, since a cache reset could blow the return value away.
61 */
62List *
64{
66
69 entry = hash_search(EventTriggerCache, &event, HASH_FIND, NULL);
70 return entry != NULL ? entry->triggerlist : NIL;
71}
72
73/*
74 * Rebuild the event trigger cache.
75 */
76static void
78{
80 HTAB *cache;
81 Relation rel;
82 Relation irel;
83 SysScanDesc scan;
84
85 if (EventTriggerCacheContext != NULL)
86 {
87 /*
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.
91 */
93 }
94 else
95 {
96 /*
97 * This is our first time attempting to build the cache, so we need to
98 * set up the memory context and register a syscache callback to
99 * capture future invalidation events.
100 */
101 if (CacheMemoryContext == NULL)
105 "EventTriggerCache",
107 CacheRegisterSyscacheCallback(EVENTTRIGGEROID,
109 (Datum) 0);
110 }
111
112 /* Prevent the memory context from being nuked while we're rebuilding. */
114
115 /* Create new hash table. */
116 ctl.keysize = sizeof(EventTriggerEvent);
117 ctl.entrysize = sizeof(EventTriggerCacheEntry);
119 cache = hash_create("EventTriggerCacheHash", 32, &ctl,
121
122 /*
123 * Prepare to scan pg_event_trigger in name order.
124 */
125 rel = relation_open(EventTriggerRelationId, AccessShareLock);
126 irel = index_open(EventTriggerNameIndexId, AccessShareLock);
127 scan = systable_beginscan_ordered(rel, irel, NULL, 0, NULL);
128
129 /*
130 * Build a cache item for each pg_event_trigger tuple, and append each one
131 * to the appropriate cache entry.
132 */
133 for (;;)
134 {
135 HeapTuple tup;
137 char *evtevent;
138 EventTriggerEvent event;
140 Datum evttags;
141 bool evttags_isnull;
143 bool found;
144 MemoryContext oldcontext;
145
146 /* Get next tuple. */
148 if (!HeapTupleIsValid(tup))
149 break;
150
151 /* Skip trigger if disabled. */
152 form = (Form_pg_event_trigger) GETSTRUCT(tup);
153 if (form->evtenabled == TRIGGER_DISABLED)
154 continue;
155
156 /* Decode event name. */
157 evtevent = NameStr(form->evtevent);
158 if (strcmp(evtevent, "ddl_command_start") == 0)
159 event = EVT_DDLCommandStart;
160 else if (strcmp(evtevent, "ddl_command_end") == 0)
161 event = EVT_DDLCommandEnd;
162 else if (strcmp(evtevent, "sql_drop") == 0)
163 event = EVT_SQLDrop;
164 else if (strcmp(evtevent, "table_rewrite") == 0)
165 event = EVT_TableRewrite;
166 else if (strcmp(evtevent, "login") == 0)
167 event = EVT_Login;
168 else
169 continue;
170
171 /* Switch to correct memory context. */
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 /* Restore previous memory context. */
193 MemoryContextSwitchTo(oldcontext);
194 }
195
196 /* Done with pg_event_trigger scan. */
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 */
221static 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_builtin(arr, TEXTOID, &elems, NULL, &nelems);
233
234 for (bms = NULL, i = 0; i < nelems; ++i)
235 {
236 char *str = TextDatumGetCString(elems[i]);
237
239 pfree(str);
240 }
241
242 pfree(elems);
243 if ((Pointer) arr != DatumGetPointer(array))
244 pfree(arr);
245
246 return bms;
247}
248
249/*
250 * Flush all cache entries when pg_event_trigger is updated.
251 *
252 * This should be rare enough that we don't need to be very granular about
253 * it, so we just blow away everything, which also avoids the possibility of
254 * memory leaks.
255 */
256static void
258{
259 /*
260 * If the cache isn't valid, then there might be a rebuild in progress, so
261 * we can't immediately blow it away. But it's advantageous to do this
262 * when possible, so as to immediately free memory.
263 */
265 {
267 EventTriggerCache = NULL;
268 }
269
270 /* Mark cache for rebuild. */
272}
#define ARR_NDIM(a)
Definition: array.h:290
#define DatumGetArrayTypeP(X)
Definition: array.h:261
#define ARR_ELEMTYPE(a)
Definition: array.h:292
#define ARR_HASNULL(a)
Definition: array.h:291
void deconstruct_array_builtin(const ArrayType *array, Oid elmtype, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3698
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:815
#define TextDatumGetCString(d)
Definition: builtins.h:98
#define NameStr(name)
Definition: c.h:756
char * Pointer
Definition: c.h:534
uint32_t uint32
Definition: c.h:543
void CreateCacheMemoryContext(void)
Definition: catcache.c:715
CommandTag GetCommandTagEnum(const char *commandname)
Definition: cmdtag.c:83
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:952
HTAB * hash_create(const char *tabname, int64 nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:358
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
List * EventCacheLookup(EventTriggerEvent event)
Definition: evtcache.c:63
static void BuildEventTriggerCache(void)
Definition: evtcache.c:77
static void InvalidateEventCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
Definition: evtcache.c:257
static MemoryContext EventTriggerCacheContext
Definition: evtcache.c:47
EventTriggerCacheStateType
Definition: evtcache.c:34
@ ETCS_REBUILD_STARTED
Definition: evtcache.c:36
@ ETCS_NEEDS_REBUILD
Definition: evtcache.c:35
@ ETCS_VALID
Definition: evtcache.c:37
static Bitmapset * DecodeTextArrayToBitmapset(Datum array)
Definition: evtcache.c:222
static HTAB * EventTriggerCache
Definition: evtcache.c:46
static EventTriggerCacheStateType EventTriggerCacheState
Definition: evtcache.c:48
EventTriggerEvent
Definition: evtcache.h:21
@ EVT_SQLDrop
Definition: evtcache.h:24
@ EVT_Login
Definition: evtcache.h:26
@ EVT_DDLCommandEnd
Definition: evtcache.h:23
@ EVT_DDLCommandStart
Definition: evtcache.h:22
@ EVT_TableRewrite
Definition: evtcache.h:25
SysScanDesc systable_beginscan_ordered(Relation heapRelation, Relation indexRelation, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:650
void systable_endscan_ordered(SysScanDesc sysscan)
Definition: genam.c:757
HeapTuple systable_getnext_ordered(SysScanDesc sysscan, ScanDirection direction)
Definition: genam.c:732
const char * str
@ HASH_FIND
Definition: hsearch.h:113
@ HASH_ENTER
Definition: hsearch.h:114
#define HASH_CONTEXT
Definition: hsearch.h:102
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_BLOBS
Definition: hsearch.h:97
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static Datum heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: htup_details.h:904
static void * GETSTRUCT(const HeapTupleData *tuple)
Definition: htup_details.h:728
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:177
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
void CacheRegisterSyscacheCallback(int cacheid, SyscacheCallbackFunction func, Datum arg)
Definition: inval.c:1812
int i
Definition: isn.c:77
List * lappend(List *list, void *datum)
Definition: list.c:339
#define AccessShareLock
Definition: lockdefs.h:36
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:400
void pfree(void *pointer)
Definition: mcxt.c:1594
void * palloc0(Size size)
Definition: mcxt.c:1395
MemoryContext CacheMemoryContext
Definition: mcxt.c:169
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
void * arg
FormData_pg_event_trigger * Form_pg_event_trigger
#define NIL
Definition: pg_list.h:68
#define list_make1(x1)
Definition: pg_list.h:212
uint64_t Datum
Definition: postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:322
tree ctl
Definition: radixtree.h:1838
#define RelationGetDescr(relation)
Definition: rel.h:541
@ ForwardScanDirection
Definition: sdir.h:28
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:205
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:47
EventTriggerEvent event
Definition: evtcache.c:42
Bitmapset * tagset
Definition: evtcache.h:33
Definition: dynahash.c:222
Definition: pg_list.h:54
#define TRIGGER_DISABLED
Definition: trigger.h:152