1
0
mirror of https://github.com/postgres/postgres.git synced 2025-05-08 07:21:33 +03:00
Robert Haas e26c539e9f Wrap calls to SearchSysCache and related functions using macros.
The purpose of this change is to eliminate the need for every caller
of SearchSysCache, SearchSysCacheCopy, SearchSysCacheExists,
GetSysCacheOid, and SearchSysCacheList to know the maximum number
of allowable keys for a syscache entry (currently 4).  This will
make it far easier to increase the maximum number of keys in a
future release should we choose to do so, and it makes the code
shorter, too.

Design and review by Tom Lane.
2010-02-14 18:42:19 +00:00

201 lines
5.2 KiB
C

/*-------------------------------------------------------------------------
*
* spccache.c
* Tablespace cache management.
*
* We cache the parsed version of spcoptions for each tablespace to avoid
* needing to reparse on every lookup. Right now, there doesn't appear to
* be a measurable performance gain from doing this, but that might change
* in the future as we add more options.
*
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/spccache.c,v 1.5 2010/02/14 18:42:17 rhaas Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/reloptions.h"
#include "catalog/pg_tablespace.h"
#include "commands/tablespace.h"
#include "miscadmin.h"
#include "optimizer/cost.h"
#include "utils/catcache.h"
#include "utils/hsearch.h"
#include "utils/inval.h"
#include "utils/spccache.h"
#include "utils/syscache.h"
/* Hash table for information about each tablespace */
static HTAB *TableSpaceCacheHash = NULL;
typedef struct
{
Oid oid; /* lookup key - must be first */
TableSpaceOpts *opts; /* options, or NULL if none */
} TableSpaceCacheEntry;
/*
* InvalidateTableSpaceCacheCallback
* Flush all cache entries when pg_tablespace is updated.
*
* When pg_tablespace is updated, we must flush the cache entry at least
* for that tablespace. Currently, we just flush them all. This is quick
* and easy and doesn't cost much, since there shouldn't be terribly many
* tablespaces, nor do we expect them to be frequently modified.
*/
static void
InvalidateTableSpaceCacheCallback(Datum arg, int cacheid, ItemPointer tuplePtr)
{
HASH_SEQ_STATUS status;
TableSpaceCacheEntry *spc;
hash_seq_init(&status, TableSpaceCacheHash);
while ((spc = (TableSpaceCacheEntry *) hash_seq_search(&status)) != NULL)
{
if (spc->opts)
pfree(spc->opts);
if (hash_search(TableSpaceCacheHash,
(void *) &spc->oid,
HASH_REMOVE,
NULL) == NULL)
elog(ERROR, "hash table corrupted");
}
}
/*
* InitializeTableSpaceCache
* Initialize the tablespace cache.
*/
static void
InitializeTableSpaceCache(void)
{
HASHCTL ctl;
/* Initialize the hash table. */
MemSet(&ctl, 0, sizeof(ctl));
ctl.keysize = sizeof(Oid);
ctl.entrysize = sizeof(TableSpaceCacheEntry);
ctl.hash = oid_hash;
TableSpaceCacheHash =
hash_create("TableSpace cache", 16, &ctl,
HASH_ELEM | HASH_FUNCTION);
/* Make sure we've initialized CacheMemoryContext. */
if (!CacheMemoryContext)
CreateCacheMemoryContext();
/* Watch for invalidation events. */
CacheRegisterSyscacheCallback(TABLESPACEOID,
InvalidateTableSpaceCacheCallback,
(Datum) 0);
}
/*
* get_tablespace
* Fetch TableSpaceCacheEntry structure for a specified table OID.
*
* Pointers returned by this function should not be stored, since a cache
* flush will invalidate them.
*/
static TableSpaceCacheEntry *
get_tablespace(Oid spcid)
{
TableSpaceCacheEntry *spc;
HeapTuple tp;
TableSpaceOpts *opts;
/*
* Since spcid is always from a pg_class tuple, InvalidOid implies the
* default.
*/
if (spcid == InvalidOid)
spcid = MyDatabaseTableSpace;
/* Find existing cache entry, if any. */
if (!TableSpaceCacheHash)
InitializeTableSpaceCache();
spc = (TableSpaceCacheEntry *) hash_search(TableSpaceCacheHash,
(void *) &spcid,
HASH_FIND,
NULL);
if (spc)
return spc;
/*
* Not found in TableSpace cache. Check catcache. If we don't find a
* valid HeapTuple, it must mean someone has managed to request tablespace
* details for a non-existent tablespace. We'll just treat that case as if
* no options were specified.
*/
tp = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(spcid));
if (!HeapTupleIsValid(tp))
opts = NULL;
else
{
Datum datum;
bool isNull;
datum = SysCacheGetAttr(TABLESPACEOID,
tp,
Anum_pg_tablespace_spcoptions,
&isNull);
if (isNull)
opts = NULL;
else
{
bytea *bytea_opts = tablespace_reloptions(datum, false);
opts = MemoryContextAlloc(CacheMemoryContext, VARSIZE(bytea_opts));
memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
}
ReleaseSysCache(tp);
}
/*
* Now create the cache entry. It's important to do this only after
* reading the pg_tablespace entry, since doing so could cause a cache
* flush.
*/
spc = (TableSpaceCacheEntry *) hash_search(TableSpaceCacheHash,
(void *) &spcid,
HASH_ENTER,
NULL);
spc->opts = opts;
return spc;
}
/*
* get_tablespace_page_costs
* Return random and/or sequential page costs for a given tablespace.
*/
void
get_tablespace_page_costs(Oid spcid,
double *spc_random_page_cost,
double *spc_seq_page_cost)
{
TableSpaceCacheEntry *spc = get_tablespace(spcid);
Assert(spc != NULL);
if (spc_random_page_cost)
{
if (!spc->opts || spc->opts->random_page_cost < 0)
*spc_random_page_cost = random_page_cost;
else
*spc_random_page_cost = spc->opts->random_page_cost;
}
if (spc_seq_page_cost)
{
if (!spc->opts || spc->opts->seq_page_cost < 0)
*spc_seq_page_cost = seq_page_cost;
else
*spc_seq_page_cost = spc->opts->seq_page_cost;
}
}