#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().
1.7.1