Header And Logo

PostgreSQL
| The world's most advanced open source database.

spccache.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * spccache.c
00004  *    Tablespace cache management.
00005  *
00006  * We cache the parsed version of spcoptions for each tablespace to avoid
00007  * needing to reparse on every lookup.  Right now, there doesn't appear to
00008  * be a measurable performance gain from doing this, but that might change
00009  * in the future as we add more options.
00010  *
00011  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00012  * Portions Copyright (c) 1994, Regents of the University of California
00013  *
00014  * IDENTIFICATION
00015  *    src/backend/utils/cache/spccache.c
00016  *
00017  *-------------------------------------------------------------------------
00018  */
00019 #include "postgres.h"
00020 
00021 #include "access/reloptions.h"
00022 #include "catalog/pg_tablespace.h"
00023 #include "commands/tablespace.h"
00024 #include "miscadmin.h"
00025 #include "optimizer/cost.h"
00026 #include "utils/catcache.h"
00027 #include "utils/hsearch.h"
00028 #include "utils/inval.h"
00029 #include "utils/spccache.h"
00030 #include "utils/syscache.h"
00031 
00032 
00033 /* Hash table for information about each tablespace */
00034 static HTAB *TableSpaceCacheHash = NULL;
00035 
00036 typedef struct
00037 {
00038     Oid         oid;            /* lookup key - must be first */
00039     TableSpaceOpts *opts;       /* options, or NULL if none */
00040 } TableSpaceCacheEntry;
00041 
00042 
00043 /*
00044  * InvalidateTableSpaceCacheCallback
00045  *      Flush all cache entries when pg_tablespace is updated.
00046  *
00047  * When pg_tablespace is updated, we must flush the cache entry at least
00048  * for that tablespace.  Currently, we just flush them all.  This is quick
00049  * and easy and doesn't cost much, since there shouldn't be terribly many
00050  * tablespaces, nor do we expect them to be frequently modified.
00051  */
00052 static void
00053 InvalidateTableSpaceCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
00054 {
00055     HASH_SEQ_STATUS status;
00056     TableSpaceCacheEntry *spc;
00057 
00058     hash_seq_init(&status, TableSpaceCacheHash);
00059     while ((spc = (TableSpaceCacheEntry *) hash_seq_search(&status)) != NULL)
00060     {
00061         if (spc->opts)
00062             pfree(spc->opts);
00063         if (hash_search(TableSpaceCacheHash,
00064                         (void *) &spc->oid,
00065                         HASH_REMOVE,
00066                         NULL) == NULL)
00067             elog(ERROR, "hash table corrupted");
00068     }
00069 }
00070 
00071 /*
00072  * InitializeTableSpaceCache
00073  *      Initialize the tablespace cache.
00074  */
00075 static void
00076 InitializeTableSpaceCache(void)
00077 {
00078     HASHCTL     ctl;
00079 
00080     /* Initialize the hash table. */
00081     MemSet(&ctl, 0, sizeof(ctl));
00082     ctl.keysize = sizeof(Oid);
00083     ctl.entrysize = sizeof(TableSpaceCacheEntry);
00084     ctl.hash = oid_hash;
00085     TableSpaceCacheHash =
00086         hash_create("TableSpace cache", 16, &ctl,
00087                     HASH_ELEM | HASH_FUNCTION);
00088 
00089     /* Make sure we've initialized CacheMemoryContext. */
00090     if (!CacheMemoryContext)
00091         CreateCacheMemoryContext();
00092 
00093     /* Watch for invalidation events. */
00094     CacheRegisterSyscacheCallback(TABLESPACEOID,
00095                                   InvalidateTableSpaceCacheCallback,
00096                                   (Datum) 0);
00097 }
00098 
00099 /*
00100  * get_tablespace
00101  *      Fetch TableSpaceCacheEntry structure for a specified table OID.
00102  *
00103  * Pointers returned by this function should not be stored, since a cache
00104  * flush will invalidate them.
00105  */
00106 static TableSpaceCacheEntry *
00107 get_tablespace(Oid spcid)
00108 {
00109     TableSpaceCacheEntry *spc;
00110     HeapTuple   tp;
00111     TableSpaceOpts *opts;
00112 
00113     /*
00114      * Since spcid is always from a pg_class tuple, InvalidOid implies the
00115      * default.
00116      */
00117     if (spcid == InvalidOid)
00118         spcid = MyDatabaseTableSpace;
00119 
00120     /* Find existing cache entry, if any. */
00121     if (!TableSpaceCacheHash)
00122         InitializeTableSpaceCache();
00123     spc = (TableSpaceCacheEntry *) hash_search(TableSpaceCacheHash,
00124                                                (void *) &spcid,
00125                                                HASH_FIND,
00126                                                NULL);
00127     if (spc)
00128         return spc;
00129 
00130     /*
00131      * Not found in TableSpace cache.  Check catcache.  If we don't find a
00132      * valid HeapTuple, it must mean someone has managed to request tablespace
00133      * details for a non-existent tablespace.  We'll just treat that case as
00134      * if no options were specified.
00135      */
00136     tp = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(spcid));
00137     if (!HeapTupleIsValid(tp))
00138         opts = NULL;
00139     else
00140     {
00141         Datum       datum;
00142         bool        isNull;
00143 
00144         datum = SysCacheGetAttr(TABLESPACEOID,
00145                                 tp,
00146                                 Anum_pg_tablespace_spcoptions,
00147                                 &isNull);
00148         if (isNull)
00149             opts = NULL;
00150         else
00151         {
00152             bytea      *bytea_opts = tablespace_reloptions(datum, false);
00153 
00154             opts = MemoryContextAlloc(CacheMemoryContext, VARSIZE(bytea_opts));
00155             memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
00156         }
00157         ReleaseSysCache(tp);
00158     }
00159 
00160     /*
00161      * Now create the cache entry.  It's important to do this only after
00162      * reading the pg_tablespace entry, since doing so could cause a cache
00163      * flush.
00164      */
00165     spc = (TableSpaceCacheEntry *) hash_search(TableSpaceCacheHash,
00166                                                (void *) &spcid,
00167                                                HASH_ENTER,
00168                                                NULL);
00169     spc->opts = opts;
00170     return spc;
00171 }
00172 
00173 /*
00174  * get_tablespace_page_costs
00175  *      Return random and/or sequential page costs for a given tablespace.
00176  */
00177 void
00178 get_tablespace_page_costs(Oid spcid,
00179                           double *spc_random_page_cost,
00180                           double *spc_seq_page_cost)
00181 {
00182     TableSpaceCacheEntry *spc = get_tablespace(spcid);
00183 
00184     Assert(spc != NULL);
00185 
00186     if (spc_random_page_cost)
00187     {
00188         if (!spc->opts || spc->opts->random_page_cost < 0)
00189             *spc_random_page_cost = random_page_cost;
00190         else
00191             *spc_random_page_cost = spc->opts->random_page_cost;
00192     }
00193 
00194     if (spc_seq_page_cost)
00195     {
00196         if (!spc->opts || spc->opts->seq_page_cost < 0)
00197             *spc_seq_page_cost = seq_page_cost;
00198         else
00199             *spc_seq_page_cost = spc->opts->seq_page_cost;
00200     }
00201 }