/*-------------------------------------------------------------------------
 *
 * syscache.c
 *	  System cache management routines
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.30 1999/07/16 05:23:21 momjian 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 <string.h>
#include "postgres.h"

#include "access/heapam.h"
#include "catalog/catname.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_type.h"
#include "utils/catcache.h"

extern bool AMI_OVERRIDE;		/* XXX style */

#include "utils/syscache.h"
#include "catalog/indexing.h"

typedef HeapTuple (*ScanFunc) ();

/* ----------------
 *		Warning:  cacheinfo[] below is changed, then be sure and
 *		update the magic constants in syscache.h!
 * ----------------
 */
static struct cachedesc cacheinfo[] = {
	{AccessMethodOperatorRelationName,	/* AMOPOPID */
		3,
		{
			Anum_pg_amop_amopclaid,
			Anum_pg_amop_amopopr,
			Anum_pg_amop_amopid,
			0
		},
		sizeof(FormData_pg_amop),
		NULL,
	(ScanFunc) NULL},
	{AccessMethodOperatorRelationName,	/* AMOPSTRATEGY */
		3,
		{
			Anum_pg_amop_amopid,
			Anum_pg_amop_amopclaid,
			Anum_pg_amop_amopstrategy,
			0
		},
		sizeof(FormData_pg_amop),
		NULL,
	(ScanFunc) NULL},
	{AttributeRelationName,		/* ATTNAME */
		2,
		{
			Anum_pg_attribute_attrelid,
			Anum_pg_attribute_attname,
			0,
			0
		},
		ATTRIBUTE_TUPLE_SIZE,
		AttributeNameIndex,
	(ScanFunc) AttributeNameIndexScan},
	{AttributeRelationName,		/* ATTNUM */
		2,
		{
			Anum_pg_attribute_attrelid,
			Anum_pg_attribute_attnum,
			0,
			0
		},
		ATTRIBUTE_TUPLE_SIZE,
		AttributeNumIndex,
	(ScanFunc) AttributeNumIndexScan},
	{IndexRelationName,			/* INDEXRELID */
		1,
		{
			Anum_pg_index_indexrelid,
			0,
			0,
			0
		},
		offsetof(FormData_pg_index, indpred),
		NULL,
	NULL},
	{LanguageRelationName,		/* LANNAME */
		1,
		{
			Anum_pg_language_lanname,
			0,
			0,
			0
		},
		offsetof(FormData_pg_language, lancompiler),
		NULL,
	NULL},
	{OperatorRelationName,		/* OPRNAME */
		4,
		{
			Anum_pg_operator_oprname,
			Anum_pg_operator_oprleft,
			Anum_pg_operator_oprright,
			Anum_pg_operator_oprkind
		},
		sizeof(FormData_pg_operator),
		NULL,
	NULL},
	{OperatorRelationName,		/* OPROID */
		1,
		{
			ObjectIdAttributeNumber,
			0,
			0,
			0
		},
		sizeof(FormData_pg_operator),
		NULL,
	(ScanFunc) NULL},
	{ProcedureRelationName,		/* PRONAME */
		3,
		{
			Anum_pg_proc_proname,
			Anum_pg_proc_pronargs,
			Anum_pg_proc_proargtypes,
			0
		},
		offsetof(FormData_pg_proc, prosrc),
		ProcedureNameIndex,
	(ScanFunc) ProcedureNameIndexScan},
	{ProcedureRelationName,		/* PROOID */
		1,
		{
			ObjectIdAttributeNumber,
			0,
			0,
			0
		},
		offsetof(FormData_pg_proc, prosrc),
		ProcedureOidIndex,
	(ScanFunc) ProcedureOidIndexScan},
	{RelationRelationName,		/* RELNAME */
		1,
		{
			Anum_pg_class_relname,
			0,
			0,
			0
		},
		CLASS_TUPLE_SIZE,
		ClassNameIndex,
	(ScanFunc) ClassNameIndexScan},
	{RelationRelationName,		/* RELOID */
		1,
		{
			ObjectIdAttributeNumber,
			0,
			0,
			0
		},
		CLASS_TUPLE_SIZE,
		ClassOidIndex,
	(ScanFunc) ClassOidIndexScan},
	{TypeRelationName,			/* TYPNAME */
		1,
		{
			Anum_pg_type_typname,
			0,
			0,
			0
		},
		offsetof(FormData_pg_type, typalign) +sizeof(char),
		TypeNameIndex,
	TypeNameIndexScan},
	{TypeRelationName,			/* TYPOID */
		1,
		{
			ObjectIdAttributeNumber,
			0,
			0,
			0
		},
		offsetof(FormData_pg_type, typalign) +sizeof(char),
		TypeOidIndex,
	TypeOidIndexScan},
	{AccessMethodRelationName,	/* AMNAME */
		1,
		{
			Anum_pg_am_amname,
			0,
			0,
			0
		},
		sizeof(FormData_pg_am),
		NULL,
	NULL},
	{OperatorClassRelationName, /* CLANAME */
		1,
		{
			Anum_pg_opclass_opcname,
			0,
			0,
			0
		},
		sizeof(FormData_pg_opclass),
		NULL,
	NULL},
	{IndexRelationName,			/* INDRELIDKEY *//* never used */
		2,
		{
			Anum_pg_index_indrelid,
			Anum_pg_index_indkey,
			0,
			0
		},
		offsetof(FormData_pg_index, indpred),
		NULL,
	(ScanFunc) NULL},
	{InheritsRelationName,		/* INHRELID */
		2,
		{
			Anum_pg_inherits_inhrel,
			Anum_pg_inherits_inhseqno,
			0,
			0
		},
		sizeof(FormData_pg_inherits),
		NULL,
	(ScanFunc) NULL},
	{RewriteRelationName,		/* RULOID */
		1,
		{
			ObjectIdAttributeNumber,
			0,
			0,
			0
		},
		offsetof(FormData_pg_rewrite, ev_qual),
		NULL,
	(ScanFunc) NULL},
	{AggregateRelationName,		/* AGGNAME */
		2,
		{
			Anum_pg_aggregate_aggname,
			Anum_pg_aggregate_aggbasetype,
			0,
			0
		},
		offsetof(FormData_pg_aggregate, agginitval1),
		NULL,
	(ScanFunc) NULL},
	{ListenerRelationName,		/* LISTENREL */
		2,
		{
			Anum_pg_listener_relname,
			Anum_pg_listener_pid,
			0,
			0
		},
		sizeof(FormData_pg_listener),
		NULL,
	(ScanFunc) NULL},
	{ShadowRelationName,		/* USENAME */
		1,
		{
			Anum_pg_shadow_usename,
			0,
			0,
			0
		},
		sizeof(FormData_pg_shadow),
		NULL,
	(ScanFunc) NULL},
	{ShadowRelationName,		/* USESYSID */
		1,
		{
			Anum_pg_shadow_usesysid,
			0,
			0,
			0
		},
		sizeof(FormData_pg_shadow),
		NULL,
	(ScanFunc) NULL},
	{GroupRelationName,			/* GRONAME */
		1,
		{
			Anum_pg_group_groname,
			0,
			0,
			0
		},
		offsetof(FormData_pg_group, grolist[0]),
		NULL,
	(ScanFunc) NULL},
	{GroupRelationName,			/* GROSYSID */
		1,
		{
			Anum_pg_group_grosysid,
			0,
			0,
			0
		},
		offsetof(FormData_pg_group, grolist[0]),
		NULL,
	(ScanFunc) NULL},
	{RewriteRelationName,		/* REWRITENAME */
		1,
		{
			Anum_pg_rewrite_rulename,
			0,
			0,
			0
		},
		offsetof(FormData_pg_rewrite, ev_qual),
		NULL,
	(ScanFunc) NULL},
	{ProcedureRelationName,		/* PROSRC */
		1,
		{
			Anum_pg_proc_prosrc,
			0,
			0,
			0
		},
		offsetof(FormData_pg_proc, prosrc),
		ProcedureSrcIndex,
	(ScanFunc) ProcedureSrcIndexScan},
	{OperatorClassRelationName, /* CLADEFTYPE */
		1,
		{
			Anum_pg_opclass_opcdeftype,
			0,
			0,
			0
		},
		sizeof(FormData_pg_opclass),
		NULL,
	(ScanFunc) NULL},
	{LanguageRelationName,		/* LANOID */
		1,
		{
			ObjectIdAttributeNumber,
			0,
			0,
			0
		},
		offsetof(FormData_pg_language, lancompiler),
		NULL,
	NULL}
};

