/*-------------------------------------------------------------------------
 *
 * syscache.c
 *	  System cache management routines
 *
 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.56 2000/11/10 00:33:10 tgl Exp $
 *
 * NOTES
 *	  These routines allow the parser/planner/executor to perform
 *	  rapid lookups on the contents of the system catalogs.
 *
 *	  see catalog/syscache.h for a list of the cache id's
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/heapam.h"
#include "access/transam.h"
#include "utils/builtins.h"
#include "catalog/catname.h"
#include "catalog/indexing.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_group.h"
#include "catalog/pg_index.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_language.h"
#include "catalog/pg_listener.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_shadow.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_type.h"
#include "utils/catcache.h"
#include "utils/syscache.h"
#include "utils/temprel.h"
#include "miscadmin.h"


/*---------------------------------------------------------------------------

	Adding system caches:

	Add your new cache to the list in include/utils/syscache.h.  Keep
	the list sorted alphabetically and adjust the cache numbers
	accordingly.

	Add your entry to the cacheinfo[] array below.	All cache lists are
	alphabetical, so add it in the proper place.  Specify the relation
	name, index name, number of keys, and key attribute numbers.

	In include/catalog/indexing.h, add a define for the number of indexes
	on the relation, add define(s) for the index name(s), add an extern
	array to hold the index names, and use DECLARE_UNIQUE_INDEX to define
	the index.  Cache lookups return only one row, so the index should be
	unique in most cases.

	In backend/catalog/indexing.c, initialize the relation array with
	the index names for the relation.

	Finally, any place your relation gets heap_insert() or
	heap_update calls, include code to do a CatalogIndexInsert() to update
	the system indexes.  The heap_* calls do not update indexes.

	bjm 1999/11/22

  ---------------------------------------------------------------------------
*/

/* ----------------
 *		struct cachedesc: information defining a single syscache
 * ----------------
 */
struct cachedesc
{
	char	   *name;			/* name of the relation being cached */
	char	   *indname;		/* name of index relation for this cache */
	int			nkeys;			/* # of keys needed for cache lookup */
	int			key[4];			/* attribute numbers of key attrs */
};

