Skip to content
Snippets Groups Projects
lsyscache.c 30.4 KiB
Newer Older
/*-------------------------------------------------------------------------
 *
 *	  Convenience routines for common queries in the system catalog cache.
Bruce Momjian's avatar
Bruce Momjian committed
 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
Bruce Momjian's avatar
Bruce Momjian committed
 * Portions Copyright (c) 1994, Regents of the University of California
 *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.76 2002/07/12 18:43:18 tgl Exp $
 *	  Eventually, the index information should go through here, too.
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "catalog/pg_namespace.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_shadow.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_type.h"
Tom Lane's avatar
Tom Lane committed
#include "nodes/makefuncs.h"
#include "utils/array.h"
#include "utils/builtins.h"
Bruce Momjian's avatar
Bruce Momjian committed
#include "utils/lsyscache.h"
#include "utils/syscache.h"
/*				---------- AMOP CACHES ----------						 */
 *		Return t iff operator 'opno' is in operator class 'opclass'.
op_in_opclass(Oid opno, Oid opclass)
	return SearchSysCacheExists(AMOPOPID,
								ObjectIdGetDatum(opclass),
								ObjectIdGetDatum(opno),
								0, 0);
}

/*
 * op_requires_recheck
 *
 *		Return t if operator 'opno' requires a recheck when used as a
 *		member of opclass 'opclass' (ie, this opclass is lossy for this
 *		operator).
 *
 * Caller should already have verified that opno is a member of opclass,
 * therefore we raise an error if the tuple is not found.
 */
