diff options
Diffstat (limited to 'src/backend/utils/cache/attoptcache.c')
-rw-r--r-- | src/backend/utils/cache/attoptcache.c | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c new file mode 100644 index 0000000..9e252a0 --- /dev/null +++ b/src/backend/utils/cache/attoptcache.c @@ -0,0 +1,177 @@ +/*------------------------------------------------------------------------- + * + * attoptcache.c + * Attribute options cache management. + * + * Attribute options are cached separately from the fixed-size portion of + * pg_attribute entries, which are handled by the relcache. + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/utils/cache/attoptcache.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/reloptions.h" +#include "utils/attoptcache.h" +#include "utils/catcache.h" +#include "utils/hsearch.h" +#include "utils/inval.h" +#include "utils/syscache.h" + + +/* Hash table for information about each attribute's options */ +static HTAB *AttoptCacheHash = NULL; + +/* attrelid and attnum form the lookup key, and must appear first */ +typedef struct +{ + Oid attrelid; + int attnum; +} AttoptCacheKey; + +typedef struct +{ + AttoptCacheKey key; /* lookup key - must be first */ + AttributeOpts *opts; /* options, or NULL if none */ +} AttoptCacheEntry; + + +/* + * InvalidateAttoptCacheCallback + * Flush all cache entries when pg_attribute is updated. + * + * When pg_attribute is updated, we must flush the cache entry at least + * for that attribute. Currently, we just flush them all. Since attribute + * options are not currently used in performance-critical paths (such as + * query execution), this seems OK. + */ +static void +InvalidateAttoptCacheCallback(Datum arg, int cacheid, uint32 hashvalue) +{ + HASH_SEQ_STATUS status; + AttoptCacheEntry *attopt; + + hash_seq_init(&status, AttoptCacheHash); + while ((attopt = (AttoptCacheEntry *) hash_seq_search(&status)) != NULL) + { + if (attopt->opts) + pfree(attopt->opts); + if (hash_search(AttoptCacheHash, + (void *) &attopt->key, + HASH_REMOVE, + NULL) == NULL) + elog(ERROR, "hash table corrupted"); + } +} + +/* + * InitializeAttoptCache + * Initialize the attribute options cache. + */ +static void +InitializeAttoptCache(void) +{ + HASHCTL ctl; + + /* Initialize the hash table. */ + ctl.keysize = sizeof(AttoptCacheKey); + ctl.entrysize = sizeof(AttoptCacheEntry); + AttoptCacheHash = + hash_create("Attopt cache", 256, &ctl, + HASH_ELEM | HASH_BLOBS); + + /* Make sure we've initialized CacheMemoryContext. */ + if (!CacheMemoryContext) + CreateCacheMemoryContext(); + + /* Watch for invalidation events. */ + CacheRegisterSyscacheCallback(ATTNUM, + InvalidateAttoptCacheCallback, + (Datum) 0); +} + +/* + * get_attribute_options + * Fetch attribute options for a specified table OID. + */ +AttributeOpts * +get_attribute_options(Oid attrelid, int attnum) +{ + AttoptCacheKey key; + AttoptCacheEntry *attopt; + AttributeOpts *result; + HeapTuple tp; + + /* Find existing cache entry, if any. */ + if (!AttoptCacheHash) + InitializeAttoptCache(); + memset(&key, 0, sizeof(key)); /* make sure any padding bits are unset */ + key.attrelid = attrelid; + key.attnum = attnum; + attopt = + (AttoptCacheEntry *) hash_search(AttoptCacheHash, + (void *) &key, + HASH_FIND, + NULL); + + /* Not found in Attopt cache. Construct new cache entry. */ + if (!attopt) + { + AttributeOpts *opts; + + tp = SearchSysCache2(ATTNUM, + ObjectIdGetDatum(attrelid), + Int16GetDatum(attnum)); + + /* + * If we don't find a valid HeapTuple, it must mean someone has + * managed to request attribute details for a non-existent attribute. + * We treat that case as if no options were specified. + */ + if (!HeapTupleIsValid(tp)) + opts = NULL; + else + { + Datum datum; + bool isNull; + + datum = SysCacheGetAttr(ATTNUM, + tp, + Anum_pg_attribute_attoptions, + &isNull); + if (isNull) + opts = NULL; + else + { + bytea *bytea_opts = attribute_reloptions(datum, false); + + opts = MemoryContextAlloc(CacheMemoryContext, + VARSIZE(bytea_opts)); + memcpy(opts, bytea_opts, VARSIZE(bytea_opts)); + } + ReleaseSysCache(tp); + } + + /* + * It's important to create the actual cache entry only after reading + * pg_attribute, since the read could cause a cache flush. + */ + attopt = (AttoptCacheEntry *) hash_search(AttoptCacheHash, + (void *) &key, + HASH_ENTER, + NULL); + attopt->opts = opts; + } + + /* Return results in caller's memory context. */ + if (attopt->opts == NULL) + return NULL; + result = palloc(VARSIZE(attopt->opts)); + memcpy(result, attopt->opts, VARSIZE(attopt->opts)); + return result; +} |