*
* 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-2012, 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 "knl/knl_variable.h"
#include "access/reloptions.h"
#include "utils/attoptcache.h"
#include "utils/hsearch.h"
#include "utils/inval.h"
#include "utils/syscache.h"
typedef struct {
Oid attrelid;
int attnum;
} AttoptCacheKey;
typedef struct {
AttoptCacheKey key;
AttributeOpts* opts;
} 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 = NULL;
hash_seq_init(&status, u_sess->cache_cxt.att_opt_cache_hash);
while ((attopt = (AttoptCacheEntry*)hash_seq_search(&status)) != NULL) {
if (attopt->opts != NULL) {
pfree_ext(attopt->opts);
}
if (hash_search(u_sess->cache_cxt.att_opt_cache_hash, (void*)&attopt->key, HASH_REMOVE, NULL) == NULL) {
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("hash table corrupted")));
}
}
}
* InitializeAttoptCache
* Initialize the tablespace cache.
*/
static void InitializeAttoptCache(void)
{
HASHCTL ctl;
errno_t rc;
rc = memset_s(&ctl, sizeof(ctl), 0, sizeof(ctl));
securec_check(rc, "", "");
ctl.keysize = sizeof(AttoptCacheKey);
ctl.entrysize = sizeof(AttoptCacheEntry);
ctl.hash = tag_hash;
ctl.hcxt = u_sess->cache_mem_cxt;
u_sess->cache_cxt.att_opt_cache_hash =
hash_create("Attopt cache", 256, &ctl, HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
CacheRegisterSessionSyscacheCallback(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 = NULL;
AttributeOpts* result = NULL;
HeapTuple tp;
errno_t rc = EOK;
if (!u_sess->cache_cxt.att_opt_cache_hash) {
InitializeAttoptCache();
}
rc = memset_s(&key, sizeof(key), 0, sizeof(key));
securec_check(rc, "\0", "\0");
key.attrelid = attrelid;
key.attnum = attnum;
attopt = (AttoptCacheEntry*)hash_search(u_sess->cache_cxt.att_opt_cache_hash, (void*)&key, HASH_FIND, NULL);
if (attopt == NULL) {
AttributeOpts* opts = NULL;
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 = false;
datum = SysCacheGetAttr(ATTNUM, tp, Anum_pg_attribute_attoptions, &isNull);
if (isNull) {
opts = NULL;
} else {
bytea* bytea_opts = attribute_reloptions(datum, false);
if (bytea_opts == NULL) {
ereport(ERROR, (errcode(ERRCODE_INVALID_OPTION), errmsg("Invalid attribute relation option.")));
}
opts = (AttributeOpts*)MemoryContextAlloc(u_sess->cache_mem_cxt, VARSIZE(bytea_opts));
rc = memcpy_s(opts, VARSIZE(bytea_opts), bytea_opts, VARSIZE(bytea_opts));
securec_check(rc, "\0", "\0");
}
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(u_sess->cache_cxt.att_opt_cache_hash, (void*)&key, HASH_ENTER, NULL);
attopt->opts = opts;
}
if (attopt->opts == NULL) {
return NULL;
}
result = (AttributeOpts*)palloc(VARSIZE(attopt->opts));
rc = memcpy_s(result, VARSIZE(attopt->opts), attopt->opts, VARSIZE(attopt->opts));
securec_check(rc, "\0", "\0");
return result;
}