static struct cachedesc cacheinfo[] = {
	{AggregateRelationName,		/* AGGNAME */
	 AggregateNameTypeIndex,
		2,
		{
			Anum_pg_aggregate_aggname,
			Anum_pg_aggregate_aggbasetype,
			0,
			0
		}},
	{AccessMethodRelationName,	/* AMNAME */
	 AmNameIndex,
		1,
		{
			Anum_pg_am_amname,
			0,
			0,
			0
		}},
	{AccessMethodOperatorRelationName,	/* AMOPOPID */
	 AccessMethodOpidIndex,
		3,
		{
			Anum_pg_amop_amopclaid,
			Anum_pg_amop_amopopr,
			Anum_pg_amop_amopid,
			0
		}},
	{AccessMethodOperatorRelationName,	/* AMOPSTRATEGY */
	 AccessMethodStrategyIndex,
		3,
		{
			Anum_pg_amop_amopid,
			Anum_pg_amop_amopclaid,
			Anum_pg_amop_amopstrategy,
			0
		}},
	{AttributeRelationName,		/* ATTNAME */
	 AttributeRelidNameIndex,
		2,
		{
			Anum_pg_attribute_attrelid,
			Anum_pg_attribute_attname,
			0,
			0
		}},
	{AttributeRelationName,		/* ATTNUM */
	 AttributeRelidNumIndex,
		2,
		{
			Anum_pg_attribute_attrelid,
			Anum_pg_attribute_attnum,
			0,
			0
		}},
	{OperatorClassRelationName, /* CLADEFTYPE */
	 OpclassDeftypeIndex,
		1,
		{
			Anum_pg_opclass_opcdeftype,
			0,
			0,
			0
		}},
	{OperatorClassRelationName, /* CLANAME */
	 OpclassNameIndex,
		1,
		{
			Anum_pg_opclass_opcname,
			0,
			0,
			0
		}},
	{GroupRelationName,			/* GRONAME */
	 GroupNameIndex,
		1,
		{
			Anum_pg_group_groname,
			0,
			0,
			0
		}},
	{GroupRelationName,			/* GROSYSID */
	 GroupSysidIndex,
		1,
		{
			Anum_pg_group_grosysid,
			0,
			0,
			0
		}},
	{IndexRelationName,			/* INDEXRELID */
	 IndexRelidIndex,
		1,
		{
			Anum_pg_index_indexrelid,
			0,
			0,
			0
		}},
	{InheritsRelationName,		/* INHRELID */
	 InheritsRelidSeqnoIndex,
		2,
		{
			Anum_pg_inherits_inhrelid,
			Anum_pg_inherits_inhseqno,
			0,
			0
		}},
	{LanguageRelationName,		/* LANGNAME */
	 LanguageNameIndex,
		1,
		{
			Anum_pg_language_lanname,
			0,
			0,
			0
		}},
	{LanguageRelationName,		/* LANGOID */
	 LanguageOidIndex,
		1,
		{
			ObjectIdAttributeNumber,
			0,
			0,
			0
		}},
	{ListenerRelationName,		/* LISTENREL */
	 ListenerPidRelnameIndex,
		2,
		{
			Anum_pg_listener_pid,
			Anum_pg_listener_relname,
			0,
			0
		}},
	{OperatorRelationName,		/* OPERNAME */
	 OperatorNameIndex,
		4,
		{
			Anum_pg_operator_oprname,
			Anum_pg_operator_oprleft,
			Anum_pg_operator_oprright,
			Anum_pg_operator_oprkind
		}},
	{OperatorRelationName,		/* OPEROID */
	 OperatorOidIndex,
		1,
		{
			ObjectIdAttributeNumber,
			0,
			0,
			0
		}},
	{ProcedureRelationName,		/* PROCNAME */
	 ProcedureNameIndex,
		3,
		{
			Anum_pg_proc_proname,
			Anum_pg_proc_pronargs,
			Anum_pg_proc_proargtypes,
			0
		}},
	{ProcedureRelationName,		/* PROCOID */
	 ProcedureOidIndex,
		1,
		{
			ObjectIdAttributeNumber,
			0,
			0,
			0
		}},
	{RelationRelationName,		/* RELNAME */
	 ClassNameIndex,
		1,
		{
			Anum_pg_class_relname,
			0,
			0,
			0
		}},
	{RelationRelationName,		/* RELOID */
	 ClassOidIndex,
		1,
		{
			ObjectIdAttributeNumber,
			0,
			0,
			0
		}},
	{RewriteRelationName,		/* REWRITENAME */
	 RewriteRulenameIndex,
		1,
		{
			Anum_pg_rewrite_rulename,
			0,
			0,
			0
		}},
	{RewriteRelationName,		/* RULEOID */
	 RewriteOidIndex,
		1,
		{
			ObjectIdAttributeNumber,
			0,
			0,
			0
		}},
	{ShadowRelationName,		/* SHADOWNAME */
	 ShadowNameIndex,
		1,
		{
			Anum_pg_shadow_usename,
			0,
			0,
			0
		}},
	{ShadowRelationName,		/* SHADOWSYSID */
	 ShadowSysidIndex,
		1,
		{
			Anum_pg_shadow_usesysid,
			0,
			0,
			0
		}},
	{StatisticRelationName,		/* STATRELID */
	 StatisticRelidAttnumIndex,
		2,
		{
			Anum_pg_statistic_starelid,
			Anum_pg_statistic_staattnum,
			0,
			0
		}},
	{TypeRelationName,			/* TYPENAME */
	 TypeNameIndex,
		1,
		{
			Anum_pg_type_typname,
			0,
			0,
			0
		}},
	{TypeRelationName,			/* TYPEOID */
	 TypeOidIndex,
		1,
		{
			ObjectIdAttributeNumber,
			0,
			0,
			0
		}}
};

static CatCache *SysCache[lengthof(cacheinfo)];
static int32 SysCacheSize = lengthof(cacheinfo);
static bool CacheInitialized = false;


bool
IsCacheInitialized(void)
{
	return CacheInitialized;
}


/*
 * zerocaches
 *
 *	  Make sure the SysCache structure is zero'd.
 */
void
zerocaches()
{
	MemSet((char *) SysCache, 0, SysCacheSize * sizeof(CatCache *));
}


/*
 * InitCatalogCache - initialize the caches
 */
void
InitCatalogCache()
{
	int			cacheId;		/* XXX type */

	if (!AMI_OVERRIDE)
	{
		for (cacheId = 0; cacheId < SysCacheSize; cacheId += 1)
		{
			Assert(!PointerIsValid(SysCache[cacheId]));

			SysCache[cacheId] = InitSysCache(cacheId,
											 cacheinfo[cacheId].name,
											 cacheinfo[cacheId].indname,
											 cacheinfo[cacheId].nkeys,
											 cacheinfo[cacheId].key);
			if (!PointerIsValid(SysCache[cacheId]))
			{
				elog(ERROR,
					 "InitCatalogCache: Can't init cache %s (%d)",
					 cacheinfo[cacheId].name,
					 cacheId);
			}

		}
	}
	CacheInitialized = true;
}


