Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Enumerations | Functions | Variables

evtcache.c File Reference

#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"
Include dependency graph for evtcache.c:

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)
ListEventCacheLookup (EventTriggerEvent event)

Variables

static HTABEventTriggerCache
static MemoryContext EventTriggerCacheContext
static EventTriggerCacheStateType EventTriggerCacheState = ETCS_NEEDS_REBUILD

Enumeration Type Documentation

Enumerator:
ETCS_NEEDS_REBUILD 
ETCS_REBUILD_STARTED 
ETCS_VALID 

Definition at line 34 of file evtcache.c.

{
    ETCS_NEEDS_REBUILD,
    ETCS_REBUILD_STARTED,
    ETCS_VALID
} EventTriggerCacheStateType;


Function Documentation

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  ) 
static void InvalidateEventCacheCallback ( Datum  arg,
int  cacheid,
uint32  hashvalue 
) [static]

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;
}


Variable Documentation

Definition at line 47 of file evtcache.c.

Definition at line 48 of file evtcache.c.

EventTriggerCacheStateType EventTriggerCacheState = ETCS_NEEDS_REBUILD [static]