static struct catcache *SysCache[lengthof(cacheinfo)];
static int32 SysCacheSize = lengthof(cacheinfo);


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

/*
 * Note:
 *		This function was written because the initialized catalog caches
 *		are used to determine which caches may contain tuples which need
 *		to be invalidated in other backends.
 */
void
InitCatalogCache()
{
	int			cacheId;		/* XXX type */

	if (!AMI_OVERRIDE)
	{
		for (cacheId = 0; cacheId < SysCacheSize; cacheId += 1)
		{

			Assert(!PointerIsValid((Pointer) SysCache[cacheId]));

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

		}
	}
}

/*
 * SearchSysCacheTupleCopy
 *
 *	This is like SearchSysCacheTuple, except it returns a copy of the tuple
 *	that the user is required to pfree().
 */
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 */
}


/*
 * 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.
 *
 *	XXX The tuple that is returned is NOT supposed to be pfree'd!
 */
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(cacheinfo[cacheId].name,
										 cacheinfo[cacheId].indname,
										 cacheId,
										 cacheinfo[cacheId].nkeys,
										 cacheinfo[cacheId].key,
										 cacheinfo[cacheId].iScanFunc);
		if (!PointerIsValid(SysCache[cacheId]))
			elog(ERROR,
				 "InitCatalogCache: Can't init cache %s(%d)",
				 cacheinfo[cacheId].name,
				 cacheId);
	}

	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;
}

