/*
|
* Licensed to the Apache Software Foundation (ASF) under one
|
* or more contributor license agreements. See the NOTICE file
|
* distributed with this work for additional information
|
* regarding copyright ownership. The ASF licenses this file
|
* to you under the Apache License, Version 2.0 (the
|
* "License"); you may not use this file except in compliance
|
* with the License. You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing,
|
* software distributed under the License is distributed on an
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* KIND, either express or implied. See the License for the
|
* specific language governing permissions and limitations
|
* under the License.
|
*/
|
|
#include "postgres.h"
|
|
#include "access/genam.h"
|
#include "access/heapam.h"
|
#include "catalog/pg_collation.h"
|
#include "utils/builtins.h"
|
#include "utils/catcache.h"
|
#include "utils/inval.h"
|
|
#include "catalog/ag_graph.h"
|
#include "catalog/ag_label.h"
|
#include "utils/ag_cache.h"
|
|
typedef struct graph_name_cache_entry
|
{
|
NameData name; // hash key
|
graph_cache_data data;
|
} graph_name_cache_entry;
|
|
typedef struct graph_namespace_cache_entry
|
{
|
Oid namespace; // hash key
|
graph_cache_data data;
|
} graph_namespace_cache_entry;
|
|
typedef struct label_name_graph_cache_key
|
{
|
NameData name;
|
Oid graph;
|
} label_name_graph_cache_key;
|
|
typedef struct label_name_graph_cache_entry
|
{
|
label_name_graph_cache_key key; // hash key
|
label_cache_data data;
|
} label_name_graph_cache_entry;
|
|
typedef struct label_graph_oid_cache_key
|
{
|
Oid graph;
|
int32 id;
|
} label_graph_oid_cache_key;
|
|
typedef struct label_graph_oid_cache_entry
|
{
|
label_graph_oid_cache_key key; // hash key
|
label_cache_data data;
|
} label_graph_oid_cache_entry;
|
|
typedef struct label_relation_cache_entry
|
{
|
Oid relation; // hash key
|
label_cache_data data;
|
} label_relation_cache_entry;
|
|
typedef struct label_seq_name_graph_cache_key
|
{
|
NameData name;
|
Oid graph;
|
} label_seq_name_graph_cache_key;
|
|
typedef struct label_seq_name_graph_cache_entry
|
{
|
label_seq_name_graph_cache_key key; // hash key
|
label_cache_data data;
|
} label_seq_name_graph_cache_entry;
|
|
// ag_graph.name
|
static HTAB *graph_name_cache_hash = NULL;
|
static ScanKeyData graph_name_scan_keys[1];
|
|
// ag_graph.namespace
|
static HTAB *graph_namespace_cache_hash = NULL;
|
static ScanKeyData graph_namespace_scan_keys[1];
|
|
// ag_label.name, ag_label.graph
|
static HTAB *label_name_graph_cache_hash = NULL;
|
static ScanKeyData label_name_graph_scan_keys[2];
|
|
// ag_label.graph, ag_label.id
|
static HTAB *label_graph_oid_cache_hash = NULL;
|
static ScanKeyData label_graph_oid_scan_keys[2];
|
|
// ag_label.relation
|
static HTAB *label_relation_cache_hash = NULL;
|
static ScanKeyData label_relation_scan_keys[1];
|
|
// ag_label.seq_name, ag_label.graph
|
static HTAB *label_seq_name_graph_cache_hash = NULL;
|
static ScanKeyData label_seq_name_graph_scan_keys[2];
|
|
// initialize all caches
|
static void initialize_caches(void);
|
|
// common
|
static void ag_cache_scan_key_init(ScanKey entry, AttrNumber attno,
|
RegProcedure func);
|
static int name_hash_compare(const void *key1, const void *key2, Size keysize);
|
|
// ag_graph
|
static void initialize_graph_caches(void);
|
static void create_graph_caches(void);
|
static void create_graph_name_cache(void);
|
static void create_graph_namespace_cache(void);
|
static void invalidate_graph_caches(Datum arg, int cache_id,
|
uint32 hash_value);
|
static void flush_graph_name_cache(void);
|
static void flush_graph_namespace_cache(void);
|
static graph_cache_data *search_graph_name_cache_miss(Name name);
|
static graph_cache_data *search_graph_namespace_cache_miss(Oid namespace);
|
static void fill_graph_cache_data(graph_cache_data *cache_data,
|
HeapTuple tuple, TupleDesc tuple_desc);
|
|
// ag_label
|
static void initialize_label_caches(void);
|
static void create_label_caches(void);
|
static void create_label_name_graph_cache(void);
|
static void create_label_graph_oid_cache(void);
|
static void create_label_relation_cache(void);
|
static void create_label_seq_name_graph_cache(void);
|
static void invalidate_label_caches(Datum arg, Oid relid);
|
static void invalidate_label_name_graph_cache(Oid relid);
|
static void flush_label_name_graph_cache(void);
|
static void invalidate_label_graph_oid_cache(Oid relid);
|
static void flush_label_graph_oid_cache(void);
|
static void invalidate_label_relation_cache(Oid relid);
|
static void flush_label_relation_cache(void);
|
static void invalidate_label_seq_name_graph_cache(Oid relid);
|
static void flush_label_seq_name_graph_cache(void);
|
|
static label_cache_data *search_label_name_graph_cache_miss(Name name,
|
Oid graph);
|
static void *label_name_graph_cache_hash_search(Name name, Oid graph,
|
HASHACTION action,
|
bool *found);
|
static label_cache_data *search_label_graph_oid_cache_miss(Oid graph,
|
uint32 id);
|
static void *label_graph_oid_cache_hash_search(uint32 graph, int32 id,
|
HASHACTION action, bool *found);
|
static label_cache_data *search_label_relation_cache_miss(Oid relation);
|
static label_cache_data *search_label_seq_name_graph_cache_miss(Name name,
|
Oid graph);
|
static void *label_seq_name_graph_cache_hash_search(Name name, Oid graph,
|
HASHACTION action,
|
bool *found);
|
|
static void fill_label_cache_data(label_cache_data *cache_data,
|
HeapTuple tuple, TupleDesc tuple_desc);
|
|
static void initialize_caches(void)
|
{
|
static bool initialized = false;
|
|
if (initialized)
|
{
|
return;
|
}
|
if (!CacheMemoryContext)
|
{
|
CreateCacheMemoryContext();
|
}
|
initialize_graph_caches();
|
initialize_label_caches();
|
|
initialized = true;
|
}
|
|
static void ag_cache_scan_key_init(ScanKey entry, AttrNumber attno,
|
RegProcedure func)
|
{
|
entry->sk_flags = 0;
|
entry->sk_attno = attno;
|
entry->sk_strategy = BTEqualStrategyNumber;
|
entry->sk_subtype = InvalidOid;
|
entry->sk_collation = C_COLLATION_OID;
|
fmgr_info_cxt(func, &entry->sk_func, CacheMemoryContext);
|
entry->sk_argument = (Datum)0;
|
}
|
|
static int name_hash_compare(const void *key1, const void *key2, Size keysize)
|
{
|
Name name1 = (Name)key1;
|
Name name2 = (Name)key2;
|
|
// keysize parameter is superfluous here
|
AssertArg(keysize == NAMEDATALEN);
|
|
return strncmp(NameStr(*name1), NameStr(*name2), NAMEDATALEN);
|
}
|
|
static void initialize_graph_caches(void)
|
{
|
// ag_graph.name
|
ag_cache_scan_key_init(&graph_name_scan_keys[0], Anum_ag_graph_name,
|
F_NAMEEQ);
|
|
// ag_graph.namespace
|
ag_cache_scan_key_init(&graph_namespace_scan_keys[0],
|
Anum_ag_graph_namespace, F_OIDEQ);
|
|
create_graph_caches();
|
|
/*
|
* A graph is backed by the bound namespace. So, register the invalidation
|
* logic of the graph caches for invalidation events of NAMESPACEOID cache.
|
*/
|
CacheRegisterSyscacheCallback(NAMESPACEOID, invalidate_graph_caches,
|
(Datum)0);
|
}
|
|
static void create_graph_caches(void)
|
{
|
/*
|
* All the hash tables are created using their dedicated memory contexts
|
* which are under TopMemoryContext.
|
*/
|
create_graph_name_cache();
|
create_graph_namespace_cache();
|
}
|
|
static void create_graph_name_cache(void)
|
{
|
HASHCTL hash_ctl;
|
|
MemSet(&hash_ctl, 0, sizeof(hash_ctl));
|
hash_ctl.keysize = sizeof(NameData);
|
hash_ctl.entrysize = sizeof(graph_name_cache_entry);
|
hash_ctl.match = name_hash_compare;
|
|
/*
|
* Please see the comment of hash_create() for the nelem value 16 here.
|
* HASH_BLOBS flag is set because the key for this hash is fixed-size.
|
*/
|
graph_name_cache_hash = hash_create("ag_graph (name) cache", 16, &hash_ctl,
|
HASH_ELEM | HASH_BLOBS | HASH_COMPARE);
|
}
|
|
static void create_graph_namespace_cache(void)
|
{
|
HASHCTL hash_ctl;
|
|
MemSet(&hash_ctl, 0, sizeof(hash_ctl));
|
hash_ctl.keysize = sizeof(Oid);
|
hash_ctl.entrysize = sizeof(graph_namespace_cache_entry);
|
|
/*
|
* Please see the comment of hash_create() for the nelem value 16 here.
|
* HASH_BLOBS flag is set because the size of the key is sizeof(uint32).
|
*/
|
graph_namespace_cache_hash = hash_create("ag_graph (namespace) cache", 16,
|
&hash_ctl,
|
HASH_ELEM | HASH_BLOBS);
|
}
|
|
static void invalidate_graph_caches(Datum arg, int cache_id, uint32 hash_value)
|
{
|
Assert(graph_name_cache_hash);
|
|
/*
|
* Currently, all entries in the graph caches are flushed because
|
* hash_value is for an entry in NAMESPACEOID cache. Since the caches
|
* are not currently used in performance-critical paths, this seems OK.
|
*/
|
flush_graph_name_cache();
|
flush_graph_namespace_cache();
|
}
|
|
static void flush_graph_name_cache(void)
|
{
|
HASH_SEQ_STATUS hash_seq;
|
|
hash_seq_init(&hash_seq, graph_name_cache_hash);
|
for (;;)
|
{
|
graph_name_cache_entry *entry;
|
void *removed;
|
|
entry = hash_seq_search(&hash_seq);
|
if (!entry)
|
{
|
break;
|
}
|
removed = hash_search(graph_name_cache_hash, &entry->name, HASH_REMOVE,
|
NULL);
|
if (!removed)
|
{
|
ereport(ERROR, (errmsg_internal("graph (name) cache corrupted")));
|
}
|
}
|
}
|
|
static void flush_graph_namespace_cache(void)
|
{
|
HASH_SEQ_STATUS hash_seq;
|
|
hash_seq_init(&hash_seq, graph_namespace_cache_hash);
|
for (;;)
|
{
|
graph_namespace_cache_entry *entry;
|
void *removed;
|
|
entry = hash_seq_search(&hash_seq);
|
if (!entry)
|
{
|
break;
|
}
|
|
removed = hash_search(graph_namespace_cache_hash, &entry->namespace,
|
HASH_REMOVE, NULL);
|
if (!removed)
|
{
|
ereport(ERROR,
|
(errmsg_internal("graph (namespace) cache corrupted")));
|
}
|
}
|
}
|
|
graph_cache_data *search_graph_name_cache(const char *name)
|
{
|
NameData name_key;
|
graph_name_cache_entry *entry;
|
|
AssertArg(name);
|
|
initialize_caches();
|
|
namestrcpy(&name_key, name);
|
entry = hash_search(graph_name_cache_hash, &name_key, HASH_FIND, NULL);
|
if (entry)
|
{
|
return &entry->data;
|
}
|
|
return search_graph_name_cache_miss(&name_key);
|
}
|
|
static graph_cache_data *search_graph_name_cache_miss(Name name)
|
{
|
ScanKeyData scan_keys[1];
|
Relation ag_graph;
|
SysScanDesc scan_desc;
|
HeapTuple tuple;
|
bool found;
|
graph_name_cache_entry *entry;
|
|
memcpy(scan_keys, graph_name_scan_keys, sizeof(graph_name_scan_keys));
|
scan_keys[0].sk_argument = NameGetDatum(name);
|
|
/*
|
* Calling table_open() might call AcceptInvalidationMessage() and that
|
* might flush the graph caches. This is OK because this function is called
|
* when the desired entry is not in the cache.
|
*/
|
ag_graph = table_open(ag_graph_relation_id(), AccessShareLock);
|
scan_desc = systable_beginscan(ag_graph, ag_graph_name_index_id(), true,
|
NULL, 1, scan_keys);
|
|
// don't need to loop over scan_desc because ag_graph_name_index is UNIQUE
|
tuple = systable_getnext(scan_desc);
|
if (!HeapTupleIsValid(tuple))
|
{
|
systable_endscan(scan_desc);
|
table_close(ag_graph, AccessShareLock);
|
|
return NULL;
|
}
|
|
// get a new entry
|
entry = hash_search(graph_name_cache_hash, name, HASH_ENTER, &found);
|
Assert(!found); // no concurrent update on graph_name_cache_hash
|
|
// fill the new entry with the retrieved tuple
|
fill_graph_cache_data(&entry->data, tuple, RelationGetDescr(ag_graph));
|
|
systable_endscan(scan_desc);
|
table_close(ag_graph, AccessShareLock);
|
|
return &entry->data;
|
}
|
|
graph_cache_data *search_graph_namespace_cache(Oid namespace)
|
{
|
graph_namespace_cache_entry *entry;
|
|
initialize_caches();
|
|
entry = hash_search(graph_namespace_cache_hash, &namespace, HASH_FIND,
|
NULL);
|
if (entry)
|
{
|
return &entry->data;
|
}
|
|
return search_graph_namespace_cache_miss(namespace);
|
}
|
|
static graph_cache_data *search_graph_namespace_cache_miss(Oid namespace)
|
{
|
ScanKeyData scan_keys[1];
|
Relation ag_graph;
|
SysScanDesc scan_desc;
|
HeapTuple tuple;
|
bool found;
|
graph_namespace_cache_entry *entry;
|
|
memcpy(scan_keys, graph_namespace_scan_keys,
|
sizeof(graph_namespace_scan_keys));
|
scan_keys[0].sk_argument = ObjectIdGetDatum(namespace);
|
|
/*
|
* Calling table_open() might call AcceptInvalidationMessage() and that
|
* might flush the graph caches. This is OK because this function is called
|
* when the desired entry is not in the cache.
|
*/
|
ag_graph = table_open(ag_graph_relation_id(), AccessShareLock);
|
scan_desc = systable_beginscan(ag_graph, ag_graph_namespace_index_id(),
|
true, NULL, 1, scan_keys);
|
|
// don't need to loop over scan_desc because ag_graph_namespace_index is
|
// UNIQUE
|
tuple = systable_getnext(scan_desc);
|
if (!HeapTupleIsValid(tuple))
|
{
|
systable_endscan(scan_desc);
|
table_close(ag_graph, AccessShareLock);
|
|
return NULL;
|
}
|
|
// get a new entry
|
entry = hash_search(graph_namespace_cache_hash, &namespace, HASH_ENTER,
|
&found);
|
Assert(!found); // no concurrent update on graph_namespace_cache_hash
|
|
// fill the new entry with the retrieved tuple
|
fill_graph_cache_data(&entry->data, tuple, RelationGetDescr(ag_graph));
|
|
systable_endscan(scan_desc);
|
table_close(ag_graph, AccessShareLock);
|
|
return &entry->data;
|
}
|
|
static void fill_graph_cache_data(graph_cache_data *cache_data,
|
HeapTuple tuple, TupleDesc tuple_desc)
|
{
|
bool is_null;
|
Datum value;
|
Name name;
|
|
// ag_graph.id
|
value = heap_getattr(tuple, Anum_ag_graph_oid, tuple_desc, &is_null);
|
Assert(!is_null);
|
cache_data->oid = DatumGetObjectId(value);
|
// ag_graph.name
|
value = heap_getattr(tuple, Anum_ag_graph_name, tuple_desc, &is_null);
|
Assert(!is_null);
|
name = DatumGetName(value);
|
Assert(name != NULL);
|
namestrcpy(&cache_data->name, name->data);
|
// ag_graph.namespace
|
value = heap_getattr(tuple, Anum_ag_graph_namespace, tuple_desc, &is_null);
|
Assert(!is_null);
|
cache_data->namespace = DatumGetObjectId(value);
|
}
|
|
static void initialize_label_caches(void)
|
{
|
// ag_label.name, ag_label.graph
|
ag_cache_scan_key_init(&label_name_graph_scan_keys[0], Anum_ag_label_name,
|
F_NAMEEQ);
|
ag_cache_scan_key_init(&label_name_graph_scan_keys[1], Anum_ag_label_graph,
|
F_INT4EQ);
|
|
// ag_label.graph, ag_label.id
|
ag_cache_scan_key_init(&label_graph_oid_scan_keys[0], Anum_ag_label_graph,
|
F_INT4EQ);
|
ag_cache_scan_key_init(&label_graph_oid_scan_keys[1], Anum_ag_label_id,
|
F_INT4EQ);
|
|
// ag_label.relation
|
ag_cache_scan_key_init(&label_relation_scan_keys[0],
|
Anum_ag_label_relation, F_OIDEQ);
|
|
// ag_label.seq_name, ag_label.graph
|
ag_cache_scan_key_init(&label_seq_name_graph_scan_keys[0], Anum_ag_label_seq_name,
|
F_NAMEEQ);
|
ag_cache_scan_key_init(&label_seq_name_graph_scan_keys[1], Anum_ag_label_graph,
|
F_OIDEQ);
|
|
// ag_label.seq_name, ag_label.graph
|
ag_cache_scan_key_init(&label_seq_name_graph_scan_keys[0],
|
Anum_ag_label_seq_name, F_NAMEEQ);
|
ag_cache_scan_key_init(&label_seq_name_graph_scan_keys[1],
|
Anum_ag_label_graph, F_OIDEQ);
|
|
// ag_label.seq_name, ag_label.graph
|
ag_cache_scan_key_init(&label_seq_name_graph_scan_keys[0],
|
Anum_ag_label_seq_name, F_NAMEEQ);
|
ag_cache_scan_key_init(&label_seq_name_graph_scan_keys[1],
|
Anum_ag_label_graph, F_OIDEQ);
|
|
create_label_caches();
|
|
/*
|
* A label is backed by the bound relation. So, register the invalidation
|
* logic of the label caches for invalidation events of relation cache.
|
*/
|
CacheRegisterRelcacheCallback(invalidate_label_caches, (Datum)0);
|
}
|
|
static void create_label_caches(void)
|
{
|
/*
|
* All the hash tables are created using their dedicated memory contexts
|
* which are under TopMemoryContext.
|
*/
|
create_label_name_graph_cache();
|
create_label_graph_oid_cache();
|
create_label_relation_cache();
|
create_label_seq_name_graph_cache();
|
}
|
|
static void create_label_name_graph_cache(void)
|
{
|
HASHCTL hash_ctl;
|
|
MemSet(&hash_ctl, 0, sizeof(hash_ctl));
|
hash_ctl.keysize = sizeof(label_name_graph_cache_key);
|
hash_ctl.entrysize = sizeof(label_name_graph_cache_entry);
|
|
/*
|
* Please see the comment of hash_create() for the nelem value 16 here.
|
* HASH_BLOBS flag is set because the key for this hash is fixed-size.
|
*/
|
label_name_graph_cache_hash = hash_create("ag_label (name, graph) cache",
|
16, &hash_ctl,
|
HASH_ELEM | HASH_BLOBS);
|
}
|
|
static void create_label_graph_oid_cache(void)
|
{
|
HASHCTL hash_ctl;
|
|
MemSet(&hash_ctl, 0, sizeof(hash_ctl));
|
hash_ctl.keysize = sizeof(label_graph_oid_cache_key);
|
hash_ctl.entrysize = sizeof(label_graph_oid_cache_entry);
|
|
/*
|
* Please see the comment of hash_create() for the nelem value 16 here.
|
* HASH_BLOBS flag is set because the key for this hash is fixed-size.
|
*/
|
label_graph_oid_cache_hash = hash_create("ag_label (graph, id) cache", 16,
|
&hash_ctl,
|
HASH_ELEM | HASH_BLOBS);
|
}
|
|
static void create_label_relation_cache(void)
|
{
|
HASHCTL hash_ctl;
|
|
MemSet(&hash_ctl, 0, sizeof(hash_ctl));
|
hash_ctl.keysize = sizeof(Oid);
|
hash_ctl.entrysize = sizeof(label_relation_cache_entry);
|
|
/*
|
* Please see the comment of hash_create() for the nelem value 16 here.
|
* HASH_BLOBS flag is set because the size of the key is sizeof(uint32).
|
*/
|
label_relation_cache_hash = hash_create("ag_label (relation) cache", 16,
|
&hash_ctl, HASH_ELEM | HASH_BLOBS);
|
}
|
|
static void create_label_seq_name_graph_cache(void)
|
{
|
HASHCTL hash_ctl;
|
|
MemSet(&hash_ctl, 0, sizeof(hash_ctl));
|
hash_ctl.keysize = sizeof(label_seq_name_graph_cache_key);
|
hash_ctl.entrysize = sizeof(label_seq_name_graph_cache_entry);
|
|
/*
|
* Please see the comment of hash_create() for the nelem value 16 here.
|
* HASH_BLOBS flag is set because the key for this hash is fixed-size.
|
*/
|
label_seq_name_graph_cache_hash = hash_create("ag_label (seq_name, graph) cache",
|
16, &hash_ctl,
|
HASH_ELEM | HASH_BLOBS);
|
}
|
|
static void invalidate_label_caches(Datum arg, Oid relid)
|
{
|
Assert(label_name_graph_cache_hash);
|
Assert(label_seq_name_graph_cache_hash);
|
|
|
if (OidIsValid(relid))
|
{
|
invalidate_label_name_graph_cache(relid);
|
invalidate_label_graph_oid_cache(relid);
|
invalidate_label_relation_cache(relid);
|
invalidate_label_seq_name_graph_cache(relid);
|
}
|
else
|
{
|
flush_label_name_graph_cache();
|
flush_label_graph_oid_cache();
|
flush_label_relation_cache();
|
flush_label_seq_name_graph_cache();
|
}
|
}
|
|
static void invalidate_label_name_graph_cache(Oid relid)
|
{
|
HASH_SEQ_STATUS hash_seq;
|
|
hash_seq_init(&hash_seq, label_name_graph_cache_hash);
|
for (;;)
|
{
|
label_name_graph_cache_entry *entry;
|
void *removed;
|
|
entry = hash_seq_search(&hash_seq);
|
if (!entry)
|
{
|
break;
|
}
|
if (entry->data.relation != relid)
|
{
|
continue;
|
}
|
removed = hash_search(label_name_graph_cache_hash, &entry->key,
|
HASH_REMOVE, NULL);
|
hash_seq_term(&hash_seq);
|
|
if (!removed)
|
{
|
ereport(ERROR,
|
(errmsg_internal("label (name, graph) cache corrupted")));
|
}
|
|
break;
|
}
|
}
|
|
static void flush_label_name_graph_cache(void)
|
{
|
HASH_SEQ_STATUS hash_seq;
|
|
hash_seq_init(&hash_seq, label_name_graph_cache_hash);
|
for (;;)
|
{
|
label_name_graph_cache_entry *entry;
|
void *removed;
|
|
entry = hash_seq_search(&hash_seq);
|
if (!entry)
|
{
|
break;
|
}
|
removed = hash_search(label_name_graph_cache_hash, &entry->key,
|
HASH_REMOVE, NULL);
|
if (!removed)
|
{
|
ereport(ERROR,
|
(errmsg_internal("label (name, graph) cache corrupted")));
|
}
|
}
|
}
|
|
static void invalidate_label_graph_oid_cache(Oid relid)
|
{
|
HASH_SEQ_STATUS hash_seq;
|
|
hash_seq_init(&hash_seq, label_graph_oid_cache_hash);
|
for (;;)
|
{
|
label_graph_oid_cache_entry *entry;
|
void *removed;
|
|
entry = hash_seq_search(&hash_seq);
|
if (!entry)
|
{
|
break;
|
}
|
if (entry->data.relation != relid)
|
{
|
continue;
|
}
|
removed = hash_search(label_graph_oid_cache_hash, &entry->key,
|
HASH_REMOVE, NULL);
|
hash_seq_term(&hash_seq);
|
|
if (!removed)
|
{
|
ereport(ERROR,
|
(errmsg_internal("label (graph, id) cache corrupted")));
|
}
|
|
break;
|
}
|
}
|
|
static void flush_label_graph_oid_cache(void)
|
{
|
HASH_SEQ_STATUS hash_seq;
|
|
hash_seq_init(&hash_seq, label_graph_oid_cache_hash);
|
for (;;)
|
{
|
label_graph_oid_cache_entry *entry;
|
void *removed;
|
|
entry = hash_seq_search(&hash_seq);
|
if (!entry)
|
{
|
break;
|
}
|
removed = hash_search(label_graph_oid_cache_hash, &entry->key,
|
HASH_REMOVE, NULL);
|
if (!removed)
|
{
|
ereport(ERROR,
|
(errmsg_internal("label (graph, id) cache corrupted")));
|
}
|
}
|
}
|
|
static void invalidate_label_relation_cache(Oid relid)
|
{
|
label_relation_cache_entry *entry;
|
void *removed;
|
|
entry = hash_search(label_relation_cache_hash, &relid, HASH_FIND, NULL);
|
if (!entry)
|
{
|
return;
|
}
|
removed = hash_search(label_relation_cache_hash, &relid, HASH_REMOVE,
|
NULL);
|
if (!removed)
|
{
|
ereport(ERROR, (errmsg_internal("label (namespace) cache corrupted")));
|
}
|
}
|
|
static void flush_label_relation_cache(void)
|
{
|
HASH_SEQ_STATUS hash_seq;
|
|
hash_seq_init(&hash_seq, label_relation_cache_hash);
|
for (;;)
|
{
|
label_relation_cache_entry *entry;
|
void *removed;
|
|
entry = hash_seq_search(&hash_seq);
|
if (!entry)
|
{
|
break;
|
}
|
removed = hash_search(label_relation_cache_hash, &entry->relation,
|
HASH_REMOVE, NULL);
|
if (!removed)
|
{
|
ereport(ERROR,
|
(errmsg_internal("label (relation) cache corrupted")));
|
}
|
}
|
}
|
|
static void invalidate_label_seq_name_graph_cache(Oid relid)
|
{
|
HASH_SEQ_STATUS hash_seq;
|
|
hash_seq_init(&hash_seq, label_seq_name_graph_cache_hash);
|
for (;;)
|
{
|
label_seq_name_graph_cache_entry *entry;
|
void *removed;
|
|
entry = hash_seq_search(&hash_seq);
|
if (!entry)
|
{
|
break;
|
}
|
if (entry->data.relation != relid)
|
{
|
continue;
|
}
|
removed = hash_search(label_seq_name_graph_cache_hash, &entry->key,
|
HASH_REMOVE, NULL);
|
hash_seq_term(&hash_seq);
|
|
if (!removed)
|
{
|
ereport(ERROR,
|
(errmsg_internal("label (seq_name, graph) cache corrupted")));
|
}
|
|
break;
|
}
|
}
|
|
static void flush_label_seq_name_graph_cache(void)
|
{
|
HASH_SEQ_STATUS hash_seq;
|
|
hash_seq_init(&hash_seq, label_seq_name_graph_cache_hash);
|
for (;;)
|
{
|
label_seq_name_graph_cache_entry *entry;
|
void *removed;
|
|
entry = hash_seq_search(&hash_seq);
|
if (!entry)
|
{
|
break;
|
}
|
removed = hash_search(label_seq_name_graph_cache_hash, &entry->key,
|
HASH_REMOVE, NULL);
|
if (!removed)
|
{
|
ereport(ERROR,
|
(errmsg_internal("label (seq_name, graph) cache corrupted")));
|
}
|
}
|
}
|
|
label_cache_data *search_label_name_graph_cache(const char *name, Oid graph)
|
{
|
NameData name_key;
|
label_name_graph_cache_entry *entry;
|
|
AssertArg(name);
|
|
initialize_caches();
|
|
namestrcpy(&name_key, name);
|
entry = label_name_graph_cache_hash_search(&name_key, graph, HASH_FIND,
|
NULL);
|
if (entry)
|
{
|
return &entry->data;
|
}
|
return search_label_name_graph_cache_miss(&name_key, graph);
|
}
|
|
static label_cache_data *search_label_name_graph_cache_miss(Name name,
|
Oid graph)
|
{
|
ScanKeyData scan_keys[2];
|
Relation ag_label;
|
SysScanDesc scan_desc;
|
HeapTuple tuple;
|
bool found;
|
label_name_graph_cache_entry *entry;
|
|
memcpy(scan_keys, label_name_graph_scan_keys,
|
sizeof(label_name_graph_scan_keys));
|
scan_keys[0].sk_argument = NameGetDatum(name);
|
scan_keys[1].sk_argument = ObjectIdGetDatum(graph);
|
|
/*
|
* Calling table_open() might call AcceptInvalidationMessage() and that
|
* might invalidate the label caches. This is OK because this function is
|
* called when the desired entry is not in the cache.
|
*/
|
ag_label = table_open(ag_label_relation_id(), AccessShareLock);
|
scan_desc = systable_beginscan(ag_label, ag_label_name_graph_index_id(),
|
true, NULL, 2, scan_keys);
|
|
/*
|
* don't need to loop over scan_desc because ag_label_name_graph_index is
|
* UNIQUE
|
*/
|
tuple = systable_getnext(scan_desc);
|
if (!HeapTupleIsValid(tuple))
|
{
|
systable_endscan(scan_desc);
|
table_close(ag_label, AccessShareLock);
|
|
return NULL;
|
}
|
|
// get a new entry
|
entry = label_name_graph_cache_hash_search(name, graph, HASH_ENTER,
|
&found);
|
Assert(!found); // no concurrent update on label_name_graph_cache_hash
|
|
// fill the new entry with the retrieved tuple
|
fill_label_cache_data(&entry->data, tuple, RelationGetDescr(ag_label));
|
|
systable_endscan(scan_desc);
|
table_close(ag_label, AccessShareLock);
|
|
return &entry->data;
|
}
|
|
static void *label_name_graph_cache_hash_search(Name name, Oid graph,
|
HASHACTION action, bool *found)
|
{
|
label_name_graph_cache_key key;
|
|
// initialize the hash key for label_name_graph_cache_hash
|
namestrcpy(&key.name, name->data);
|
key.graph = graph;
|
|
return hash_search(label_name_graph_cache_hash, &key, action, found);
|
}
|
|
label_cache_data *search_label_graph_oid_cache(uint32 graph_oid, int32 id)
|
{
|
label_graph_oid_cache_entry *entry;
|
|
AssertArg(label_id_is_valid(id));
|
|
initialize_caches();
|
|
entry = label_graph_oid_cache_hash_search(graph_oid, id, HASH_FIND, NULL);
|
if (entry)
|
{
|
return &entry->data;
|
}
|
return search_label_graph_oid_cache_miss(graph_oid, id);
|
}
|
|
static label_cache_data *search_label_graph_oid_cache_miss(Oid graph, uint32 id)
|
{
|
ScanKeyData scan_keys[2];
|
Relation ag_label;
|
SysScanDesc scan_desc;
|
HeapTuple tuple;
|
bool found;
|
label_graph_oid_cache_entry *entry;
|
|
memcpy(scan_keys, label_graph_oid_scan_keys,
|
sizeof(label_graph_oid_scan_keys));
|
scan_keys[0].sk_argument = ObjectIdGetDatum(graph);
|
scan_keys[1].sk_argument = Int32GetDatum(id);
|
|
/*
|
* Calling table_open() might call AcceptInvalidationMessage() and that
|
* might invalidate the label caches. This is OK because this function is
|
* called when the desired entry is not in the cache.
|
*/
|
ag_label = table_open(ag_label_relation_id(), AccessShareLock);
|
scan_desc = systable_beginscan(ag_label, ag_label_graph_oid_index_id(), true,
|
NULL, 2, scan_keys);
|
|
/*
|
* don't need to loop over scan_desc because ag_label_graph_oid_index is
|
* UNIQUE
|
*/
|
tuple = systable_getnext(scan_desc);
|
if (!HeapTupleIsValid(tuple))
|
{
|
systable_endscan(scan_desc);
|
table_close(ag_label, AccessShareLock);
|
|
return NULL;
|
}
|
|
// get a new entry
|
entry = label_graph_oid_cache_hash_search(graph, id, HASH_ENTER, &found);
|
Assert(!found); // no concurrent update on label_graph_oid_cache_hash
|
|
// fill the new entry with the retrieved tuple
|
fill_label_cache_data(&entry->data, tuple, RelationGetDescr(ag_label));
|
|
systable_endscan(scan_desc);
|
table_close(ag_label, AccessShareLock);
|
|
return &entry->data;
|
}
|
|
static void *label_graph_oid_cache_hash_search(uint32 graph, int32 id,
|
HASHACTION action, bool *found)
|
{
|
label_graph_oid_cache_key key;
|
|
// initialize the hash key for label_graph_oid_cache_hash
|
key.graph = graph;
|
key.id = id;
|
|
return hash_search(label_graph_oid_cache_hash, &key, action, found);
|
}
|
|
label_cache_data *search_label_relation_cache(Oid relation)
|
{
|
label_relation_cache_entry *entry;
|
|
initialize_caches();
|
|
entry = hash_search(label_relation_cache_hash, &relation, HASH_FIND, NULL);
|
if (entry)
|
{
|
return &entry->data;
|
}
|
return search_label_relation_cache_miss(relation);
|
}
|
|
static label_cache_data *search_label_relation_cache_miss(Oid relation)
|
{
|
ScanKeyData scan_keys[1];
|
Relation ag_label;
|
SysScanDesc scan_desc;
|
HeapTuple tuple;
|
bool found;
|
label_cache_data *entry;
|
|
memcpy(scan_keys, label_relation_scan_keys,
|
sizeof(label_relation_scan_keys));
|
scan_keys[0].sk_argument = ObjectIdGetDatum(relation);
|
|
/*
|
* Calling table_open() might call AcceptInvalidationMessage() and that
|
* might invalidate the label caches. This is OK because this function is
|
* called when the desired entry is not in the cache.
|
*/
|
ag_label = table_open(ag_label_relation_id(), AccessShareLock);
|
scan_desc = systable_beginscan(ag_label, ag_label_relation_index_id(), true,
|
NULL, 1, scan_keys);
|
|
// don't need to loop over scan_desc because ag_label_relation_index is
|
// UNIQUE
|
tuple = systable_getnext(scan_desc);
|
if (!HeapTupleIsValid(tuple))
|
{
|
systable_endscan(scan_desc);
|
table_close(ag_label, AccessShareLock);
|
|
return NULL;
|
}
|
|
// get a new entry
|
entry = hash_search(label_relation_cache_hash, &relation, HASH_ENTER,
|
&found);
|
Assert(!found); // no concurrent update on label_relation_cache_hash
|
|
// fill the new entry with the retrieved tuple
|
fill_label_cache_data(entry, tuple, RelationGetDescr(ag_label));
|
|
systable_endscan(scan_desc);
|
table_close(ag_label, AccessShareLock);
|
|
return entry;
|
}
|
|
label_cache_data *search_label_seq_name_graph_cache(const char *name, Oid graph)
|
{
|
NameData name_key;
|
label_seq_name_graph_cache_entry *entry;
|
|
AssertArg(name);
|
AssertArg(OidIsValid(graph));
|
|
initialize_caches();
|
|
namestrcpy(&name_key, name);
|
entry = label_seq_name_graph_cache_hash_search(&name_key, graph, HASH_FIND,
|
NULL);
|
if (entry)
|
{
|
return &entry->data;
|
}
|
return search_label_seq_name_graph_cache_miss(&name_key, graph);
|
}
|
|
static label_cache_data *search_label_seq_name_graph_cache_miss(Name name,
|
Oid graph)
|
{
|
ScanKeyData scan_keys[2];
|
Relation ag_label;
|
SysScanDesc scan_desc;
|
HeapTuple tuple;
|
bool found;
|
label_seq_name_graph_cache_entry *entry;
|
|
memcpy(scan_keys, label_seq_name_graph_scan_keys,
|
sizeof(label_seq_name_graph_scan_keys));
|
scan_keys[0].sk_argument = NameGetDatum(name);
|
scan_keys[1].sk_argument = ObjectIdGetDatum(graph);
|
|
/*
|
* Calling table_open() might call AcceptInvalidationMessage() and that
|
* might invalidate the label caches. This is OK because this function is
|
* called when the desired entry is not in the cache.
|
*/
|
ag_label = table_open(ag_label_relation_id(), AccessShareLock);
|
scan_desc = systable_beginscan(ag_label, ag_label_seq_name_graph_index_id(),
|
true, NULL, 2, scan_keys);
|
|
/*
|
* don't need to loop over scan_desc because ag_label_seq_name_graph_index is
|
* UNIQUE
|
*/
|
tuple = systable_getnext(scan_desc);
|
if (!HeapTupleIsValid(tuple))
|
{
|
systable_endscan(scan_desc);
|
table_close(ag_label, AccessShareLock);
|
|
return NULL;
|
}
|
|
// get a new entry
|
entry = label_seq_name_graph_cache_hash_search(name, graph, HASH_ENTER,
|
&found);
|
Assert(!found); // no concurrent update on label_seq_name_graph_cache_hash
|
|
// fill the new entry with the retrieved tuple
|
fill_label_cache_data(&entry->data, tuple, RelationGetDescr(ag_label));
|
|
systable_endscan(scan_desc);
|
table_close(ag_label, AccessShareLock);
|
|
return &entry->data;
|
}
|
|
static void *label_seq_name_graph_cache_hash_search(Name name, Oid graph,
|
HASHACTION action,
|
bool *found)
|
{
|
label_seq_name_graph_cache_key key;
|
|
// initialize the hash key for label_seq_name_graph_cache_hash
|
namestrcpy(&key.name, name->data);
|
key.graph = graph;
|
|
return hash_search(label_seq_name_graph_cache_hash, &key, action, found);
|
}
|
|
static void fill_label_cache_data(label_cache_data *cache_data,
|
HeapTuple tuple, TupleDesc tuple_desc)
|
{
|
bool is_null;
|
Datum value;
|
Name name;
|
|
// ag_label.name
|
value = heap_getattr(tuple, Anum_ag_label_name, tuple_desc, &is_null);
|
Assert(!is_null);
|
name = DatumGetName(value);
|
Assert(name != NULL);
|
namestrcpy(&cache_data->name, name->data);
|
// ag_label.graph
|
value = heap_getattr(tuple, Anum_ag_label_graph, tuple_desc, &is_null);
|
Assert(!is_null);
|
cache_data->graph = DatumGetObjectId(value);
|
// ag_label.id
|
value = heap_getattr(tuple, Anum_ag_label_id, tuple_desc, &is_null);
|
Assert(!is_null);
|
cache_data->id = DatumGetInt32(value);
|
// ag_label.kind
|
value = heap_getattr(tuple, Anum_ag_label_kind, tuple_desc, &is_null);
|
Assert(!is_null);
|
cache_data->kind = DatumGetChar(value);
|
// ag_label.relation
|
value = heap_getattr(tuple, Anum_ag_label_relation, tuple_desc, &is_null);
|
Assert(!is_null);
|
cache_data->relation = DatumGetObjectId(value);
|
// ag_label.seq_name
|
value = heap_getattr(tuple, Anum_ag_label_seq_name, tuple_desc, &is_null);
|
Assert(!is_null);
|
namestrcpy(&cache_data->seq_name, DatumGetName(value)->data);
|
}
|