* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.55 2001/05/09 23:13:35 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.67 2002/03/29 19:06:15 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
#include "postgres.h"
#include "access/tupmacs.h"
+#include "catalog/pg_amop.h"
+#include "catalog/pg_opclass.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"
+#include "nodes/makefuncs.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
+
/* ---------- AMOP CACHES ---------- */
/*
- * op_class
+ * op_in_opclass
*
- * Return t iff operator 'opno' is in operator class 'opclass' for
- * access method 'amopid'.
+ * Return t iff operator 'opno' is in operator class 'opclass'.
*/
bool
-op_class(Oid opno, Oid opclass, Oid amopid)
+op_in_opclass(Oid opno, Oid opclass)
{
return SearchSysCacheExists(AMOPOPID,
ObjectIdGetDatum(opclass),
ObjectIdGetDatum(opno),
- ObjectIdGetDatum(amopid),
- 0);
+ 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 ---------- */
/* watch this space...
*/
+/* ---------- 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 ---------- */
/*
}
/*
+ * 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.
+ *
+ * Raises error if operators cannot be found. Assuming that the operator
+ * had indeed been marked mergejoinable, this indicates that whoever marked
+ * it so was mistaken.
+ */
+void
+op_mergejoin_crossops(Oid opno, Oid *ltop, Oid *gtop,
+ RegProcedure *ltproc, RegProcedure *gtproc)
+{
+ HeapTuple tp;
+ Form_pg_operator optup;
+ Oid oprleft,
+ oprright;
+
+ /*
+ * Get the declared left and right operand types 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);
+ oprleft = optup->oprleft;
+ oprright = optup->oprright;
+ ReleaseSysCache(tp);
+
+ /*
+ * Look up the "<" operator with the same input types. If there isn't
+ * one, whoever marked the "=" operator mergejoinable was a loser.
+ */
+ tp = SearchSysCache(OPERNAME,
+ PointerGetDatum("<"),
+ ObjectIdGetDatum(oprleft),
+ ObjectIdGetDatum(oprright),
+ CharGetDatum('b'));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "op_mergejoin_crossops: mergejoin operator %u has no matching < operator",
+ opno);
+ optup = (Form_pg_operator) GETSTRUCT(tp);
+ *ltop = tp->t_data->t_oid;
+ if (ltproc)
+ *ltproc = optup->oprcode;
+ ReleaseSysCache(tp);
+
+ /*
+ * And the same for the ">" operator.
+ */
+ tp = SearchSysCache(OPERNAME,
+ PointerGetDatum(">"),
+ ObjectIdGetDatum(oprleft),
+ ObjectIdGetDatum(oprright),
+ CharGetDatum('b'));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "op_mergejoin_crossops: mergejoin operator %u has no matching > operator",
+ opno);
+ optup = (Form_pg_operator) GETSTRUCT(tp);
+ *gtop = tp->t_data->t_oid;
+ if (gtproc)
+ *gtproc = optup->oprcode;
+ ReleaseSysCache(tp);
+}
+
+/*
* op_hashjoinable
*
* Returns the hash operator corresponding to a hashjoinable operator,
/* ---------- 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);
+}
+
#ifdef NOT_USED
/*
* get_relnatts
else
return InvalidAttrNumber;
}
-
#endif
/*
* get_rel_name
- *
* Returns the name of a given relation.
*
- * Note: returns a palloc'd copy of the string, or NULL if no such operator.
+ * 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.
*/
char *
get_rel_name(Oid relid)
return NULL;
}
+/*
+ * 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;
+}
+
+/*
* get_typlen
*
* Given the type OID, return the length of the type.
else
return 'i';
}
-
#endif
char
/*
* get_typdefault
+ * Given a type OID, return the type's default value, if any.
+ *
+ * The result is a palloc'd expression node tree, or NULL if there
+ * is no defined default for the datatype.
*
- * Given a type OID, return the typdefault field associated with that
- * type, or Datum(NULL) if there is no typdefault. (This implies
- * that pass-by-value types can't have a default value that has
- * a representation of zero. Not worth fixing now.)
- * The result points to palloc'd storage for non-pass-by-value types.
+ * NB: caller should be prepared to coerce result to correct datatype;
+ * the returned expression tree might produce something of the wrong type.
*/
-Datum
+Node *
get_typdefault(Oid typid)
{
HeapTuple typeTuple;
Form_pg_type type;
- struct varlena *typDefault;
+ Datum datum;
bool isNull;
- int32 dataSize;
- int32 typLen;
- bool typByVal;
- Datum returnValue;
+ Node *expr;
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);
/*
- * First, see if there is a non-null typdefault field (usually there
- * isn't)
+ * typdefault and typdefaultbin are potentially null, so don't try to
+ * access 'em as struct fields. Must do it the hard way with
+ * SysCacheGetAttr.
*/
- typDefault = (struct varlena *)
- DatumGetPointer(SysCacheGetAttr(TYPEOID,
- typeTuple,
- Anum_pg_type_typdefault,
- &isNull));
+ datum = SysCacheGetAttr(TYPEOID,
+ typeTuple,
+ Anum_pg_type_typdefaultbin,
+ &isNull);
- if (isNull)
+ if (!isNull)
{
- ReleaseSysCache(typeTuple);
- return PointerGetDatum(NULL);
- }
-
- /*
- * Otherwise, extract/copy the value.
- */
- dataSize = VARSIZE(typDefault) - VARHDRSZ;
- typLen = type->typlen;
- typByVal = type->typbyval;
-
- if (typByVal)
- {
- if (dataSize == typLen)
- returnValue = fetch_att(VARDATA(typDefault), typByVal, typLen);
- else
- returnValue = PointerGetDatum(NULL);
+ /* We have an expression default */
+ expr = stringToNode(DatumGetCString(DirectFunctionCall1(textout,
+ datum)));
}
- else if (typLen < 0)
+ else
{
- /* variable-size type */
- if (dataSize < 0)
- returnValue = PointerGetDatum(NULL);
- else
+ /* Perhaps we have a plain literal default */
+ datum = SysCacheGetAttr(TYPEOID,
+ typeTuple,
+ Anum_pg_type_typdefault,
+ &isNull);
+
+ if (!isNull)
{
- returnValue = PointerGetDatum(palloc(VARSIZE(typDefault)));
- memcpy((char *) DatumGetPointer(returnValue),
- (char *) typDefault,
- (int) VARSIZE(typDefault));
+ char *strDefaultVal;
+
+ /* Convert text datum to C string */
+ strDefaultVal = DatumGetCString(DirectFunctionCall1(textout,
+ datum));
+ /* Convert C string to a value of the given type */
+ datum = OidFunctionCall3(type->typinput,
+ CStringGetDatum(strDefaultVal),
+ ObjectIdGetDatum(type->typelem),
+ Int32GetDatum(-1));
+ /* Build a Const node containing the value */
+ expr = (Node *) makeConst(typid,
+ type->typlen,
+ datum,
+ false,
+ type->typbyval,
+ false, /* not a set */
+ false);
+ pfree(strDefaultVal);
}
- }
- else
- {
- /* fixed-size pass-by-ref type */
- if (dataSize != typLen)
- returnValue = PointerGetDatum(NULL);
else
{
- returnValue = PointerGetDatum(palloc(dataSize));
- memcpy((char *) DatumGetPointer(returnValue),
- VARDATA(typDefault),
- (int) dataSize);
+ /* No default */
+ expr = NULL;
}
}
ReleaseSysCache(typeTuple);
- return returnValue;
+ return expr;
+}
+
+/*
+ * getBaseType
+ * If the given type is a domain, return its base type;
+ * otherwise return the type's own OID.
+ */
+Oid
+getBaseType(Oid typid)
+{
+ /*
+ * We loop to find the bottom base type in a stack of domains.
+ */
+ for (;;)
+ {
+ HeapTuple tup;
+ Form_pg_type typTup;
+
+ tup = SearchSysCache(TYPEOID,
+ ObjectIdGetDatum(typid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "getBaseType: failed to lookup type %u", typid);
+ typTup = (Form_pg_type) GETSTRUCT(tup);
+ if (typTup->typtype != 'd')
+ {
+ /* Not a domain, so done */
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ typid = typTup->typbasetype;
+ ReleaseSysCache(tup);
+ }
+
+ return typid;
}
/*
*/
if (typlen > 0)
return typlen;
+
/*
* type_maximum_size knows the encoding of typmod for some datatypes;
* don't duplicate that knowledge here.
if (maxwidth > 0)
{
/*
- * For BPCHAR, the max width is also the only width. Otherwise
- * we need to guess about the typical data width given the max.
- * A sliding scale for percentage of max width seems reasonable.
+ * For BPCHAR, the max width is also the only width. Otherwise we
+ * need to guess about the typical data width given the max. A
+ * sliding scale for percentage of max width seems reasonable.
*/
if (typid == BPCHAROID)
return maxwidth;
if (maxwidth <= 32)
return maxwidth; /* assume full width */
if (maxwidth < 1000)
- return 32 + (maxwidth - 32) / 2; /* assume 50% */
+ return 32 + (maxwidth - 32) / 2; /* assume 50% */
+
/*
* Beyond 1000, assume we're looking at something like
* "varchar(10000)" where the limit isn't actually reached often,
*/
return 32 + (1000 - 32) / 2;
}
+
/*
* Ooops, we have no idea ... wild guess time.
*/
else
return '\0';
}
-
#endif
/* ---------- STATISTICS CACHE ---------- */
0, 0);
if (HeapTupleIsValid(tp))
{
- int32 stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth;
+ int32 stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth;
ReleaseSysCache(tp);
if (stawidth > 0)
if (isnull)
elog(ERROR, "get_attstatsslot: stavalues is null");
statarray = DatumGetArrayTypeP(val);
+
/*
- * Do initial examination of the array. This produces a list
- * of text Datums --- ie, pointers into the text array value.
+ * Do initial examination of the array. This produces a list of
+ * text Datums --- ie, pointers into the text array value.
*/
deconstruct_array(statarray, false, -1, 'i', values, nvalues);
narrayelem = *nvalues;
+
/*
- * We now need to replace each text Datum by its internal equivalent.
+ * We now need to replace each text Datum by its internal
+ * equivalent.
*
* Get the type input proc and typelem for the column datatype.
*/
fmgr_info(((Form_pg_type) GETSTRUCT(typeTuple))->typinput, &inputproc);
typelem = ((Form_pg_type) GETSTRUCT(typeTuple))->typelem;
ReleaseSysCache(typeTuple);
+
/*
- * Do the conversions. The palloc'd array of Datums is reused
- * in place.
+ * Do the conversions. The palloc'd array of Datums is reused in
+ * place.
*/
for (j = 0; j < narrayelem; j++)
{
Int32GetDatum(atttypmod));
pfree(strval);
}
+
/*
* Free statarray if it's a detoasted copy.
*/
if (isnull)
elog(ERROR, "get_attstatsslot: stanumbers is null");
statarray = DatumGetArrayTypeP(val);
+
/*
- * We expect the array to be a 1-D float4 array; verify that.
- * We don't need to use deconstruct_array() since the array
- * data is just going to look like a C array of float4 values.
+ * We expect the array to be a 1-D float4 array; verify that. We
+ * don't need to use deconstruct_array() since the array data is
+ * just going to look like a C array of float4 values.
*/
narrayelem = ARR_DIMS(statarray)[0];
if (ARR_NDIM(statarray) != 1 || narrayelem <= 0 ||
*numbers = (float4 *) palloc(narrayelem * sizeof(float4));
memcpy(*numbers, ARR_DATA_PTR(statarray), narrayelem * sizeof(float4));
*nnumbers = narrayelem;
+
/*
* Free statarray if it's a detoasted copy.
*/
{
if (values)
{
- if (! get_typbyval(atttype))
+ if (!get_typbyval(atttype))
{
- int i;
+ int i;
for (i = 0; i < nvalues; i++)
pfree(DatumGetPointer(values[i]));
if (numbers)
pfree(numbers);
}
+
+/* ---------- PG_SHADOW CACHE ---------- */
+
+/*
+ * get_usesysid
+ *
+ * Given a user name, look up the user's sysid.
+ * Raises an error if no such user (rather than returning zero,
+ * which might possibly be a valid usesysid).
+ *
+ * Note: the type of usesysid is currently int4, but may change to Oid
+ * someday. It'd be reasonable to return zero on failure if we were
+ * using Oid ...
+ */
+int32
+get_usesysid(const char *username)
+{
+ int32 result;
+ HeapTuple userTup;
+
+ userTup = SearchSysCache(SHADOWNAME,
+ PointerGetDatum(username),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(userTup))
+ elog(ERROR, "user \"%s\" does not exist", username);
+
+ result = ((Form_pg_shadow) GETSTRUCT(userTup))->usesysid;
+
+ ReleaseSysCache(userTup);
+
+ return result;
+}