/*
 * SearchSysCacheStruct
 *	  Fills 's' with the information retrieved by calling SearchSysCache()
 *	  with arguments key1...key4.  Retrieves only the portion of the tuple
 *	  which is not variable-length.
 *
 * NOTE: we are assuming that non-variable-length fields in the system
 *		 catalogs will always be defined!
 *
 * Returns 1L if a tuple was found, 0L if not.
 */
int32
SearchSysCacheStruct(int cacheId,		/* cache selection code */
					 char *returnStruct,		/* (preallocated!) */
					 Datum key1,
					 Datum key2,
					 Datum key3,
					 Datum key4)
{
	HeapTuple	tp;

	if (!PointerIsValid(returnStruct))
	{
		elog(ERROR, "SearchSysCacheStruct: No receiving struct");
		return 0;
	}
	tp = SearchSysCacheTuple(cacheId, key1, key2, key3, key4);
	if (!HeapTupleIsValid(tp))
		return 0;
	memcpy(returnStruct, (char *) GETSTRUCT(tp), cacheinfo[cacheId].size);
	return 1;
}


/*
 * SearchSysCacheGetAttribute
 *	  Returns the attribute corresponding to 'attributeNumber' for
 *	  a given cached tuple.  This routine usually needs to be used for
 *	  attributes that might be NULL or might be at a variable offset
 *	  in the tuple.
 *
 * XXX This re-opens the relation, so this is slower than just pulling
 * fixed-location fields out of the struct returned by SearchSysCacheTuple.
 *
 * [callers all assume this returns a (struct varlena *). -ay 10/94]
 */
void *
SearchSysCacheGetAttribute(int cacheId,
						   AttrNumber attributeNumber,
						   Datum key1,
						   Datum key2,
						   Datum key3,
						   Datum key4)
{
	HeapTuple	tp;
	char	   *cacheName;
	Relation	relation;
	int32		attributeLength,
				attributeByValue;
	bool		isNull;
	Datum		attributeValue;
	void	   *returnValue;

	tp = SearchSysCacheTuple(cacheId, key1, key2, key3, key4);
	cacheName = cacheinfo[cacheId].name;

	if (!HeapTupleIsValid(tp))
	{
#ifdef	CACHEDEBUG
		elog(DEBUG,
			 "SearchSysCacheGetAttribute: Lookup in %s(%d) failed",
			 cacheName, cacheId);
#endif	 /* defined(CACHEDEBUG) */
		return NULL;
	}

	relation = heap_openr(cacheName);

	if (attributeNumber < 0 &&
		attributeNumber > FirstLowInvalidHeapAttributeNumber)
	{
		attributeLength = heap_sysattrlen(attributeNumber);
		attributeByValue = heap_sysattrbyval(attributeNumber);
	}
	else if (attributeNumber > 0 &&
			 attributeNumber <= relation->rd_rel->relnatts)
	{
		attributeLength = relation->rd_att->attrs[attributeNumber - 1]->attlen;
		attributeByValue = relation->rd_att->attrs[attributeNumber - 1]->attbyval;
	}
	else
	{
		elog(ERROR,
			 "SearchSysCacheGetAttribute: Bad attr # %d in %s(%d)",
			 attributeNumber, cacheName, cacheId);
		return NULL;
	}

	attributeValue = heap_getattr(tp,
								  attributeNumber,
								  RelationGetDescr(relation),
								  &isNull);

	if (isNull)
	{

		/*
		 * Used to be an elog(DEBUG, ...) here and a claim that it should
		 * be a FATAL error, I don't think either is warranted -mer 6/9/92
		 */
		return NULL;
	}

	if (attributeByValue)
		returnValue = (void *) attributeValue;
	else
	{
		char	   *tmp;
		int			size = (attributeLength < 0)
		? VARSIZE((struct varlena *) attributeValue)	/* variable length */
		: attributeLength;		/* fixed length */

		tmp = (char *) palloc(size);
		memcpy(tmp, (void *) attributeValue, size);
		returnValue = (void *) tmp;
	}

	heap_close(relation);
	return returnValue;
}