bool
op_requires_recheck(Oid opno, Oid opclass)
{
	HeapTuple	tp;
	Form_pg_amop amop_tup;
	bool		result;

	tp = SearchSysCache(AMOPOPID,
						ObjectIdGetDatum(opclass),
						ObjectIdGetDatum(opno),
						0, 0);
	if (!HeapTupleIsValid(tp))
		elog(ERROR, "op_requires_recheck: op %u is not a member of opclass %u",
			 opno, opclass);
	amop_tup = (Form_pg_amop) GETSTRUCT(tp);

	result = amop_tup->amopreqcheck;
	ReleaseSysCache(tp);
	return result;
/*				---------- ATTRIBUTE CACHES ----------					 */
 *
 *		Given the relation id and the attribute number,
 *		return the "attname" field from the attribute relation.
 *
 * Note: returns a palloc'd copy of the string, or NULL if no such operator.
get_attname(Oid relid, AttrNumber attnum)
{
	tp = SearchSysCache(ATTNUM,
						ObjectIdGetDatum(relid),
						Int16GetDatum(attnum),
						0, 0);
	if (HeapTupleIsValid(tp))
	{
		Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
		char	   *result;
		result = pstrdup(NameStr(att_tup->attname));
		ReleaseSysCache(tp);
		return result;
Bruce Momjian's avatar
Bruce Momjian committed
		return NULL;
 *
 *		Given the relation id and the attribute name,
 *		return the "attnum" field from the attribute relation.
 */
AttrNumber
get_attnum(Oid relid, char *attname)
{
	tp = SearchSysCache(ATTNAME,
						ObjectIdGetDatum(relid),
						PointerGetDatum(attname),
						0, 0);
	if (HeapTupleIsValid(tp))
	{
		Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
		result = att_tup->attnum;
		ReleaseSysCache(tp);
		return result;
Bruce Momjian's avatar
Bruce Momjian committed
		return InvalidAttrNumber;
 *
 *		Given the relation OID and the attribute number with the relation,
 *		return the attribute type OID.
 */
Oid
get_atttype(Oid relid, AttrNumber attnum)
{
	tp = SearchSysCache(ATTNUM,
						ObjectIdGetDatum(relid),
						Int16GetDatum(attnum),
						0, 0);
	if (HeapTupleIsValid(tp))
	{
		Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
		Oid			result;
		result = att_tup->atttypid;
		ReleaseSysCache(tp);
		return result;
}

/* This routine uses the attname instead of the attnum because it
 * replaces the routine find_atttype, which is called sometimes when
 * only the attname, not the attno, is available.
 */
bool
get_attisset(Oid relid, char *attname)
{
	tp = SearchSysCache(ATTNAME,
						ObjectIdGetDatum(relid),
						PointerGetDatum(attname),
						0, 0);
	if (HeapTupleIsValid(tp))
		Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
		bool		result;
		result = att_tup->attisset;
		ReleaseSysCache(tp);
		return result;
Bruce Momjian's avatar
Bruce Momjian committed
/*
Bruce Momjian's avatar
Bruce Momjian committed
 *
 *		Given the relation id and the attribute number,
 *		return the "atttypmod" field from the attribute relation.
 */
Bruce Momjian's avatar
Bruce Momjian committed
get_atttypmod(Oid relid, AttrNumber attnum)
{
Bruce Momjian's avatar
Bruce Momjian committed

	tp = SearchSysCache(ATTNUM,
						ObjectIdGetDatum(relid),
						Int16GetDatum(attnum),
						0, 0);
	if (HeapTupleIsValid(tp))
	{
		Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
		int32		result;
		result = att_tup->atttypmod;
		ReleaseSysCache(tp);
		return result;
Bruce Momjian's avatar
Bruce Momjian committed
	else
Bruce Momjian's avatar
Bruce Momjian committed
}

/*
 * get_atttypetypmod
 *
 *		A two-fer: given the relation id and the attribute number,
 *		fetch both type OID and atttypmod in a single cache lookup.
 *
 * Unlike the otherwise-similar get_atttype/get_atttypmod, this routine
 * raises an error if it can't obtain the information.
 */
void
get_atttypetypmod(Oid relid, AttrNumber attnum,
				  Oid *typid, int32 *typmod)
{
	HeapTuple	tp;
	Form_pg_attribute att_tup;

	tp = SearchSysCache(ATTNUM,
						ObjectIdGetDatum(relid),
						Int16GetDatum(attnum),
						0, 0);
	if (!HeapTupleIsValid(tp))
		elog(ERROR, "cache lookup failed for relation %u attribute %d",
			 relid, attnum);
	att_tup = (Form_pg_attribute) GETSTRUCT(tp);

	*typid = att_tup->atttypid;
	*typmod = att_tup->atttypmod;
	ReleaseSysCache(tp);
}

/*				---------- INDEX CACHE ----------						 */
/*				---------- OPCLASS CACHE ----------						 */

/*
 * opclass_is_btree
 *
 *		Returns TRUE iff the specified opclass is associated with the
 *		btree index access method.
 */
bool
opclass_is_btree(Oid opclass)
{
	HeapTuple	tp;
	Form_pg_opclass cla_tup;
	bool		result;

	tp = SearchSysCache(CLAOID,
						ObjectIdGetDatum(opclass),
						0, 0, 0);
	if (!HeapTupleIsValid(tp))
		elog(ERROR, "cache lookup failed for opclass %u", opclass);
	cla_tup = (Form_pg_opclass) GETSTRUCT(tp);

	result = (cla_tup->opcamid == BTREE_AM_OID);
	ReleaseSysCache(tp);
	return result;
}

/*				---------- OPERATOR CACHE ----------					 */
 *
 *		Returns the regproc id of the routine used to implement an
 *		operator given the operator oid.
 */
RegProcedure
get_opcode(Oid opno)
{
	tp = SearchSysCache(OPEROID,
						ObjectIdGetDatum(opno),
						0, 0, 0);
	if (HeapTupleIsValid(tp))
	{
		Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
		RegProcedure result;
		result = optup->oprcode;
		ReleaseSysCache(tp);
		return result;
		return (RegProcedure) InvalidOid;
 *	  returns the name of the operator with the given opno
 * Note: returns a palloc'd copy of the string, or NULL if no such operator.
	tp = SearchSysCache(OPEROID,
						ObjectIdGetDatum(opno),
						0, 0, 0);
	if (HeapTupleIsValid(tp))
		Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
		char	   *result;
		result = pstrdup(NameStr(optup->oprname));
		ReleaseSysCache(tp);
		return result;
 *
 *		Returns the left and right sort operators and types corresponding to a
 *		mergejoinable operator, or nil if the operator is not mergejoinable.
op_mergejoinable(Oid opno, Oid ltype, Oid rtype, Oid *leftOp, Oid *rightOp)
	tp = SearchSysCache(OPEROID,
						ObjectIdGetDatum(opno),
						0, 0, 0);
	if (HeapTupleIsValid(tp))
		Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);

		if (optup->oprlsortop &&
			optup->oprrsortop &&
			optup->oprleft == ltype &&
			optup->oprright == rtype)
		{
			*leftOp = optup->oprlsortop;
			*rightOp = optup->oprrsortop;
/*
 * op_mergejoin_crossops
 *
 *		Returns the cross-type comparison operators (ltype "<" rtype and
 *		ltype ">" rtype) for an operator previously determined to be
 *		mergejoinable.  Optionally, fetches the regproc ids of these
 *		operators, as well as their operator OIDs.
 */
void
op_mergejoin_crossops(Oid opno, Oid *ltop, Oid *gtop,
					  RegProcedure *ltproc, RegProcedure *gtproc)
{
	HeapTuple	tp;
	Form_pg_operator optup;

	/*
	 * Get the declared comparison operators of the operator.
	 */
	tp = SearchSysCache(OPEROID,
						ObjectIdGetDatum(opno),
						0, 0, 0);
	if (!HeapTupleIsValid(tp))	/* shouldn't happen */
		elog(ERROR, "op_mergejoin_crossops: operator %u not found", opno);
	optup = (Form_pg_operator) GETSTRUCT(tp);
	*ltop = optup->oprltcmpop;
	*gtop = optup->oprgtcmpop;
	/* Check < op provided */
	if (!OidIsValid(*ltop))
		elog(ERROR, "op_mergejoin_crossops: mergejoin operator %u has no matching < operator",
			 opno);
	if (ltproc)
		*ltproc = get_opcode(*ltop);
	/* Check > op provided */
	if (!OidIsValid(*gtop))
		elog(ERROR, "op_mergejoin_crossops: mergejoin operator %u has no matching > operator",
			 opno);
	if (gtproc)
		*gtproc = get_opcode(*gtop);
 *
 * Returns the hash operator corresponding to a hashjoinable operator,
 * or InvalidOid if the operator is not hashjoinable.
 */
Oid
op_hashjoinable(Oid opno, Oid ltype, Oid rtype)
{
	tp = SearchSysCache(OPEROID,
						ObjectIdGetDatum(opno),
						0, 0, 0);
	if (HeapTupleIsValid(tp))
	{
		Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);

		if (optup->oprcanhash &&
			optup->oprleft == ltype &&
			optup->oprright == rtype)
			result = opno;
		ReleaseSysCache(tp);
 * Get the provolatile flag for the operator's underlying function.
	RegProcedure funcid = get_opcode(opno);

	if (funcid == (RegProcedure) InvalidOid)
		elog(ERROR, "Operator OID %u does not exist", opno);

	return func_volatile((Oid) funcid);
 *
 *		Returns the corresponding commutator of an operator.
 */
Oid
get_commutator(Oid opno)
{
	tp = SearchSysCache(OPEROID,
						ObjectIdGetDatum(opno),
						0, 0, 0);
	if (HeapTupleIsValid(tp))
	{
		Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
		Oid			result;
		result = optup->oprcom;
		ReleaseSysCache(tp);
		return result;
 *
 *		Returns the corresponding negator of an operator.
	tp = SearchSysCache(OPEROID,
						ObjectIdGetDatum(opno),
						0, 0, 0);
	if (HeapTupleIsValid(tp))
	{
		Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
		Oid			result;
		result = optup->oprnegate;
		ReleaseSysCache(tp);
		return result;
 *
 *		Returns procedure id for computing selectivity of an operator.
 */
RegProcedure
get_oprrest(Oid opno)
{
	tp = SearchSysCache(OPEROID,
						ObjectIdGetDatum(opno),
						0, 0, 0);
	if (HeapTupleIsValid(tp))
	{
		Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
		RegProcedure result;
		result = optup->oprrest;
		ReleaseSysCache(tp);
		return result;
		return (RegProcedure) InvalidOid;
 *
 *		Returns procedure id for computing selectivity of a join.
 */
RegProcedure
get_oprjoin(Oid opno)
{
	tp = SearchSysCache(OPEROID,
						ObjectIdGetDatum(opno),
						0, 0, 0);
	if (HeapTupleIsValid(tp))
	{
		Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
		RegProcedure result;
		result = optup->oprjoin;
		ReleaseSysCache(tp);
		return result;
		return (RegProcedure) InvalidOid;
/*				---------- FUNCTION CACHE ----------					 */

/*
 * get_func_name
 *	  returns the name of the function with the given funcid
 *
 * Note: returns a palloc'd copy of the string, or NULL if no such function.
 */
char *
get_func_name(Oid funcid)
{
	HeapTuple	tp;

	tp = SearchSysCache(PROCOID,
						ObjectIdGetDatum(funcid),
						0, 0, 0);
	if (HeapTupleIsValid(tp))
	{
		Form_pg_proc functup = (Form_pg_proc) GETSTRUCT(tp);
		char	   *result;

		result = pstrdup(NameStr(functup->proname));
		ReleaseSysCache(tp);
		return result;
	}
	else
		return NULL;
}

/*
 * get_func_rettype
 *		Given procedure id, return the function's result type.
 */
Oid
get_func_rettype(Oid funcid)
{
	tp = SearchSysCache(PROCOID,
						ObjectIdGetDatum(funcid),
						0, 0, 0);
	if (!HeapTupleIsValid(tp))
		elog(ERROR, "Function OID %u does not exist", funcid);

	result = ((Form_pg_proc) GETSTRUCT(tp))->prorettype;
	ReleaseSysCache(tp);
	return result;
/*
 * get_func_retset
 *		Given procedure id, return the function's proretset flag.
 */
bool
get_func_retset(Oid funcid)
{
	HeapTuple	tp;
	bool		result;

	tp = SearchSysCache(PROCOID,
						ObjectIdGetDatum(funcid),
						0, 0, 0);
	if (!HeapTupleIsValid(tp))
		elog(ERROR, "Function OID %u does not exist", funcid);

	result = ((Form_pg_proc) GETSTRUCT(tp))->proretset;
	ReleaseSysCache(tp);
	return result;
}

 * func_volatile
 *		Given procedure id, return the function's provolatile flag.
	tp = SearchSysCache(PROCOID,
						ObjectIdGetDatum(funcid),
						0, 0, 0);
	if (!HeapTupleIsValid(tp))
		elog(ERROR, "Function OID %u does not exist", funcid);

	result = ((Form_pg_proc) GETSTRUCT(tp))->provolatile;
	ReleaseSysCache(tp);
	return result;
/*				---------- RELATION CACHE ----------					 */
/*
 * get_relname_relid
 *		Given name and namespace of a relation, look up the OID.
 *
 * Returns InvalidOid if there is no such relation.
 */
Oid
get_relname_relid(const char *relname, Oid relnamespace)
{
	return GetSysCacheOid(RELNAMENSP,
						  PointerGetDatum(relname),
						  ObjectIdGetDatum(relnamespace),
						  0, 0);
}

/*
 * get_system_catalog_relid
 *		Get the OID of a system catalog identified by name.
 */
Oid
get_system_catalog_relid(const char *catname)
{
	Oid		relid;

	relid = GetSysCacheOid(RELNAMENSP,
						   PointerGetDatum(catname),
						   ObjectIdGetDatum(PG_CATALOG_NAMESPACE),
						   0, 0);
	if (!OidIsValid(relid))
		elog(ERROR, "get_system_catalog_relid: cannot find %s", catname);

	return relid;
}

 *
 *		Returns the number of attributes for a given relation.
 */
int
get_relnatts(Oid relid)
{
	tp = SearchSysCache(RELOID,
						ObjectIdGetDatum(relid),
						0, 0, 0);
	if (HeapTupleIsValid(tp))
	{
		Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
		int			result;
		result = reltup->relnatts;
		ReleaseSysCache(tp);
		return result;
Bruce Momjian's avatar
Bruce Momjian committed
		return InvalidAttrNumber;
 *		Returns the name of a given relation.
 * Returns a palloc'd copy of the string, or NULL if no such relation.
 *
 * NOTE: since relation name is not unique, be wary of code that uses this
 * for anything except preparing error messages.
get_rel_name(Oid relid)
{
	tp = SearchSysCache(RELOID,
						ObjectIdGetDatum(relid),
						0, 0, 0);
	if (HeapTupleIsValid(tp))
	{
		Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
		char	   *result;
		result = pstrdup(NameStr(reltup->relname));
		ReleaseSysCache(tp);
		return result;
Bruce Momjian's avatar
Bruce Momjian committed
		return NULL;
/*
 * get_rel_namespace
 *
 *		Returns the pg_namespace OID associated with a given relation.
 */
Oid
get_rel_namespace(Oid relid)
{
	HeapTuple	tp;

	tp = SearchSysCache(RELOID,
						ObjectIdGetDatum(relid),
						0, 0, 0);
	if (HeapTupleIsValid(tp))
	{
		Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
		Oid		result;

		result = reltup->relnamespace;
		ReleaseSysCache(tp);
		return result;
	}
	else
		return InvalidOid;
}

/*
 * get_rel_type_id
 *
 *		Returns the pg_type OID associated with a given relation.
 *
 * Note: not all pg_class entries have associated pg_type OIDs; so be
 * careful to check for InvalidOid result.
 */
Oid
get_rel_type_id(Oid relid)
{
	HeapTuple	tp;

	tp = SearchSysCache(RELOID,
						ObjectIdGetDatum(relid),
						0, 0, 0);
	if (HeapTupleIsValid(tp))
	{
		Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
		Oid		result;

		result = reltup->reltype;
		ReleaseSysCache(tp);
		return result;
	}
	else
		return InvalidOid;
}

/*				---------- TYPE CACHE ----------						 */
/*
 * get_typisdefined
 *
 *		Given the type OID, determine whether the type is defined
 *		(if not, it's only a shell).
 */
bool
get_typisdefined(Oid typid)
{
	HeapTuple	tp;

	tp = SearchSysCache(TYPEOID,
						ObjectIdGetDatum(typid),
						0, 0, 0);
	if (HeapTupleIsValid(tp))
	{
		Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
		bool		result;

		result = typtup->typisdefined;
		ReleaseSysCache(tp);
		return result;
	}
	else
		return false;
}

 *
 *		Given the type OID, return the length of the type.
 */
int16
get_typlen(Oid typid)
{
	tp = SearchSysCache(TYPEOID,
						ObjectIdGetDatum(typid),
						0, 0, 0);
	if (HeapTupleIsValid(tp))
	{
		Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
		int16		result;
		result = typtup->typlen;
		ReleaseSysCache(tp);
		return result;
 *
 *		Given the type OID, determine whether the type is returned by value or
 *		not.  Returns true if by value, false if by reference.
 */
bool
get_typbyval(Oid typid)
{
	tp = SearchSysCache(TYPEOID,
						ObjectIdGetDatum(typid),
						0, 0, 0);
	if (HeapTupleIsValid(tp))
	{
		Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
		bool		result;
		result = typtup->typbyval;
		ReleaseSysCache(tp);
		return result;
Bruce Momjian's avatar
Bruce Momjian committed
		return false;
/*
 * get_typlenbyval
 *
 *		A two-fer: given the type OID, return both typlen and typbyval.
 *
 *		Since both pieces of info are needed to know how to copy a Datum,
 *		many places need both.	Might as well get them with one cache lookup
 *		instead of two.  Also, this routine raises an error instead of
 *		returning a bogus value when given a bad type OID.
 */
void
get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval)
{
	HeapTuple	tp;
	Form_pg_type typtup;

	tp = SearchSysCache(TYPEOID,
						ObjectIdGetDatum(typid),
						0, 0, 0);
	if (!HeapTupleIsValid(tp))
		elog(ERROR, "cache lookup failed for type %u", typid);
	typtup = (Form_pg_type) GETSTRUCT(tp);
	*typlen = typtup->typlen;
	*typbyval = typtup->typbyval;
	ReleaseSysCache(tp);
}

char
get_typalign(Oid typid)
{
	tp = SearchSysCache(TYPEOID,
						ObjectIdGetDatum(typid),
						0, 0, 0);
	if (HeapTupleIsValid(tp))
	{
		Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
		char		result;
		result = typtup->typalign;
		ReleaseSysCache(tp);
		return result;
Bruce Momjian's avatar
Bruce Momjian committed
		return 'i';
char
get_typstorage(Oid typid)
{
	HeapTuple	tp;

	tp = SearchSysCache(TYPEOID,
						ObjectIdGetDatum(typid),
						0, 0, 0);
	if (HeapTupleIsValid(tp))
	{
		Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
		char		result;

		result = typtup->typstorage;
		ReleaseSysCache(tp);
		return result;
	}
	else
		return 'p';
}

 *	  Given a type OID, return the type's default value, if any.
Tom Lane's avatar
Tom Lane committed
 *
 *	  The result is a palloc'd expression node tree, or NULL if there
 *	  is no defined default for the datatype.
 *
 * NB: caller should be prepared to coerce result to correct datatype;
 * the returned expression tree might produce something of the wrong type.
Tom Lane's avatar
Tom Lane committed
get_typdefault(Oid typid)
	HeapTuple	typeTuple;
	Form_pg_type type;
	typeTuple = SearchSysCache(TYPEOID,
							   ObjectIdGetDatum(typid),
							   0, 0, 0);
	if (!HeapTupleIsValid(typeTuple))
		elog(ERROR, "get_typdefault: failed to lookup type %u", typid);
	type = (Form_pg_type) GETSTRUCT(typeTuple);
Tom Lane's avatar
Tom Lane committed
	 * typdefault and typdefaultbin are potentially null, so don't try to
	 * access 'em as struct fields. Must do it the hard way with
	 * SysCacheGetAttr.
	datum = SysCacheGetAttr(TYPEOID,
							typeTuple,
							Anum_pg_type_typdefaultbin,
							&isNull);
Tom Lane's avatar
Tom Lane committed
	if (!isNull)
	{
		/* We have an expression default */
		expr = stringToNode(DatumGetCString(DirectFunctionCall1(textout,
																datum)));
	}