/*
 * SearchSysCacheTuple
 *
 *	A layer on top of SearchSysCache that does the initialization and
 *	key-setting for you.
 *
 *	Returns the cache copy of the tuple if one is found, NULL if not.
 *	The tuple is the 'cache' copy.
 *
 *	CAUTION: The tuple that is returned must NOT be freed by the caller!
 *
 *	CAUTION: The returned tuple may be flushed from the cache during
 *	subsequent cache lookup operations, or by shared cache invalidation.
 *	Callers should not expect the pointer to remain valid for long.
 *
 *  XXX we ought to have some kind of referencecount mechanism for
 *  cache entries, to ensure entries aren't deleted while in use.
 */
HeapTuple
SearchSysCacheTuple(int cacheId,/* cache selection code */
					Datum key1,
					Datum key2,
					Datum key3,
					Datum key4)
{
	HeapTuple	tp;

	if (cacheId < 0 || cacheId >= SysCacheSize)
	{
		elog(ERROR, "SearchSysCacheTuple: Bad cache id %d", cacheId);
		return (HeapTuple) NULL;
	}

	Assert(AMI_OVERRIDE || PointerIsValid(SysCache[cacheId]));

	if (!PointerIsValid(SysCache[cacheId]))
	{
		SysCache[cacheId] = InitSysCache(cacheId,
										 cacheinfo[cacheId].name,
										 cacheinfo[cacheId].indname,
										 cacheinfo[cacheId].nkeys,
										 cacheinfo[cacheId].key);
		if (!PointerIsValid(SysCache[cacheId]))
			elog(ERROR,
				 "InitCatalogCache: Can't init cache %s(%d)",
				 cacheinfo[cacheId].name,
				 cacheId);
	}

	/*
	 * If someone tries to look up a relname, translate temp relation
	 * names to real names.  Less obviously, apply the same translation
	 * to type names, so that the type tuple of a temp table will be found
	 * when sought.  This is a kluge ... temp table substitution should be
	 * happening at a higher level ...
	 */
	if (cacheId == RELNAME || cacheId == TYPENAME)
	{
		char	   *nontemp_relname;

		nontemp_relname = get_temp_rel_by_username(DatumGetCString(key1));
		if (nontemp_relname != NULL)
			key1 = CStringGetDatum(nontemp_relname);
	}

	tp = SearchSysCache(SysCache[cacheId], key1, key2, key3, key4);
	if (!HeapTupleIsValid(tp))
	{
#ifdef CACHEDEBUG
		elog(DEBUG,
			 "SearchSysCacheTuple: Search %s(%d) %d %d %d %d failed",
			 cacheinfo[cacheId].name,
			 cacheId, key1, key2, key3, key4);
#endif
		return (HeapTuple) NULL;
	}
	return tp;
}


/*
 * SearchSysCacheTupleCopy
 *
 *	This is like SearchSysCacheTuple, except it returns a palloc'd copy of
 *	the tuple.  The caller should heap_freetuple() the returned copy when
 *	done with it.  This routine should be used when the caller intends to
 *	continue to access the tuple for more than a very short period of time.
 */
HeapTuple
SearchSysCacheTupleCopy(int cacheId,	/* cache selection code */
						Datum key1,
						Datum key2,
						Datum key3,
						Datum key4)
{
	HeapTuple	cachetup;

	cachetup = SearchSysCacheTuple(cacheId, key1, key2, key3, key4);
	if (PointerIsValid(cachetup))
		return heap_copytuple(cachetup);
	else
		return cachetup;		/* NULL */
}


/*
 * SysCacheGetAttr
 *
 *		Given a tuple previously fetched by SearchSysCacheTuple() or
 *		SearchSysCacheTupleCopy(), extract a specific attribute.
 *
 * This is equivalent to using heap_getattr() on a tuple fetched
 * from a non-cached relation.	Usually, this is only used for attributes
 * that could be NULL or variable length; the fixed-size attributes in
 * a system table are accessed just by mapping the tuple onto the C struct
 * declarations from include/catalog/.
 *
 * As with heap_getattr(), if the attribute is of a pass-by-reference type
 * then a pointer into the tuple data area is returned --- the caller must
 * not modify or pfree the datum!
 */
Datum
SysCacheGetAttr(int cacheId, HeapTuple tup,
				AttrNumber attributeNumber,
				bool *isNull)
{

	/*
	 * We just need to get the TupleDesc out of the cache entry, and then
	 * we can apply heap_getattr().  We expect that the cache control data
	 * is currently valid --- if the caller recently fetched the tuple, then
	 * it should be.
	 */
	if (cacheId < 0 || cacheId >= SysCacheSize)
		elog(ERROR, "SysCacheGetAttr: Bad cache id %d", cacheId);
	if (!PointerIsValid(SysCache[cacheId]) ||
		!PointerIsValid(SysCache[cacheId]->cc_tupdesc))
		elog(ERROR, "SysCacheGetAttr: missing cache data for id %d", cacheId);

	return heap_getattr(tup, attributeNumber,
						SysCache[cacheId]->cc_tupdesc,
						isNull);
}