/*
 * TypeDefaultRetrieve
 *
 *	  Given a type OID, return the typdefault field associated with that
 *	  type.  The result is a Datum, and points to palloc'd storage for
 *	  non-pass-by-value types.
 *
 * [identical to get_typdefault, expecting a (struct varlena *) as ret val.
 *	some day, either of the functions should be removed		 -ay 10/94]
 */
void *
TypeDefaultRetrieve(Oid typId)
{
	struct varlena *typDefault;
	int32		dataSize;
	HeapTuple	typeTuple;
	Form_pg_type type;
	int32		typByVal,
				typLen;
	void	   *returnValue;

	/*
	 * First, see if there is a non-null typdefault field (usually there isn't)
	 */
	typDefault = (struct varlena *)
		SearchSysCacheGetAttribute(TYPOID,
								   Anum_pg_type_typdefault,
								   ObjectIdGetDatum(typId),
								   0, 0, 0);

	if (typDefault == NULL)
	{
#ifdef	CACHEDEBUG
		elog(DEBUG, "TypeDefaultRetrieve: No extractable typdefault in %s(%d)",
			 cacheinfo[TYPOID].name, TYPOID);
#endif	 /* defined(CACHEDEBUG) */
		return NULL;
	}

	dataSize = VARSIZE(typDefault) - VARHDRSZ;

	/*
	 * Need the type's length and byVal fields.
	 *
	 * XXX silly to repeat the syscache search that SearchSysCacheGetAttribute
	 * just did --- but at present this path isn't taken often enough to
	 * make it worth fixing.
	 */
	typeTuple = SearchSysCacheTuple(TYPOID,
									ObjectIdGetDatum(typId),
									0, 0, 0);

	if (!HeapTupleIsValid(typeTuple))
	{
		/* should never get here, really... */
#ifdef	CACHEDEBUG
		elog(DEBUG, "TypeDefaultRetrieve: Lookup in %s(%d) failed",
			 cacheinfo[TYPOID].name, TYPOID);
#endif	 /* defined(CACHEDEBUG) */
		return NULL;
	}

	type = (Form_pg_type) GETSTRUCT(typeTuple);
	typLen = type->typlen;
	typByVal = type->typbyval;

	if (typByVal)
	{
		int8		i8;
		int16		i16;
		int32		i32 = 0;

		if (dataSize == typLen)
		{
			switch (typLen)
			{
				case sizeof(int8):
					memcpy((char *) &i8, VARDATA(typDefault), sizeof(int8));
					i32 = i8;
					break;
				case sizeof(int16):
					memcpy((char *) &i16, VARDATA(typDefault), sizeof(int16));
					i32 = i16;
					break;
				case sizeof(int32):
					memcpy((char *) &i32, VARDATA(typDefault), sizeof(int32));
					break;
			}
			returnValue = (void *) i32;
		}
		else
			returnValue = NULL;
	}
	else
	{
		if ((typLen < 0 && dataSize < 0) || dataSize != typLen)
			returnValue = NULL;
		else
		{
			returnValue = (void *) palloc(VARSIZE(typDefault));
			memcpy((char *) returnValue,
				   (char *) typDefault,
				   (int) VARSIZE(typDefault));
		}
	}

	return returnValue;
}
