#include "postgres.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/indexing.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
#include "utils/evtcache.h"
#include "utils/inval.h"
#include "utils/memutils.h"
#include "utils/hsearch.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
Go to the source code of this file.
Data Structures | |
struct | EventTriggerCacheEntry |
Enumerations | |
enum | EventTriggerCacheStateType { ETCS_NEEDS_REBUILD, ETCS_REBUILD_STARTED, ETCS_VALID } |
Functions | |
static void | BuildEventTriggerCache (void) |
static void | InvalidateEventCacheCallback (Datum arg, int cacheid, uint32 hashvalue) |
static int | DecodeTextArrayToCString (Datum array, char ***cstringp) |
List * | EventCacheLookup (EventTriggerEvent event) |
Variables | |
static HTAB * | EventTriggerCache |
static MemoryContext | EventTriggerCacheContext |
static EventTriggerCacheStateType | EventTriggerCacheState = ETCS_NEEDS_REBUILD |
Definition at line 34 of file evtcache.c.
{ ETCS_NEEDS_REBUILD, ETCS_REBUILD_STARTED, ETCS_VALID } EventTriggerCacheStateType;
static void BuildEventTriggerCache | ( | void | ) | [static] |
Definition at line 78 of file evtcache.c.
References AccessShareLock, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), Anum_pg_event_trigger_evttags, CacheMemoryContext, CacheRegisterSyscacheCallback(), CreateCacheMemoryContext(), DecodeTextArrayToCString(), EventTriggerCacheItem::enabled, HASHCTL::entrysize, ETCS_REBUILD_STARTED, EventTriggerCacheState, EventTriggerNameIndexId, EVENTTRIGGEROID, EventTriggerRelationId, EventTriggerCacheItem::fnoid, ForwardScanDirection, GetLatestSnapshot(), GETSTRUCT, HASHCTL::hash, HASH_CONTEXT, hash_create(), HASH_ELEM, HASH_ENTER, HASH_FUNCTION, hash_search(), HASHCTL::hcxt, heap_getattr, HeapTupleIsValid, index_close(), index_open(), InvalidateEventCacheCallback(), HASHCTL::keysize, lappend(), list_make1, MemoryContextResetAndDeleteChildren(), MemoryContextSwitchTo(), MemSet, NameStr, EventTriggerCacheItem::ntags, NULL, palloc0(), pg_qsort_strcmp(), qsort, relation_close(), relation_open(), RelationGetDescr, systable_beginscan_ordered(), systable_endscan_ordered(), systable_getnext_ordered(), EventTriggerCacheItem::tag, TRIGGER_DISABLED, and EventTriggerCacheEntry::triggerlist.
Referenced by EventCacheLookup().
{ HASHCTL ctl; HTAB *cache; MemoryContext oldcontext; Relation rel; Relation irel; SysScanDesc scan; if (EventTriggerCacheContext != NULL) { /* * Free up any memory already allocated in EventTriggerCacheContext. * This can happen either because a previous rebuild failed, or * because an invalidation happened before the rebuild was complete. */ MemoryContextResetAndDeleteChildren(EventTriggerCacheContext); } else { /* * This is our first time attempting to build the cache, so we need * to set up the memory context and register a syscache callback to * capture future invalidation events. */ if (CacheMemoryContext == NULL) CreateCacheMemoryContext(); EventTriggerCacheContext = AllocSetContextCreate(CacheMemoryContext, "EventTriggerCache", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); CacheRegisterSyscacheCallback(EVENTTRIGGEROID, InvalidateEventCacheCallback, (Datum) 0); } /* Switch to correct memory context. */ oldcontext = MemoryContextSwitchTo(EventTriggerCacheContext); /* Prevent the memory context from being nuked while we're rebuilding. */ EventTriggerCacheState = ETCS_REBUILD_STARTED; /* Create new hash table. */ MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(EventTriggerEvent); ctl.entrysize = sizeof(EventTriggerCacheEntry); ctl.hash = tag_hash; ctl.hcxt = EventTriggerCacheContext; cache = hash_create("Event Trigger Cache", 32, &ctl, HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT); /* * Prepare to scan pg_event_trigger in name order. We use an MVCC * snapshot to avoid getting inconsistent results if the table is * being concurrently updated. */ rel = relation_open(EventTriggerRelationId, AccessShareLock); irel = index_open(EventTriggerNameIndexId, AccessShareLock); scan = systable_beginscan_ordered(rel, irel, GetLatestSnapshot(), 0, NULL); /* * Build a cache item for each pg_event_trigger tuple, and append each * one to the appropriate cache entry. */ for (;;) { HeapTuple tup; Form_pg_event_trigger form; char *evtevent; EventTriggerEvent event; EventTriggerCacheItem *item; Datum evttags; bool evttags_isnull; EventTriggerCacheEntry *entry; bool found; /* Get next tuple. */ tup = systable_getnext_ordered(scan, ForwardScanDirection); if (!HeapTupleIsValid(tup)) break; /* Skip trigger if disabled. */ form = (Form_pg_event_trigger) GETSTRUCT(tup); if (form->evtenabled == TRIGGER_DISABLED) continue; /* Decode event name. */ evtevent = NameStr(form->evtevent); if (strcmp(evtevent, "ddl_command_start") == 0) event = EVT_DDLCommandStart; else if (strcmp(evtevent, "ddl_command_end") == 0) event = EVT_DDLCommandEnd; else if (strcmp(evtevent, "sql_drop") == 0) event = EVT_SQLDrop; else continue; /* Allocate new cache item. */ item = palloc0(sizeof(EventTriggerCacheItem)); item->fnoid = form->evtfoid; item->enabled = form->evtenabled; /* Decode and sort tags array. */ evttags = heap_getattr(tup, Anum_pg_event_trigger_evttags, RelationGetDescr(rel), &evttags_isnull); if (!evttags_isnull) { item->ntags = DecodeTextArrayToCString(evttags, &item->tag); qsort(item->tag, item->ntags, sizeof(char *), pg_qsort_strcmp); } /* Add to cache entry. */ entry = hash_search(cache, &event, HASH_ENTER, &found); if (found) entry->triggerlist = lappend(entry->triggerlist, item); else entry->triggerlist = list_make1(item); } /* Done with pg_event_trigger scan. */ systable_endscan_ordered(scan); index_close(irel, AccessShareLock); relation_close(rel, AccessShareLock); /* Restore previous memory context. */ MemoryContextSwitchTo(oldcontext); /* Install new cache. */ EventTriggerCache = cache; /* * If the cache has been invalidated since we entered this routine, we * still use and return the cache we just finished constructing, to avoid * infinite loops, but we leave the cache marked stale so that we'll * rebuild it again on next access. Otherwise, we mark the cache valid. */ if (EventTriggerCacheState == ETCS_REBUILD_STARTED) EventTriggerCacheState = ETCS_VALID; }
static int DecodeTextArrayToCString | ( | Datum | array, | |
char *** | cstringp | |||
) | [static] |
Definition at line 228 of file evtcache.c.
References ARR_ELEMTYPE, ARR_HASNULL, ARR_NDIM, DatumGetArrayTypeP, deconstruct_array(), elog, ERROR, i, NULL, palloc(), pfree(), TextDatumGetCString, and TEXTOID.
Referenced by BuildEventTriggerCache().
{ ArrayType *arr = DatumGetArrayTypeP(array); Datum *elems; char **cstring; int i; int nelems; if (ARR_NDIM(arr) != 1 || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != TEXTOID) elog(ERROR, "expected 1-D text array"); deconstruct_array(arr, TEXTOID, -1, false, 'i', &elems, NULL, &nelems); cstring = palloc(nelems * sizeof(char *)); for (i = 0; i < nelems; ++i) cstring[i] = TextDatumGetCString(elems[i]); pfree(elems); *cstringp = cstring; return nelems; }
List* EventCacheLookup | ( | EventTriggerEvent | event | ) |
Definition at line 64 of file evtcache.c.
References BuildEventTriggerCache(), ETCS_VALID, EventTriggerCacheState, HASH_FIND, hash_search(), NULL, and EventTriggerCacheEntry::triggerlist.
Referenced by EventTriggerCommonSetup(), and trackDroppedObjectsNeeded().
{ EventTriggerCacheEntry *entry; if (EventTriggerCacheState != ETCS_VALID) BuildEventTriggerCache(); entry = hash_search(EventTriggerCache, &event, HASH_FIND, NULL); return entry != NULL ? entry->triggerlist : NULL; }
Definition at line 257 of file evtcache.c.
References ETCS_VALID, EventTriggerCacheState, and MemoryContextResetAndDeleteChildren().
Referenced by BuildEventTriggerCache().
{ /* * If the cache isn't valid, then there might be a rebuild in progress, * so we can't immediately blow it away. But it's advantageous to do * this when possible, so as to immediately free memory. */ if (EventTriggerCacheState == ETCS_VALID) { MemoryContextResetAndDeleteChildren(EventTriggerCacheContext); EventTriggerCache = NULL; } /* Mark cache for rebuild. */ EventTriggerCacheState = ETCS_NEEDS_REBUILD; }
HTAB* EventTriggerCache [static] |
Definition at line 47 of file evtcache.c.
MemoryContext EventTriggerCacheContext [static] |
Definition at line 48 of file evtcache.c.
EventTriggerCacheStateType EventTriggerCacheState = ETCS_NEEDS_REBUILD [static] |
Definition at line 49 of file evtcache.c.
Referenced by BuildEventTriggerCache(), EventCacheLookup(), and InvalidateEventCacheCallback().