00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include "postgres.h"
00015
00016 #include "access/genam.h"
00017 #include "access/heapam.h"
00018 #include "access/htup_details.h"
00019 #include "catalog/pg_event_trigger.h"
00020 #include "catalog/indexing.h"
00021 #include "catalog/pg_type.h"
00022 #include "commands/trigger.h"
00023 #include "utils/array.h"
00024 #include "utils/builtins.h"
00025 #include "utils/catcache.h"
00026 #include "utils/evtcache.h"
00027 #include "utils/inval.h"
00028 #include "utils/memutils.h"
00029 #include "utils/hsearch.h"
00030 #include "utils/rel.h"
00031 #include "utils/snapmgr.h"
00032 #include "utils/syscache.h"
00033
00034 typedef enum
00035 {
00036 ETCS_NEEDS_REBUILD,
00037 ETCS_REBUILD_STARTED,
00038 ETCS_VALID
00039 } EventTriggerCacheStateType;
00040
00041 typedef struct
00042 {
00043 EventTriggerEvent event;
00044 List *triggerlist;
00045 } EventTriggerCacheEntry;
00046
00047 static HTAB *EventTriggerCache;
00048 static MemoryContext EventTriggerCacheContext;
00049 static EventTriggerCacheStateType EventTriggerCacheState = ETCS_NEEDS_REBUILD;
00050
00051 static void BuildEventTriggerCache(void);
00052 static void InvalidateEventCacheCallback(Datum arg,
00053 int cacheid, uint32 hashvalue);
00054 static int DecodeTextArrayToCString(Datum array, char ***cstringp);
00055
00056
00057
00058
00059
00060
00061
00062
00063 List *
00064 EventCacheLookup(EventTriggerEvent event)
00065 {
00066 EventTriggerCacheEntry *entry;
00067
00068 if (EventTriggerCacheState != ETCS_VALID)
00069 BuildEventTriggerCache();
00070 entry = hash_search(EventTriggerCache, &event, HASH_FIND, NULL);
00071 return entry != NULL ? entry->triggerlist : NULL;
00072 }
00073
00074
00075
00076
00077 static void
00078 BuildEventTriggerCache(void)
00079 {
00080 HASHCTL ctl;
00081 HTAB *cache;
00082 MemoryContext oldcontext;
00083 Relation rel;
00084 Relation irel;
00085 SysScanDesc scan;
00086
00087 if (EventTriggerCacheContext != NULL)
00088 {
00089
00090
00091
00092
00093
00094 MemoryContextResetAndDeleteChildren(EventTriggerCacheContext);
00095 }
00096 else
00097 {
00098
00099
00100
00101
00102
00103 if (CacheMemoryContext == NULL)
00104 CreateCacheMemoryContext();
00105 EventTriggerCacheContext =
00106 AllocSetContextCreate(CacheMemoryContext,
00107 "EventTriggerCache",
00108 ALLOCSET_DEFAULT_MINSIZE,
00109 ALLOCSET_DEFAULT_INITSIZE,
00110 ALLOCSET_DEFAULT_MAXSIZE);
00111 CacheRegisterSyscacheCallback(EVENTTRIGGEROID,
00112 InvalidateEventCacheCallback,
00113 (Datum) 0);
00114 }
00115
00116
00117 oldcontext = MemoryContextSwitchTo(EventTriggerCacheContext);
00118
00119
00120 EventTriggerCacheState = ETCS_REBUILD_STARTED;
00121
00122
00123 MemSet(&ctl, 0, sizeof(ctl));
00124 ctl.keysize = sizeof(EventTriggerEvent);
00125 ctl.entrysize = sizeof(EventTriggerCacheEntry);
00126 ctl.hash = tag_hash;
00127 ctl.hcxt = EventTriggerCacheContext;
00128 cache = hash_create("Event Trigger Cache", 32, &ctl,
00129 HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
00130
00131
00132
00133
00134
00135
00136 rel = relation_open(EventTriggerRelationId, AccessShareLock);
00137 irel = index_open(EventTriggerNameIndexId, AccessShareLock);
00138 scan = systable_beginscan_ordered(rel, irel, GetLatestSnapshot(), 0, NULL);
00139
00140
00141
00142
00143
00144 for (;;)
00145 {
00146 HeapTuple tup;
00147 Form_pg_event_trigger form;
00148 char *evtevent;
00149 EventTriggerEvent event;
00150 EventTriggerCacheItem *item;
00151 Datum evttags;
00152 bool evttags_isnull;
00153 EventTriggerCacheEntry *entry;
00154 bool found;
00155
00156
00157 tup = systable_getnext_ordered(scan, ForwardScanDirection);
00158 if (!HeapTupleIsValid(tup))
00159 break;
00160
00161
00162 form = (Form_pg_event_trigger) GETSTRUCT(tup);
00163 if (form->evtenabled == TRIGGER_DISABLED)
00164 continue;
00165
00166
00167 evtevent = NameStr(form->evtevent);
00168 if (strcmp(evtevent, "ddl_command_start") == 0)
00169 event = EVT_DDLCommandStart;
00170 else if (strcmp(evtevent, "ddl_command_end") == 0)
00171 event = EVT_DDLCommandEnd;
00172 else if (strcmp(evtevent, "sql_drop") == 0)
00173 event = EVT_SQLDrop;
00174 else
00175 continue;
00176
00177
00178 item = palloc0(sizeof(EventTriggerCacheItem));
00179 item->fnoid = form->evtfoid;
00180 item->enabled = form->evtenabled;
00181
00182
00183 evttags = heap_getattr(tup, Anum_pg_event_trigger_evttags,
00184 RelationGetDescr(rel), &evttags_isnull);
00185 if (!evttags_isnull)
00186 {
00187 item->ntags = DecodeTextArrayToCString(evttags, &item->tag);
00188 qsort(item->tag, item->ntags, sizeof(char *), pg_qsort_strcmp);
00189 }
00190
00191
00192 entry = hash_search(cache, &event, HASH_ENTER, &found);
00193 if (found)
00194 entry->triggerlist = lappend(entry->triggerlist, item);
00195 else
00196 entry->triggerlist = list_make1(item);
00197 }
00198
00199
00200 systable_endscan_ordered(scan);
00201 index_close(irel, AccessShareLock);
00202 relation_close(rel, AccessShareLock);
00203
00204
00205 MemoryContextSwitchTo(oldcontext);
00206
00207
00208 EventTriggerCache = cache;
00209
00210
00211
00212
00213
00214
00215
00216 if (EventTriggerCacheState == ETCS_REBUILD_STARTED)
00217 EventTriggerCacheState = ETCS_VALID;
00218 }
00219
00220
00221
00222
00223
00224
00225
00226
00227 static int
00228 DecodeTextArrayToCString(Datum array, char ***cstringp)
00229 {
00230 ArrayType *arr = DatumGetArrayTypeP(array);
00231 Datum *elems;
00232 char **cstring;
00233 int i;
00234 int nelems;
00235
00236 if (ARR_NDIM(arr) != 1 || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != TEXTOID)
00237 elog(ERROR, "expected 1-D text array");
00238 deconstruct_array(arr, TEXTOID, -1, false, 'i', &elems, NULL, &nelems);
00239
00240 cstring = palloc(nelems * sizeof(char *));
00241 for (i = 0; i < nelems; ++i)
00242 cstring[i] = TextDatumGetCString(elems[i]);
00243
00244 pfree(elems);
00245 *cstringp = cstring;
00246 return nelems;
00247 }
00248
00249
00250
00251
00252
00253
00254
00255
00256 static void
00257 InvalidateEventCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
00258 {
00259
00260
00261
00262
00263
00264 if (EventTriggerCacheState == ETCS_VALID)
00265 {
00266 MemoryContextResetAndDeleteChildren(EventTriggerCacheContext);
00267 EventTriggerCache = NULL;
00268 }
00269
00270
00271 EventTriggerCacheState = ETCS_NEEDS_REBUILD;
00272 }