* lsyscache.c
* Convenience routines for common queries in the system catalog cache.
*
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.96 2003/06/22 22:04:54 tgl Exp $
+ * src/backend/utils/cache/lsyscache.c
*
* NOTES
* Eventually, the index information should go through here, too.
*-------------------------------------------------------------------------
*/
#include "postgres.h"
-#include "miscadmin.h"
#include "access/hash.h"
-#include "access/tupmacs.h"
+#include "access/nbtree.h"
+#include "bootstrap/bootstrap.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
+#include "catalog/pg_collation.h"
+#include "catalog/pg_constraint.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
-#include "catalog/pg_shadow.h"
+#include "catalog/pg_range.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_type.h"
+#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "utils/array.h"
#include "utils/builtins.h"
-#include "utils/catcache.h"
#include "utils/datum.h"
+#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
+#include "utils/rel.h"
#include "utils/syscache.h"
+#include "utils/typcache.h"
+
+/* Hook for plugins to get control in get_attavgwidth() */
+get_attavgwidth_hook_type get_attavgwidth_hook = NULL;
/* ---------- AMOP CACHES ---------- */
/*
- * op_in_opclass
+ * op_in_opfamily
+ *
+ * Return t iff operator 'opno' is in operator family 'opfamily'.
*
- * Return t iff operator 'opno' is in operator class 'opclass'.
+ * This function only considers search operators, not ordering operators.
*/
bool
-op_in_opclass(Oid opno, Oid opclass)
+op_in_opfamily(Oid opno, Oid opfamily)
{
- return SearchSysCacheExists(AMOPOPID,
- ObjectIdGetDatum(opno),
- ObjectIdGetDatum(opclass),
- 0, 0);
+ return SearchSysCacheExists3(AMOPOPID,
+ ObjectIdGetDatum(opno),
+ CharGetDatum(AMOP_SEARCH),
+ ObjectIdGetDatum(opfamily));
}
/*
- * op_requires_recheck
+ * get_op_opfamily_strategy
*
- * Return t if operator 'opno' requires a recheck when used as a
- * member of opclass 'opclass' (ie, this opclass is lossy for this
- * operator).
+ * Get the operator's strategy number within the specified opfamily,
+ * or 0 if it's not a member of the opfamily.
*
- * Caller should already have verified that opno is a member of opclass,
- * therefore we raise an error if the tuple is not found.
+ * This function only considers search operators, not ordering operators.
*/
-bool
-op_requires_recheck(Oid opno, Oid opclass)
+int
+get_op_opfamily_strategy(Oid opno, Oid opfamily)
{
HeapTuple tp;
Form_pg_amop amop_tup;
- bool result;
+ int result;
- tp = SearchSysCache(AMOPOPID,
- ObjectIdGetDatum(opno),
- ObjectIdGetDatum(opclass),
- 0, 0);
+ tp = SearchSysCache3(AMOPOPID,
+ ObjectIdGetDatum(opno),
+ CharGetDatum(AMOP_SEARCH),
+ ObjectIdGetDatum(opfamily));
if (!HeapTupleIsValid(tp))
- elog(ERROR, "op_requires_recheck: op %u is not a member of opclass %u",
- opno, opclass);
+ return 0;
amop_tup = (Form_pg_amop) GETSTRUCT(tp);
+ result = amop_tup->amopstrategy;
+ ReleaseSysCache(tp);
+ return result;
+}
+
+/*
+ * get_op_opfamily_sortfamily
+ *
+ * If the operator is an ordering operator within the specified opfamily,
+ * return its amopsortfamily OID; else return InvalidOid.
+ */
+Oid
+get_op_opfamily_sortfamily(Oid opno, Oid opfamily)
+{
+ HeapTuple tp;
+ Form_pg_amop amop_tup;
+ Oid result;
- result = amop_tup->amopreqcheck;
+ tp = SearchSysCache3(AMOPOPID,
+ ObjectIdGetDatum(opno),
+ CharGetDatum(AMOP_ORDER),
+ ObjectIdGetDatum(opfamily));
+ if (!HeapTupleIsValid(tp))
+ return InvalidOid;
+ amop_tup = (Form_pg_amop) GETSTRUCT(tp);
+ result = amop_tup->amopsortfamily;
ReleaseSysCache(tp);
return result;
}
/*
- * get_opclass_member
+ * get_op_opfamily_properties
+ *
+ * Get the operator's strategy number and declared input data types
+ * within the specified opfamily.
+ *
+ * Caller should already have verified that opno is a member of opfamily,
+ * therefore we raise an error if the tuple is not found.
+ */
+void
+get_op_opfamily_properties(Oid opno, Oid opfamily, bool ordering_op,
+ int *strategy,
+ Oid *lefttype,
+ Oid *righttype)
+{
+ HeapTuple tp;
+ Form_pg_amop amop_tup;
+
+ tp = SearchSysCache3(AMOPOPID,
+ ObjectIdGetDatum(opno),
+ CharGetDatum(ordering_op ? AMOP_ORDER : AMOP_SEARCH),
+ ObjectIdGetDatum(opfamily));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "operator %u is not a member of opfamily %u",
+ opno, opfamily);
+ amop_tup = (Form_pg_amop) GETSTRUCT(tp);
+ *strategy = amop_tup->amopstrategy;
+ *lefttype = amop_tup->amoplefttype;
+ *righttype = amop_tup->amoprighttype;
+ ReleaseSysCache(tp);
+}
+
+/*
+ * get_opfamily_member
* Get the OID of the operator that implements the specified strategy
- * for the specified opclass.
+ * with the specified datatypes for the specified opfamily.
*
* Returns InvalidOid if there is no pg_amop entry for the given keys.
*/
Oid
-get_opclass_member(Oid opclass, int16 strategy)
+get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype,
+ int16 strategy)
{
HeapTuple tp;
Form_pg_amop amop_tup;
Oid result;
- tp = SearchSysCache(AMOPSTRATEGY,
- ObjectIdGetDatum(opclass),
- Int16GetDatum(strategy),
- 0, 0);
+ tp = SearchSysCache4(AMOPSTRATEGY,
+ ObjectIdGetDatum(opfamily),
+ ObjectIdGetDatum(lefttype),
+ ObjectIdGetDatum(righttype),
+ Int16GetDatum(strategy));
if (!HeapTupleIsValid(tp))
return InvalidOid;
amop_tup = (Form_pg_amop) GETSTRUCT(tp);
}
/*
- * get_op_hash_function
- * Get the OID of the datatype-specific hash function associated with
- * a hashable equality operator.
+ * get_ordering_op_properties
+ * Given the OID of an ordering operator (a btree "<" or ">" operator),
+ * determine its opfamily, its declared input datatype, and its
+ * strategy number (BTLessStrategyNumber or BTGreaterStrategyNumber).
+ *
+ * Returns TRUE if successful, FALSE if no matching pg_amop entry exists.
+ * (This indicates that the operator is not a valid ordering operator.)
+ *
+ * Note: the operator could be registered in multiple families, for example
+ * if someone were to build a "reverse sort" opfamily. This would result in
+ * uncertainty as to whether "ORDER BY USING op" would default to NULLS FIRST
+ * or NULLS LAST, as well as inefficient planning due to failure to match up
+ * pathkeys that should be the same. So we want a determinate result here.
+ * Because of the way the syscache search works, we'll use the interpretation
+ * associated with the opfamily with smallest OID, which is probably
+ * determinate enough. Since there is no longer any particularly good reason
+ * to build reverse-sort opfamilies, it doesn't seem worth expending any
+ * additional effort on ensuring consistency.
+ */
+bool
+get_ordering_op_properties(Oid opno,
+ Oid *opfamily, Oid *opcintype, int16 *strategy)
+{
+ bool result = false;
+ CatCList *catlist;
+ int i;
+
+ /* ensure outputs are initialized on failure */
+ *opfamily = InvalidOid;
+ *opcintype = InvalidOid;
+ *strategy = 0;
+
+ /*
+ * Search pg_amop to see if the target operator is registered as the "<"
+ * or ">" operator of any btree opfamily.
+ */
+ catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
+
+ for (i = 0; i < catlist->n_members; i++)
+ {
+ HeapTuple tuple = &catlist->members[i]->tuple;
+ Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
+
+ /* must be btree */
+ if (aform->amopmethod != BTREE_AM_OID)
+ continue;
+
+ if (aform->amopstrategy == BTLessStrategyNumber ||
+ aform->amopstrategy == BTGreaterStrategyNumber)
+ {
+ /* Found it ... should have consistent input types */
+ if (aform->amoplefttype == aform->amoprighttype)
+ {
+ /* Found a suitable opfamily, return info */
+ *opfamily = aform->amopfamily;
+ *opcintype = aform->amoplefttype;
+ *strategy = aform->amopstrategy;
+ result = true;
+ break;
+ }
+ }
+ }
+
+ ReleaseSysCacheList(catlist);
+
+ return result;
+}
+
+/*
+ * get_sort_function_for_ordering_op
+ * Get the OID of the datatype-specific btree sort support function,
+ * or if there is none, the btree comparison function,
+ * associated with an ordering operator (a "<" or ">" operator).
+ *
+ * *sortfunc receives the support or comparison function OID.
+ * *issupport is set TRUE if it's a support func, FALSE if a comparison func.
+ * *reverse is set FALSE if the operator is "<", TRUE if it's ">"
+ * (indicating that comparison results must be negated before use).
+ *
+ * Returns TRUE if successful, FALSE if no btree function can be found.
+ * (This indicates that the operator is not a valid ordering operator.)
+ */
+bool
+get_sort_function_for_ordering_op(Oid opno, Oid *sortfunc,
+ bool *issupport, bool *reverse)
+{
+ Oid opfamily;
+ Oid opcintype;
+ int16 strategy;
+
+ /* Find the operator in pg_amop */
+ if (get_ordering_op_properties(opno,
+ &opfamily, &opcintype, &strategy))
+ {
+ /* Found a suitable opfamily, get matching support function */
+ *sortfunc = get_opfamily_proc(opfamily,
+ opcintype,
+ opcintype,
+ BTSORTSUPPORT_PROC);
+ if (OidIsValid(*sortfunc))
+ *issupport = true;
+ else
+ {
+ /* opfamily doesn't provide sort support, get comparison func */
+ *sortfunc = get_opfamily_proc(opfamily,
+ opcintype,
+ opcintype,
+ BTORDER_PROC);
+ if (!OidIsValid(*sortfunc)) /* should not happen */
+ elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
+ BTORDER_PROC, opcintype, opcintype, opfamily);
+ *issupport = false;
+ }
+ *reverse = (strategy == BTGreaterStrategyNumber);
+ return true;
+ }
+
+ /* ensure outputs are set on failure */
+ *sortfunc = InvalidOid;
+ *issupport = false;
+ *reverse = false;
+ return false;
+}
+
+/*
+ * get_equality_op_for_ordering_op
+ * Get the OID of the datatype-specific btree equality operator
+ * associated with an ordering operator (a "<" or ">" operator).
+ *
+ * If "reverse" isn't NULL, also set *reverse to FALSE if the operator is "<",
+ * TRUE if it's ">"
*
- * Returns InvalidOid if no hash function can be found. (This indicates
- * that the operator should not have been marked oprcanhash.)
+ * Returns InvalidOid if no matching equality operator can be found.
+ * (This indicates that the operator is not a valid ordering operator.)
+ */
+Oid
+get_equality_op_for_ordering_op(Oid opno, bool *reverse)
+{
+ Oid result = InvalidOid;
+ Oid opfamily;
+ Oid opcintype;
+ int16 strategy;
+
+ /* Find the operator in pg_amop */
+ if (get_ordering_op_properties(opno,
+ &opfamily, &opcintype, &strategy))
+ {
+ /* Found a suitable opfamily, get matching equality operator */
+ result = get_opfamily_member(opfamily,
+ opcintype,
+ opcintype,
+ BTEqualStrategyNumber);
+ if (reverse)
+ *reverse = (strategy == BTGreaterStrategyNumber);
+ }
+
+ return result;
+}
+
+/*
+ * get_ordering_op_for_equality_op
+ * Get the OID of a datatype-specific btree ordering operator
+ * associated with an equality operator. (If there are multiple
+ * possibilities, assume any one will do.)
+ *
+ * This function is used when we have to sort data before unique-ifying,
+ * and don't much care which sorting op is used as long as it's compatible
+ * with the intended equality operator. Since we need a sorting operator,
+ * it should be single-data-type even if the given operator is cross-type.
+ * The caller specifies whether to find an op for the LHS or RHS data type.
+ *
+ * Returns InvalidOid if no matching ordering operator can be found.
*/
Oid
-get_op_hash_function(Oid opno)
+get_ordering_op_for_equality_op(Oid opno, bool use_lhs_type)
{
+ Oid result = InvalidOid;
CatCList *catlist;
int i;
- HeapTuple tuple;
- Oid opclass = InvalidOid;
/*
* Search pg_amop to see if the target operator is registered as the "="
- * operator of any hash opclass. If the operator is registered in
- * multiple opclasses, assume we can use the associated hash function
- * from any one.
+ * operator of any btree opfamily.
*/
- catlist = SearchSysCacheList(AMOPOPID, 1,
- ObjectIdGetDatum(opno),
- 0, 0, 0);
+ catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
for (i = 0; i < catlist->n_members; i++)
{
- Form_pg_amop aform;
+ HeapTuple tuple = &catlist->members[i]->tuple;
+ Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
- tuple = &catlist->members[i]->tuple;
- aform = (Form_pg_amop) GETSTRUCT(tuple);
+ /* must be btree */
+ if (aform->amopmethod != BTREE_AM_OID)
+ continue;
- if (aform->amopstrategy == HTEqualStrategyNumber &&
- opclass_is_hash(aform->amopclaid))
+ if (aform->amopstrategy == BTEqualStrategyNumber)
{
- opclass = aform->amopclaid;
- break;
+ /* Found a suitable opfamily, get matching ordering operator */
+ Oid typid;
+
+ typid = use_lhs_type ? aform->amoplefttype : aform->amoprighttype;
+ result = get_opfamily_member(aform->amopfamily,
+ typid, typid,
+ BTLessStrategyNumber);
+ if (OidIsValid(result))
+ break;
+ /* failure probably shouldn't happen, but keep looking if so */
+ }
+ }
+
+ ReleaseSysCacheList(catlist);
+
+ return result;
+}
+
+/*
+ * get_mergejoin_opfamilies
+ * Given a putatively mergejoinable operator, return a list of the OIDs
+ * of the btree opfamilies in which it represents equality.
+ *
+ * It is possible (though at present unusual) for an operator to be equality
+ * in more than one opfamily, hence the result is a list. This also lets us
+ * return NIL if the operator is not found in any opfamilies.
+ *
+ * The planner currently uses simple equal() tests to compare the lists
+ * returned by this function, which makes the list order relevant, though
+ * strictly speaking it should not be. Because of the way syscache list
+ * searches are handled, in normal operation the result will be sorted by OID
+ * so everything works fine. If running with system index usage disabled,
+ * the result ordering is unspecified and hence the planner might fail to
+ * recognize optimization opportunities ... but that's hardly a scenario in
+ * which performance is good anyway, so there's no point in expending code
+ * or cycles here to guarantee the ordering in that case.
+ */
+List *
+get_mergejoin_opfamilies(Oid opno)
+{
+ List *result = NIL;
+ CatCList *catlist;
+ int i;
+
+ /*
+ * Search pg_amop to see if the target operator is registered as the "="
+ * operator of any btree opfamily.
+ */
+ catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
+
+ for (i = 0; i < catlist->n_members; i++)
+ {
+ HeapTuple tuple = &catlist->members[i]->tuple;
+ Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
+
+ /* must be btree equality */
+ if (aform->amopmethod == BTREE_AM_OID &&
+ aform->amopstrategy == BTEqualStrategyNumber)
+ result = lappend_oid(result, aform->amopfamily);
+ }
+
+ ReleaseSysCacheList(catlist);
+
+ return result;
+}
+
+/*
+ * get_compatible_hash_operators
+ * Get the OID(s) of hash equality operator(s) compatible with the given
+ * operator, but operating on its LHS and/or RHS datatype.
+ *
+ * An operator for the LHS type is sought and returned into *lhs_opno if
+ * lhs_opno isn't NULL. Similarly, an operator for the RHS type is sought
+ * and returned into *rhs_opno if rhs_opno isn't NULL.
+ *
+ * If the given operator is not cross-type, the results should be the same
+ * operator, but in cross-type situations they will be different.
+ *
+ * Returns true if able to find the requested operator(s), false if not.
+ * (This indicates that the operator should not have been marked oprcanhash.)
+ */
+bool
+get_compatible_hash_operators(Oid opno,
+ Oid *lhs_opno, Oid *rhs_opno)
+{
+ bool result = false;
+ CatCList *catlist;
+ int i;
+
+ /* Ensure output args are initialized on failure */
+ if (lhs_opno)
+ *lhs_opno = InvalidOid;
+ if (rhs_opno)
+ *rhs_opno = InvalidOid;
+
+ /*
+ * Search pg_amop to see if the target operator is registered as the "="
+ * operator of any hash opfamily. If the operator is registered in
+ * multiple opfamilies, assume we can use any one.
+ */
+ catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
+
+ for (i = 0; i < catlist->n_members; i++)
+ {
+ HeapTuple tuple = &catlist->members[i]->tuple;
+ Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
+
+ if (aform->amopmethod == HASH_AM_OID &&
+ aform->amopstrategy == HTEqualStrategyNumber)
+ {
+ /* No extra lookup needed if given operator is single-type */
+ if (aform->amoplefttype == aform->amoprighttype)
+ {
+ if (lhs_opno)
+ *lhs_opno = opno;
+ if (rhs_opno)
+ *rhs_opno = opno;
+ result = true;
+ break;
+ }
+
+ /*
+ * Get the matching single-type operator(s). Failure probably
+ * shouldn't happen --- it implies a bogus opfamily --- but
+ * continue looking if so.
+ */
+ if (lhs_opno)
+ {
+ *lhs_opno = get_opfamily_member(aform->amopfamily,
+ aform->amoplefttype,
+ aform->amoplefttype,
+ HTEqualStrategyNumber);
+ if (!OidIsValid(*lhs_opno))
+ continue;
+ /* Matching LHS found, done if caller doesn't want RHS */
+ if (!rhs_opno)
+ {
+ result = true;
+ break;
+ }
+ }
+ if (rhs_opno)
+ {
+ *rhs_opno = get_opfamily_member(aform->amopfamily,
+ aform->amoprighttype,
+ aform->amoprighttype,
+ HTEqualStrategyNumber);
+ if (!OidIsValid(*rhs_opno))
+ {
+ /* Forget any LHS operator from this opfamily */
+ if (lhs_opno)
+ *lhs_opno = InvalidOid;
+ continue;
+ }
+ /* Matching RHS found, so done */
+ result = true;
+ break;
+ }
}
}
ReleaseSysCacheList(catlist);
- if (OidIsValid(opclass))
+ return result;
+}
+
+/*
+ * get_op_hash_functions
+ * Get the OID(s) of hash support function(s) compatible with the given
+ * operator, operating on its LHS and/or RHS datatype as required.
+ *
+ * A function for the LHS type is sought and returned into *lhs_procno if
+ * lhs_procno isn't NULL. Similarly, a function for the RHS type is sought
+ * and returned into *rhs_procno if rhs_procno isn't NULL.
+ *
+ * If the given operator is not cross-type, the results should be the same
+ * function, but in cross-type situations they will be different.
+ *
+ * Returns true if able to find the requested function(s), false if not.
+ * (This indicates that the operator should not have been marked oprcanhash.)
+ */
+bool
+get_op_hash_functions(Oid opno,
+ RegProcedure *lhs_procno, RegProcedure *rhs_procno)
+{
+ bool result = false;
+ CatCList *catlist;
+ int i;
+
+ /* Ensure output args are initialized on failure */
+ if (lhs_procno)
+ *lhs_procno = InvalidOid;
+ if (rhs_procno)
+ *rhs_procno = InvalidOid;
+
+ /*
+ * Search pg_amop to see if the target operator is registered as the "="
+ * operator of any hash opfamily. If the operator is registered in
+ * multiple opfamilies, assume we can use any one.
+ */
+ catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
+
+ for (i = 0; i < catlist->n_members; i++)
{
- /* Found a suitable opclass, get its hash support function */
- tuple = SearchSysCache(AMPROCNUM,
- ObjectIdGetDatum(opclass),
- Int16GetDatum(HASHPROC),
- 0, 0);
- if (HeapTupleIsValid(tuple))
+ HeapTuple tuple = &catlist->members[i]->tuple;
+ Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
+
+ if (aform->amopmethod == HASH_AM_OID &&
+ aform->amopstrategy == HTEqualStrategyNumber)
{
- Form_pg_amproc aform = (Form_pg_amproc) GETSTRUCT(tuple);
- RegProcedure result;
+ /*
+ * Get the matching support function(s). Failure probably
+ * shouldn't happen --- it implies a bogus opfamily --- but
+ * continue looking if so.
+ */
+ if (lhs_procno)
+ {
+ *lhs_procno = get_opfamily_proc(aform->amopfamily,
+ aform->amoplefttype,
+ aform->amoplefttype,
+ HASHPROC);
+ if (!OidIsValid(*lhs_procno))
+ continue;
+ /* Matching LHS found, done if caller doesn't want RHS */
+ if (!rhs_procno)
+ {
+ result = true;
+ break;
+ }
+ /* Only one lookup needed if given operator is single-type */
+ if (aform->amoplefttype == aform->amoprighttype)
+ {
+ *rhs_procno = *lhs_procno;
+ result = true;
+ break;
+ }
+ }
+ if (rhs_procno)
+ {
+ *rhs_procno = get_opfamily_proc(aform->amopfamily,
+ aform->amoprighttype,
+ aform->amoprighttype,
+ HASHPROC);
+ if (!OidIsValid(*rhs_procno))
+ {
+ /* Forget any LHS function from this opfamily */
+ if (lhs_procno)
+ *lhs_procno = InvalidOid;
+ continue;
+ }
+ /* Matching RHS found, so done */
+ result = true;
+ break;
+ }
+ }
+ }
- result = aform->amproc;
- ReleaseSysCache(tuple);
- Assert(RegProcedureIsValid(result));
- return result;
+ ReleaseSysCacheList(catlist);
+
+ return result;
+}
+
+/*
+ * get_op_btree_interpretation
+ * Given an operator's OID, find out which btree opfamilies it belongs to,
+ * and what properties it has within each one. The results are returned
+ * as a palloc'd list of OpBtreeInterpretation structs.
+ *
+ * In addition to the normal btree operators, we consider a <> operator to be
+ * a "member" of an opfamily if its negator is an equality operator of the
+ * opfamily. ROWCOMPARE_NE is returned as the strategy number for this case.
+ */
+List *
+get_op_btree_interpretation(Oid opno)
+{
+ List *result = NIL;
+ OpBtreeInterpretation *thisresult;
+ CatCList *catlist;
+ int i;
+
+ /*
+ * Find all the pg_amop entries containing the operator.
+ */
+ catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
+
+ for (i = 0; i < catlist->n_members; i++)
+ {
+ HeapTuple op_tuple = &catlist->members[i]->tuple;
+ Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple);
+ StrategyNumber op_strategy;
+
+ /* must be btree */
+ if (op_form->amopmethod != BTREE_AM_OID)
+ continue;
+
+ /* Get the operator's btree strategy number */
+ op_strategy = (StrategyNumber) op_form->amopstrategy;
+ Assert(op_strategy >= 1 && op_strategy <= 5);
+
+ thisresult = (OpBtreeInterpretation *)
+ palloc(sizeof(OpBtreeInterpretation));
+ thisresult->opfamily_id = op_form->amopfamily;
+ thisresult->strategy = op_strategy;
+ thisresult->oplefttype = op_form->amoplefttype;
+ thisresult->oprighttype = op_form->amoprighttype;
+ result = lappend(result, thisresult);
+ }
+
+ ReleaseSysCacheList(catlist);
+
+ /*
+ * If we didn't find any btree opfamily containing the operator, perhaps
+ * it is a <> operator. See if it has a negator that is in an opfamily.
+ */
+ if (result == NIL)
+ {
+ Oid op_negator = get_negator(opno);
+
+ if (OidIsValid(op_negator))
+ {
+ catlist = SearchSysCacheList1(AMOPOPID,
+ ObjectIdGetDatum(op_negator));
+
+ for (i = 0; i < catlist->n_members; i++)
+ {
+ HeapTuple op_tuple = &catlist->members[i]->tuple;
+ Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple);
+ StrategyNumber op_strategy;
+
+ /* must be btree */
+ if (op_form->amopmethod != BTREE_AM_OID)
+ continue;
+
+ /* Get the operator's btree strategy number */
+ op_strategy = (StrategyNumber) op_form->amopstrategy;
+ Assert(op_strategy >= 1 && op_strategy <= 5);
+
+ /* Only consider negators that are = */
+ if (op_strategy != BTEqualStrategyNumber)
+ continue;
+
+ /* OK, report it with "strategy" ROWCOMPARE_NE */
+ thisresult = (OpBtreeInterpretation *)
+ palloc(sizeof(OpBtreeInterpretation));
+ thisresult->opfamily_id = op_form->amopfamily;
+ thisresult->strategy = ROWCOMPARE_NE;
+ thisresult->oplefttype = op_form->amoplefttype;
+ thisresult->oprighttype = op_form->amoprighttype;
+ result = lappend(result, thisresult);
+ }
+
+ ReleaseSysCacheList(catlist);
}
}
- /* Didn't find a match... */
- return InvalidOid;
+ return result;
+}
+
+/*
+ * equality_ops_are_compatible
+ * Return TRUE if the two given equality operators have compatible
+ * semantics.
+ *
+ * This is trivially true if they are the same operator. Otherwise,
+ * we look to see if they can be found in the same btree or hash opfamily.
+ * Either finding allows us to assume that they have compatible notions
+ * of equality. (The reason we need to do these pushups is that one might
+ * be a cross-type operator; for instance int24eq vs int4eq.)
+ */
+bool
+equality_ops_are_compatible(Oid opno1, Oid opno2)
+{
+ bool result;
+ CatCList *catlist;
+ int i;
+
+ /* Easy if they're the same operator */
+ if (opno1 == opno2)
+ return true;
+
+ /*
+ * We search through all the pg_amop entries for opno1.
+ */
+ catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno1));
+
+ result = false;
+ for (i = 0; i < catlist->n_members; i++)
+ {
+ HeapTuple op_tuple = &catlist->members[i]->tuple;
+ Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple);
+
+ /* must be btree or hash */
+ if (op_form->amopmethod == BTREE_AM_OID ||
+ op_form->amopmethod == HASH_AM_OID)
+ {
+ if (op_in_opfamily(opno2, op_form->amopfamily))
+ {
+ result = true;
+ break;
+ }
+ }
+ }
+
+ ReleaseSysCacheList(catlist);
+
+ return result;
+}
+
+
+/* ---------- AMPROC CACHES ---------- */
+
+/*
+ * get_opfamily_proc
+ * Get the OID of the specified support function
+ * for the specified opfamily and datatypes.
+ *
+ * Returns InvalidOid if there is no pg_amproc entry for the given keys.
+ */
+Oid
+get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, int16 procnum)
+{
+ HeapTuple tp;
+ Form_pg_amproc amproc_tup;
+ RegProcedure result;
+
+ tp = SearchSysCache4(AMPROCNUM,
+ ObjectIdGetDatum(opfamily),
+ ObjectIdGetDatum(lefttype),
+ ObjectIdGetDatum(righttype),
+ Int16GetDatum(procnum));
+ if (!HeapTupleIsValid(tp))
+ return InvalidOid;
+ amproc_tup = (Form_pg_amproc) GETSTRUCT(tp);
+ result = amproc_tup->amproc;
+ ReleaseSysCache(tp);
+ return result;
}
/*
* get_attname
- *
* 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.
+ * Note: returns a palloc'd copy of the string, or NULL if no such attribute.
*/
char *
get_attname(Oid relid, AttrNumber attnum)
{
HeapTuple tp;
- tp = SearchSysCache(ATTNUM,
- ObjectIdGetDatum(relid),
- Int16GetDatum(attnum),
- 0, 0);
+ tp = SearchSysCache2(ATTNUM,
+ ObjectIdGetDatum(relid),
+ Int16GetDatum(attnum));
if (HeapTupleIsValid(tp))
{
Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
return NULL;
}
+/*
+ * get_relid_attribute_name
+ *
+ * Same as above routine get_attname(), except that error
+ * is handled by elog() instead of returning NULL.
+ */
+char *
+get_relid_attribute_name(Oid relid, AttrNumber attnum)
+{
+ char *attname;
+
+ attname = get_attname(relid, attnum);
+ if (attname == NULL)
+ elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+ attnum, relid);
+ return attname;
+}
+
/*
* get_attnum
*
{
HeapTuple tp;
- tp = SearchSysCache(ATTNUM,
- ObjectIdGetDatum(relid),
- Int16GetDatum(attnum),
- 0, 0);
+ tp = SearchSysCache2(ATTNUM,
+ ObjectIdGetDatum(relid),
+ Int16GetDatum(attnum));
if (HeapTupleIsValid(tp))
{
Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
{
HeapTuple tp;
- tp = SearchSysCache(ATTNUM,
- ObjectIdGetDatum(relid),
- Int16GetDatum(attnum),
- 0, 0);
+ tp = SearchSysCache2(ATTNUM,
+ ObjectIdGetDatum(relid),
+ Int16GetDatum(attnum));
if (HeapTupleIsValid(tp))
{
Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
}
/*
- * get_atttypetypmod
+ * get_atttypetypmodcoll
*
- * A two-fer: given the relation id and the attribute number,
- * fetch both type OID and atttypmod in a single cache lookup.
+ * A three-fer: given the relation id and the attribute number,
+ * fetch atttypid, atttypmod, and attcollation 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)
+get_atttypetypmodcoll(Oid relid, AttrNumber attnum,
+ Oid *typid, int32 *typmod, Oid *collid)
{
HeapTuple tp;
Form_pg_attribute att_tup;
- tp = SearchSysCache(ATTNUM,
- ObjectIdGetDatum(relid),
- Int16GetDatum(attnum),
- 0, 0);
+ tp = SearchSysCache2(ATTNUM,
+ ObjectIdGetDatum(relid),
+ Int16GetDatum(attnum));
if (!HeapTupleIsValid(tp))
- elog(ERROR, "cache lookup failed for relation %u attribute %d",
- relid, attnum);
+ elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+ attnum, relid);
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
*typid = att_tup->atttypid;
*typmod = att_tup->atttypmod;
+ *collid = att_tup->attcollation;
ReleaseSysCache(tp);
}
-/* ---------- INDEX CACHE ---------- */
+/* ---------- COLLATION CACHE ---------- */
-/* watch this space...
+/*
+ * get_collation_name
+ * Returns the name of a given pg_collation entry.
+ *
+ * Returns a palloc'd copy of the string, or NULL if no such constraint.
+ *
+ * NOTE: since collation name is not unique, be wary of code that uses this
+ * for anything except preparing error messages.
*/
+char *
+get_collation_name(Oid colloid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(colloid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_collation colltup = (Form_pg_collation) GETSTRUCT(tp);
+ char *result;
+
+ result = pstrdup(NameStr(colltup->collname));
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return NULL;
+}
+
+/* ---------- CONSTRAINT CACHE ---------- */
+
+/*
+ * get_constraint_name
+ * Returns the name of a given pg_constraint entry.
+ *
+ * Returns a palloc'd copy of the string, or NULL if no such constraint.
+ *
+ * NOTE: since constraint name is not unique, be wary of code that uses this
+ * for anything except preparing error messages.
+ */
+char *
+get_constraint_name(Oid conoid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(conoid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
+ char *result;
+
+ result = pstrdup(NameStr(contup->conname));
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return NULL;
+}
/* ---------- OPCLASS CACHE ---------- */
/*
- * opclass_is_btree
+ * get_opclass_family
*
- * Returns TRUE iff the specified opclass is associated with the
- * btree index access method.
+ * Returns the OID of the operator family the opclass belongs to.
*/
-bool
-opclass_is_btree(Oid opclass)
+Oid
+get_opclass_family(Oid opclass)
{
HeapTuple tp;
Form_pg_opclass cla_tup;
- bool result;
+ Oid result;
- tp = SearchSysCache(CLAOID,
- ObjectIdGetDatum(opclass),
- 0, 0, 0);
+ tp = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
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);
+ result = cla_tup->opcfamily;
ReleaseSysCache(tp);
return result;
}
/*
- * opclass_is_hash
+ * get_opclass_input_type
*
- * Returns TRUE iff the specified opclass is associated with the
- * hash index access method.
+ * Returns the OID of the datatype the opclass indexes.
*/
-bool
-opclass_is_hash(Oid opclass)
+Oid
+get_opclass_input_type(Oid opclass)
{
HeapTuple tp;
Form_pg_opclass cla_tup;
- bool result;
+ Oid result;
- tp = SearchSysCache(CLAOID,
- ObjectIdGetDatum(opclass),
- 0, 0, 0);
+ tp = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for opclass %u", opclass);
cla_tup = (Form_pg_opclass) GETSTRUCT(tp);
- result = (cla_tup->opcamid == HASH_AM_OID);
+ result = cla_tup->opcintype;
ReleaseSysCache(tp);
return result;
}
{
HeapTuple tp;
- tp = SearchSysCache(OPEROID,
- ObjectIdGetDatum(opno),
- 0, 0, 0);
+ tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
if (HeapTupleIsValid(tp))
{
Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
{
HeapTuple tp;
- tp = SearchSysCache(OPEROID,
- ObjectIdGetDatum(opno),
- 0, 0, 0);
+ tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
if (HeapTupleIsValid(tp))
{
Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
}
/*
- * op_mergejoinable
+ * op_input_types
*
- * Returns the left and right sort operators corresponding to a
- * mergejoinable operator, or false if the operator is not mergejoinable.
+ * Returns the left and right input datatypes for an operator
+ * (InvalidOid if not relevant).
*/
-bool
-op_mergejoinable(Oid opno, Oid *leftOp, Oid *rightOp)
+void
+op_input_types(Oid opno, Oid *lefttype, Oid *righttype)
{
HeapTuple tp;
- bool result = false;
-
- tp = SearchSysCache(OPEROID,
- ObjectIdGetDatum(opno),
- 0, 0, 0);
- if (HeapTupleIsValid(tp))
- {
- Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
+ Form_pg_operator optup;
- if (optup->oprlsortop &&
- optup->oprrsortop)
- {
- *leftOp = optup->oprlsortop;
- *rightOp = optup->oprrsortop;
- result = true;
- }
- ReleaseSysCache(tp);
- }
- return result;
+ tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
+ if (!HeapTupleIsValid(tp)) /* shouldn't happen */
+ elog(ERROR, "cache lookup failed for operator %u", opno);
+ optup = (Form_pg_operator) GETSTRUCT(tp);
+ *lefttype = optup->oprleft;
+ *righttype = optup->oprright;
+ ReleaseSysCache(tp);
}
/*
- * op_mergejoin_crossops
+ * op_mergejoinable
+ *
+ * Returns true if the operator is potentially mergejoinable. (The planner
+ * will fail to find any mergejoin plans unless there are suitable btree
+ * opfamily entries for this operator and associated sortops. The pg_operator
+ * flag is just a hint to tell the planner whether to bother looking.)
*
- * 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.
+ * In some cases (currently only array_eq and record_eq), mergejoinability
+ * depends on the specific input data type the operator is invoked for, so
+ * that must be passed as well. We currently assume that only one input's type
+ * is needed to check this --- by convention, pass the left input's data type.
*/
-void
-op_mergejoin_crossops(Oid opno, Oid *ltop, Oid *gtop,
- RegProcedure *ltproc, RegProcedure *gtproc)
+bool
+op_mergejoinable(Oid opno, Oid inputtype)
{
+ bool result = false;
HeapTuple tp;
- Form_pg_operator optup;
+ TypeCacheEntry *typentry;
/*
- * Get the declared comparison operators of the operator.
+ * For array_eq or record_eq, we can sort if the element or field types
+ * are all sortable. We could implement all the checks for that here, but
+ * the typcache already does that and caches the results too, so let's
+ * rely on the typcache.
*/
- 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;
- ReleaseSysCache(tp);
-
- /* 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);
+ if (opno == ARRAY_EQ_OP)
+ {
+ typentry = lookup_type_cache(inputtype, TYPECACHE_CMP_PROC);
+ if (typentry->cmp_proc == F_BTARRAYCMP)
+ result = true;
+ }
+ else if (opno == RECORD_EQ_OP)
+ {
+ typentry = lookup_type_cache(inputtype, TYPECACHE_CMP_PROC);
+ if (typentry->cmp_proc == F_BTRECORDCMP)
+ result = true;
+ }
+ else
+ {
+ /* For all other operators, rely on pg_operator.oprcanmerge */
+ tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
- /* 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);
+ result = optup->oprcanmerge;
+ ReleaseSysCache(tp);
+ }
+ }
+ return result;
}
/*
* op_hashjoinable
*
- * Returns true if the operator is hashjoinable.
+ * Returns true if the operator is hashjoinable. (There must be a suitable
+ * hash opfamily entry for this operator if it is so marked.)
+ *
+ * In some cases (currently only array_eq), hashjoinability depends on the
+ * specific input data type the operator is invoked for, so that must be
+ * passed as well. We currently assume that only one input's type is needed
+ * to check this --- by convention, pass the left input's data type.
*/
bool
-op_hashjoinable(Oid opno)
+op_hashjoinable(Oid opno, Oid inputtype)
{
- HeapTuple tp;
bool result = false;
+ HeapTuple tp;
+ TypeCacheEntry *typentry;
- tp = SearchSysCache(OPEROID,
- ObjectIdGetDatum(opno),
- 0, 0, 0);
- if (HeapTupleIsValid(tp))
+ /* As in op_mergejoinable, let the typcache handle the hard cases */
+ /* Eventually we'll need a similar case for record_eq ... */
+ if (opno == ARRAY_EQ_OP)
{
- Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
+ typentry = lookup_type_cache(inputtype, TYPECACHE_HASH_PROC);
+ if (typentry->hash_proc == F_HASH_ARRAY)
+ result = true;
+ }
+ else
+ {
+ /* For all other operators, rely on pg_operator.oprcanhash */
+ tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
- result = optup->oprcanhash;
- ReleaseSysCache(tp);
+ result = optup->oprcanhash;
+ ReleaseSysCache(tp);
+ }
}
return result;
}
RegProcedure funcid = get_opcode(opno);
if (funcid == (RegProcedure) InvalidOid)
- elog(ERROR, "Operator OID %u does not exist", opno);
+ elog(ERROR, "operator %u does not exist", opno);
return func_strict((Oid) funcid);
}
RegProcedure funcid = get_opcode(opno);
if (funcid == (RegProcedure) InvalidOid)
- elog(ERROR, "Operator OID %u does not exist", opno);
+ elog(ERROR, "operator %u does not exist", opno);
return func_volatile((Oid) funcid);
}
{
HeapTuple tp;
- tp = SearchSysCache(OPEROID,
- ObjectIdGetDatum(opno),
- 0, 0, 0);
+ tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
if (HeapTupleIsValid(tp))
{
Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
{
HeapTuple tp;
- tp = SearchSysCache(OPEROID,
- ObjectIdGetDatum(opno),
- 0, 0, 0);
+ tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
if (HeapTupleIsValid(tp))
{
Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
{
HeapTuple tp;
- tp = SearchSysCache(OPEROID,
- ObjectIdGetDatum(opno),
- 0, 0, 0);
+ tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
if (HeapTupleIsValid(tp))
{
Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
{
HeapTuple tp;
- tp = SearchSysCache(OPEROID,
- ObjectIdGetDatum(opno),
- 0, 0, 0);
+ tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
if (HeapTupleIsValid(tp))
{
Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
{
HeapTuple tp;
- tp = SearchSysCache(PROCOID,
- ObjectIdGetDatum(funcid),
- 0, 0, 0);
+ tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (HeapTupleIsValid(tp))
{
Form_pg_proc functup = (Form_pg_proc) GETSTRUCT(tp);
return NULL;
}
+/*
+ * get_func_namespace
+ *
+ * Returns the pg_namespace OID associated with a given function.
+ */
+Oid
+get_func_namespace(Oid funcid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_proc functup = (Form_pg_proc) GETSTRUCT(tp);
+ Oid result;
+
+ result = functup->pronamespace;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
/*
* get_func_rettype
* Given procedure id, return the function's result type.
HeapTuple tp;
Oid result;
- tp = SearchSysCache(PROCOID,
- ObjectIdGetDatum(funcid),
- 0, 0, 0);
+ tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tp))
- elog(ERROR, "Function OID %u does not exist", funcid);
+ elog(ERROR, "cache lookup failed for function %u", funcid);
result = ((Form_pg_proc) GETSTRUCT(tp))->prorettype;
ReleaseSysCache(tp);
return result;
}
+/*
+ * get_func_nargs
+ * Given procedure id, return the number of arguments.
+ */
+int
+get_func_nargs(Oid funcid)
+{
+ HeapTuple tp;
+ int result;
+
+ tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for function %u", funcid);
+
+ result = ((Form_pg_proc) GETSTRUCT(tp))->pronargs;
+ ReleaseSysCache(tp);
+ return result;
+}
+
+/*
+ * get_func_signature
+ * Given procedure id, return the function's argument and result types.
+ * (The return value is the result type.)
+ *
+ * The arguments are returned as a palloc'd array.
+ */
+Oid
+get_func_signature(Oid funcid, Oid **argtypes, int *nargs)
+{
+ HeapTuple tp;
+ Form_pg_proc procstruct;
+ Oid result;
+
+ tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for function %u", funcid);
+
+ procstruct = (Form_pg_proc) GETSTRUCT(tp);
+
+ result = procstruct->prorettype;
+ *nargs = (int) procstruct->pronargs;
+ Assert(*nargs == procstruct->proargtypes.dim1);
+ *argtypes = (Oid *) palloc(*nargs * sizeof(Oid));
+ memcpy(*argtypes, procstruct->proargtypes.values, *nargs * sizeof(Oid));
+
+ ReleaseSysCache(tp);
+ return result;
+}
+
/*
* get_func_retset
* Given procedure id, return the function's proretset flag.
HeapTuple tp;
bool result;
- tp = SearchSysCache(PROCOID,
- ObjectIdGetDatum(funcid),
- 0, 0, 0);
+ tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tp))
- elog(ERROR, "Function OID %u does not exist", funcid);
+ elog(ERROR, "cache lookup failed for function %u", funcid);
result = ((Form_pg_proc) GETSTRUCT(tp))->proretset;
ReleaseSysCache(tp);
func_strict(Oid funcid)
{
HeapTuple tp;
- bool result;
+ bool result;
+
+ tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for function %u", funcid);
+
+ result = ((Form_pg_proc) GETSTRUCT(tp))->proisstrict;
+ ReleaseSysCache(tp);
+ return result;
+}
+
+/*
+ * func_volatile
+ * Given procedure id, return the function's provolatile flag.
+ */
+char
+func_volatile(Oid funcid)
+{
+ HeapTuple tp;
+ char result;
+
+ tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for function %u", funcid);
+
+ result = ((Form_pg_proc) GETSTRUCT(tp))->provolatile;
+ ReleaseSysCache(tp);
+ return result;
+}
+
+/*
+ * get_func_cost
+ * Given procedure id, return the function's procost field.
+ */
+float4
+get_func_cost(Oid funcid)
+{
+ HeapTuple tp;
+ float4 result;
- tp = SearchSysCache(PROCOID,
- ObjectIdGetDatum(funcid),
- 0, 0, 0);
+ tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tp))
- elog(ERROR, "Function OID %u does not exist", funcid);
+ elog(ERROR, "cache lookup failed for function %u", funcid);
- result = ((Form_pg_proc) GETSTRUCT(tp))->proisstrict;
+ result = ((Form_pg_proc) GETSTRUCT(tp))->procost;
ReleaseSysCache(tp);
return result;
}
/*
- * func_volatile
- * Given procedure id, return the function's provolatile flag.
+ * get_func_rows
+ * Given procedure id, return the function's prorows field.
*/
-char
-func_volatile(Oid funcid)
+float4
+get_func_rows(Oid funcid)
{
HeapTuple tp;
- char result;
+ float4 result;
- tp = SearchSysCache(PROCOID,
- ObjectIdGetDatum(funcid),
- 0, 0, 0);
+ tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tp))
- elog(ERROR, "Function OID %u does not exist", funcid);
+ elog(ERROR, "cache lookup failed for function %u", funcid);
- result = ((Form_pg_proc) GETSTRUCT(tp))->provolatile;
+ result = ((Form_pg_proc) GETSTRUCT(tp))->prorows;
ReleaseSysCache(tp);
return result;
}
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;
+ return GetSysCacheOid2(RELNAMENSP,
+ PointerGetDatum(relname),
+ ObjectIdGetDatum(relnamespace));
}
#ifdef NOT_USED
{
HeapTuple tp;
- tp = SearchSysCache(RELOID,
- ObjectIdGetDatum(relid),
- 0, 0, 0);
+ tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (HeapTupleIsValid(tp))
{
Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
{
HeapTuple tp;
- tp = SearchSysCache(RELOID,
- ObjectIdGetDatum(relid),
- 0, 0, 0);
+ tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (HeapTupleIsValid(tp))
{
Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
{
HeapTuple tp;
- tp = SearchSysCache(RELOID,
- ObjectIdGetDatum(relid),
- 0, 0, 0);
+ tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (HeapTupleIsValid(tp))
{
Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
{
HeapTuple tp;
- tp = SearchSysCache(RELOID,
- ObjectIdGetDatum(relid),
- 0, 0, 0);
+ tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (HeapTupleIsValid(tp))
{
Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
{
HeapTuple tp;
- tp = SearchSysCache(RELOID,
- ObjectIdGetDatum(relid),
- 0, 0, 0);
+ tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (HeapTupleIsValid(tp))
{
Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
return '\0';
}
+/*
+ * get_rel_tablespace
+ *
+ * Returns the pg_tablespace OID associated with a given relation.
+ *
+ * Note: InvalidOid might mean either that we couldn't find the relation,
+ * or that it is in the database's default tablespace.
+ */
+Oid
+get_rel_tablespace(Oid relid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
+ Oid result;
+
+ result = reltup->reltablespace;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
/* ---------- TYPE CACHE ---------- */
{
HeapTuple tp;
- tp = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(typid),
- 0, 0, 0);
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp))
{
Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
{
HeapTuple tp;
- tp = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(typid),
- 0, 0, 0);
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp))
{
Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
{
HeapTuple tp;
- tp = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(typid),
- 0, 0, 0);
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp))
{
Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
HeapTuple tp;
Form_pg_type typtup;
- tp = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(typid),
- 0, 0, 0);
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for type %u", typid);
typtup = (Form_pg_type) GETSTRUCT(tp);
HeapTuple tp;
Form_pg_type typtup;
- tp = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(typid),
- 0, 0, 0);
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for type %u", typid);
typtup = (Form_pg_type) GETSTRUCT(tp);
ReleaseSysCache(tp);
}
+/*
+ * getTypeIOParam
+ * Given a pg_type row, select the type OID to pass to I/O functions
+ *
+ * Formerly, all I/O functions were passed pg_type.typelem as their second
+ * parameter, but we now have a more complex rule about what to pass.
+ * This knowledge is intended to be centralized here --- direct references
+ * to typelem elsewhere in the code are wrong, if they are associated with
+ * I/O calls and not with actual subscripting operations! (But see
+ * bootstrap.c's boot_get_type_io_data() if you need to change this.)
+ *
+ * As of PostgreSQL 8.1, output functions receive only the value itself
+ * and not any auxiliary parameters, so the name of this routine is now
+ * a bit of a misnomer ... it should be getTypeInputParam.
+ */
+Oid
+getTypeIOParam(HeapTuple typeTuple)
+{
+ Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
+
+ /*
+ * Array types get their typelem as parameter; everybody else gets their
+ * own type OID as parameter.
+ */
+ if (OidIsValid(typeStruct->typelem))
+ return typeStruct->typelem;
+ else
+ return HeapTupleGetOid(typeTuple);
+}
+
+/*
+ * get_type_io_data
+ *
+ * A six-fer: given the type OID, return typlen, typbyval, typalign,
+ * typdelim, typioparam, and IO function OID. The IO function
+ * returned is controlled by IOFuncSelector
+ */
+void
+get_type_io_data(Oid typid,
+ IOFuncSelector which_func,
+ int16 *typlen,
+ bool *typbyval,
+ char *typalign,
+ char *typdelim,
+ Oid *typioparam,
+ Oid *func)
+{
+ HeapTuple typeTuple;
+ Form_pg_type typeStruct;
+
+ /*
+ * In bootstrap mode, pass it off to bootstrap.c. This hack allows us to
+ * use array_in and array_out during bootstrap.
+ */
+ if (IsBootstrapProcessingMode())
+ {
+ Oid typinput;
+ Oid typoutput;
+
+ boot_get_type_io_data(typid,
+ typlen,
+ typbyval,
+ typalign,
+ typdelim,
+ typioparam,
+ &typinput,
+ &typoutput);
+ switch (which_func)
+ {
+ case IOFunc_input:
+ *func = typinput;
+ break;
+ case IOFunc_output:
+ *func = typoutput;
+ break;
+ default:
+ elog(ERROR, "binary I/O not supported during bootstrap");
+ break;
+ }
+ return;
+ }
+
+ typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (!HeapTupleIsValid(typeTuple))
+ elog(ERROR, "cache lookup failed for type %u", typid);
+ typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
+
+ *typlen = typeStruct->typlen;
+ *typbyval = typeStruct->typbyval;
+ *typalign = typeStruct->typalign;
+ *typdelim = typeStruct->typdelim;
+ *typioparam = getTypeIOParam(typeTuple);
+ switch (which_func)
+ {
+ case IOFunc_input:
+ *func = typeStruct->typinput;
+ break;
+ case IOFunc_output:
+ *func = typeStruct->typoutput;
+ break;
+ case IOFunc_receive:
+ *func = typeStruct->typreceive;
+ break;
+ case IOFunc_send:
+ *func = typeStruct->typsend;
+ break;
+ }
+ ReleaseSysCache(typeTuple);
+}
+
#ifdef NOT_USED
char
get_typalign(Oid typid)
{
HeapTuple tp;
- tp = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(typid),
- 0, 0, 0);
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp))
{
Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
{
HeapTuple tp;
- tp = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(typid),
- 0, 0, 0);
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp))
{
Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
return 'p';
}
-/*
- * get_typtypmod
- *
- * Given the type OID, return the typtypmod field (domain's typmod
- * for base type)
- */
-int32
-get_typtypmod(Oid typid)
-{
- HeapTuple tp;
-
- tp = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(typid),
- 0, 0, 0);
- if (HeapTupleIsValid(tp))
- {
- Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
- int32 result;
-
- result = typtup->typtypmod;
- ReleaseSysCache(tp);
- return result;
- }
- else
- return -1;
-}
-
/*
* get_typdefault
* Given a type OID, return the type's default value, if any.
bool isNull;
Node *expr;
- typeTuple = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(typid),
- 0, 0, 0);
+ typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (!HeapTupleIsValid(typeTuple))
- elog(ERROR, "get_typdefault: failed to lookup type %u", typid);
+ elog(ERROR, "cache lookup failed for type %u", typid);
type = (Form_pg_type) GETSTRUCT(typeTuple);
/*
if (!isNull)
{
/* We have an expression default */
- expr = stringToNode(DatumGetCString(DirectFunctionCall1(textout,
- datum)));
+ expr = stringToNode(TextDatumGetCString(datum));
}
else
{
char *strDefaultVal;
/* Convert text datum to C string */
- strDefaultVal = DatumGetCString(DirectFunctionCall1(textout,
- datum));
+ strDefaultVal = TextDatumGetCString(datum);
/* Convert C string to a value of the given type */
- datum = OidFunctionCall3(type->typinput,
- CStringGetDatum(strDefaultVal),
- ObjectIdGetDatum(type->typelem),
- Int32GetDatum(-1));
+ datum = OidInputFunctionCall(type->typinput, strDefaultVal,
+ getTypeIOParam(typeTuple), -1);
/* Build a Const node containing the value */
expr = (Node *) makeConst(typid,
+ -1,
+ type->typcollation,
type->typlen,
datum,
false,
*/
Oid
getBaseType(Oid typid)
+{
+ int32 typmod = -1;
+
+ return getBaseTypeAndTypmod(typid, &typmod);
+}
+
+/*
+ * getBaseTypeAndTypmod
+ * If the given type is a domain, return its base type and typmod;
+ * otherwise return the type's own OID, and leave *typmod unchanged.
+ *
+ * Note that the "applied typmod" should be -1 for every domain level
+ * above the bottommost; therefore, if the passed-in typid is indeed
+ * a domain, *typmod should be -1.
+ */
+Oid
+getBaseTypeAndTypmod(Oid typid, int32 *typmod)
{
/*
* We loop to find the bottom base type in a stack of domains.
HeapTuple tup;
Form_pg_type typTup;
- tup = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(typid),
- 0, 0, 0);
+ tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (!HeapTupleIsValid(tup))
- elog(ERROR, "getBaseType: failed to lookup type %u", typid);
+ elog(ERROR, "cache lookup failed for type %u", typid);
typTup = (Form_pg_type) GETSTRUCT(tup);
- if (typTup->typtype != 'd')
+ if (typTup->typtype != TYPTYPE_DOMAIN)
{
/* Not a domain, so done */
ReleaseSysCache(tup);
break;
}
+ Assert(*typmod == -1);
typid = typTup->typbasetype;
+ *typmod = typTup->typtypmod;
+
ReleaseSysCache(tup);
}
{
/*
* 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.
+ * 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;
/*
* Beyond 1000, assume we're looking at something like
- * "varchar(10000)" where the limit isn't actually reached often,
- * and use a fixed estimate.
+ * "varchar(10000)" where the limit isn't actually reached often, and
+ * use a fixed estimate.
*/
return 32 + (1000 - 32) / 2;
}
{
HeapTuple tp;
- tp = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(typid),
- 0, 0, 0);
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp))
{
Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
return '\0';
}
+/*
+ * type_is_rowtype
+ *
+ * Convenience function to determine whether a type OID represents
+ * a "rowtype" type --- either RECORD or a named composite type.
+ */
+bool
+type_is_rowtype(Oid typid)
+{
+ return (typid == RECORDOID || get_typtype(typid) == TYPTYPE_COMPOSITE);
+}
+
+/*
+ * type_is_enum
+ * Returns true if the given type is an enum type.
+ */
+bool
+type_is_enum(Oid typid)
+{
+ return (get_typtype(typid) == TYPTYPE_ENUM);
+}
+
+/*
+ * type_is_range
+ * Returns true if the given type is a range type.
+ */
+bool
+type_is_range(Oid typid)
+{
+ return (get_typtype(typid) == TYPTYPE_RANGE);
+}
+
+/*
+ * get_type_category_preferred
+ *
+ * Given the type OID, fetch its category and preferred-type status.
+ * Throws error on failure.
+ */
+void
+get_type_category_preferred(Oid typid, char *typcategory, bool *typispreferred)
+{
+ HeapTuple tp;
+ Form_pg_type typtup;
+
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for type %u", typid);
+ typtup = (Form_pg_type) GETSTRUCT(tp);
+ *typcategory = typtup->typcategory;
+ *typispreferred = typtup->typispreferred;
+ ReleaseSysCache(tp);
+}
+
/*
* get_typ_typrelid
*
{
HeapTuple tp;
- tp = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(typid),
- 0, 0, 0);
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp))
{
Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
{
HeapTuple tp;
- tp = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(typid),
- 0, 0, 0);
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp))
{
Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
/*
* get_array_type
*
- * Given the type OID, get the corresponding array type.
+ * Given the type OID, get the corresponding "true" array type.
* Returns InvalidOid if no array type can be found.
- *
- * NB: this only considers varlena arrays to be true arrays.
*/
Oid
get_array_type(Oid typid)
{
HeapTuple tp;
+ Oid result = InvalidOid;
- tp = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(typid),
- 0, 0, 0);
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp))
{
- Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
- char *array_typename;
- Oid namespaceId;
-
- array_typename = makeArrayTypeName(NameStr(typtup->typname));
- namespaceId = typtup->typnamespace;
+ result = ((Form_pg_type) GETSTRUCT(tp))->typarray;
ReleaseSysCache(tp);
+ }
+ return result;
+}
- tp = SearchSysCache(TYPENAMENSP,
- PointerGetDatum(array_typename),
- ObjectIdGetDatum(namespaceId),
- 0, 0);
-
- pfree(array_typename);
+/*
+ * get_base_element_type
+ * Given the type OID, get the typelem, looking "through" any domain
+ * to its underlying array type.
+ *
+ * This is equivalent to get_element_type(getBaseType(typid)), but avoids
+ * an extra cache lookup. Note that it fails to provide any information
+ * about the typmod of the array.
+ */
+Oid
+get_base_element_type(Oid typid)
+{
+ /*
+ * We loop to find the bottom base type in a stack of domains.
+ */
+ for (;;)
+ {
+ HeapTuple tup;
+ Form_pg_type typTup;
- if (HeapTupleIsValid(tp))
+ tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (!HeapTupleIsValid(tup))
+ break;
+ typTup = (Form_pg_type) GETSTRUCT(tup);
+ if (typTup->typtype != TYPTYPE_DOMAIN)
{
+ /* Not a domain, so stop descending */
Oid result;
- typtup = (Form_pg_type) GETSTRUCT(tp);
- if (typtup->typlen == -1 && typtup->typelem == typid)
- result = HeapTupleGetOid(tp);
+ /* This test must match get_element_type */
+ if (typTup->typlen == -1)
+ result = typTup->typelem;
else
result = InvalidOid;
- ReleaseSysCache(tp);
+ ReleaseSysCache(tup);
return result;
}
+
+ typid = typTup->typbasetype;
+ ReleaseSysCache(tup);
}
+
+ /* Like get_element_type, silently return InvalidOid for bogus input */
return InvalidOid;
}
* Get info needed for converting values of a type to internal form
*/
void
-getTypeInputInfo(Oid type, Oid *typInput, Oid *typElem)
+getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
{
HeapTuple typeTuple;
Form_pg_type pt;
- typeTuple = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(type),
- 0, 0, 0);
+ typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type));
if (!HeapTupleIsValid(typeTuple))
- elog(ERROR, "Cache lookup of type %u failed", type);
+ elog(ERROR, "cache lookup failed for type %u", type);
pt = (Form_pg_type) GETSTRUCT(typeTuple);
if (!pt->typisdefined)
- elog(ERROR, "Type %s is only a shell",
- format_type_be(type));
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("type %s is only a shell",
+ format_type_be(type))));
if (!OidIsValid(pt->typinput))
- elog(ERROR, "No input function available for type %s",
- format_type_be(type));
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no input function available for type %s",
+ format_type_be(type))));
*typInput = pt->typinput;
- *typElem = pt->typelem;
+ *typIOParam = getTypeIOParam(typeTuple);
ReleaseSysCache(typeTuple);
}
* Get info needed for printing values of a type
*/
void
-getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem,
- bool *typIsVarlena)
+getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
{
HeapTuple typeTuple;
Form_pg_type pt;
- typeTuple = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(type),
- 0, 0, 0);
+ typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type));
if (!HeapTupleIsValid(typeTuple))
- elog(ERROR, "Cache lookup of type %u failed", type);
+ elog(ERROR, "cache lookup failed for type %u", type);
pt = (Form_pg_type) GETSTRUCT(typeTuple);
if (!pt->typisdefined)
- elog(ERROR, "Type %s is only a shell",
- format_type_be(type));
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("type %s is only a shell",
+ format_type_be(type))));
if (!OidIsValid(pt->typoutput))
- elog(ERROR, "No output function available for type %s",
- format_type_be(type));
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no output function available for type %s",
+ format_type_be(type))));
*typOutput = pt->typoutput;
- *typElem = pt->typelem;
*typIsVarlena = (!pt->typbyval) && (pt->typlen == -1);
ReleaseSysCache(typeTuple);
* Get info needed for binary input of values of a type
*/
void
-getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typElem)
+getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typIOParam)
{
HeapTuple typeTuple;
Form_pg_type pt;
- typeTuple = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(type),
- 0, 0, 0);
+ typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type));
if (!HeapTupleIsValid(typeTuple))
- elog(ERROR, "Cache lookup of type %u failed", type);
+ elog(ERROR, "cache lookup failed for type %u", type);
pt = (Form_pg_type) GETSTRUCT(typeTuple);
if (!pt->typisdefined)
- elog(ERROR, "Type %s is only a shell",
- format_type_be(type));
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("type %s is only a shell",
+ format_type_be(type))));
if (!OidIsValid(pt->typreceive))
- elog(ERROR, "No binary input function available for type %s",
- format_type_be(type));
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary input function available for type %s",
+ format_type_be(type))));
*typReceive = pt->typreceive;
- *typElem = pt->typelem;
+ *typIOParam = getTypeIOParam(typeTuple);
ReleaseSysCache(typeTuple);
}
* Get info needed for binary output of values of a type
*/
void
-getTypeBinaryOutputInfo(Oid type, Oid *typSend, Oid *typElem,
- bool *typIsVarlena)
+getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena)
{
HeapTuple typeTuple;
Form_pg_type pt;
- typeTuple = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(type),
- 0, 0, 0);
+ typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type));
if (!HeapTupleIsValid(typeTuple))
- elog(ERROR, "Cache lookup of type %u failed", type);
+ elog(ERROR, "cache lookup failed for type %u", type);
pt = (Form_pg_type) GETSTRUCT(typeTuple);
if (!pt->typisdefined)
- elog(ERROR, "Type %s is only a shell",
- format_type_be(type));
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("type %s is only a shell",
+ format_type_be(type))));
if (!OidIsValid(pt->typsend))
- elog(ERROR, "No binary output function available for type %s",
- format_type_be(type));
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary output function available for type %s",
+ format_type_be(type))));
*typSend = pt->typsend;
- *typElem = pt->typelem;
*typIsVarlena = (!pt->typbyval) && (pt->typlen == -1);
ReleaseSysCache(typeTuple);
}
+/*
+ * get_typmodin
+ *
+ * Given the type OID, return the type's typmodin procedure, if any.
+ */
+Oid
+get_typmodin(Oid typid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
+ Oid result;
+
+ result = typtup->typmodin;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+#ifdef NOT_USED
+/*
+ * get_typmodout
+ *
+ * Given the type OID, return the type's typmodout procedure, if any.
+ */
+Oid
+get_typmodout(Oid typid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
+ Oid result;
+
+ result = typtup->typmodout;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+#endif /* NOT_USED */
+
+/*
+ * get_typcollation
+ *
+ * Given the type OID, return the type's typcollation attribute.
+ */
+Oid
+get_typcollation(Oid typid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
+ Oid result;
+
+ result = typtup->typcollation;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+
+/*
+ * type_is_collatable
+ *
+ * Return whether the type cares about collations
+ */
+bool
+type_is_collatable(Oid typid)
+{
+ return OidIsValid(get_typcollation(typid));
+}
+
/* ---------- STATISTICS CACHE ---------- */
*
* Given the table and attribute number of a column, get the average
* width of entries in the column. Return zero if no data available.
+ *
+ * Currently this is only consulted for individual tables, not for inheritance
+ * trees, so we don't need an "inh" parameter.
+ *
+ * Calling a hook at this point looks somewhat strange, but is required
+ * because the optimizer calls this function without any other way for
+ * plug-ins to control the result.
*/
int32
get_attavgwidth(Oid relid, AttrNumber attnum)
{
HeapTuple tp;
+ int32 stawidth;
- tp = SearchSysCache(STATRELATT,
- ObjectIdGetDatum(relid),
- Int16GetDatum(attnum),
- 0, 0);
+ if (get_attavgwidth_hook)
+ {
+ stawidth = (*get_attavgwidth_hook) (relid, attnum);
+ if (stawidth > 0)
+ return stawidth;
+ }
+ tp = SearchSysCache3(STATRELATTINH,
+ ObjectIdGetDatum(relid),
+ Int16GetDatum(attnum),
+ BoolGetDatum(false));
if (HeapTupleIsValid(tp))
{
- int32 stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth;
-
+ stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth;
ReleaseSysCache(tp);
if (stawidth > 0)
return stawidth;
* already-looked-up tuple in the pg_statistic cache. We do this since
* most callers will want to extract more than one value from the cache
* entry, and we don't want to repeat the cache lookup unnecessarily.
+ * Also, this API allows this routine to be used with statistics tuples
+ * that have been provided by a stats hook and didn't really come from
+ * pg_statistic.
*
* statstuple: pg_statistics tuple to be examined.
- * atttype: type OID of attribute.
- * atttypmod: typmod of attribute.
+ * atttype: type OID of attribute (can be InvalidOid if values == NULL).
+ * atttypmod: typmod of attribute (can be 0 if values == NULL).
* reqkind: STAKIND code for desired statistics slot kind.
* reqop: STAOP value wanted, or InvalidOid if don't care.
+ * actualop: if not NULL, *actualop receives the actual STAOP value.
* values, nvalues: if not NULL, the slot's stavalues are extracted.
* numbers, nnumbers: if not NULL, the slot's stanumbers are extracted.
*
* If the attribute type is pass-by-reference, the values referenced by
* the values array are themselves palloc'd. The palloc'd stuff can be
* freed by calling free_attstatsslot.
+ *
+ * Note: at present, atttype/atttypmod aren't actually used here at all.
+ * But the caller must have the correct (or at least binary-compatible)
+ * type ID to pass to free_attstatsslot later.
*/
bool
get_attstatsslot(HeapTuple statstuple,
Oid atttype, int32 atttypmod,
int reqkind, Oid reqop,
+ Oid *actualop,
Datum **values, int *nvalues,
float4 **numbers, int *nnumbers)
{
Datum val;
bool isnull;
ArrayType *statarray;
+ Oid arrayelemtype;
int narrayelem;
HeapTuple typeTuple;
Form_pg_type typeForm;
if (i >= STATISTIC_NUM_SLOTS)
return false; /* not there */
+ if (actualop)
+ *actualop = (&stats->staop1)[i];
+
if (values)
{
- val = SysCacheGetAttr(STATRELATT, statstuple,
+ val = SysCacheGetAttr(STATRELATTINH, statstuple,
Anum_pg_statistic_stavalues1 + i,
&isnull);
if (isnull)
- elog(ERROR, "get_attstatsslot: stavalues is null");
+ elog(ERROR, "stavalues is null");
statarray = DatumGetArrayTypeP(val);
- /* Need to get info about the array element type */
- typeTuple = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(atttype),
- 0, 0, 0);
+ /*
+ * Need to get info about the array element type. We look at the
+ * actual element type embedded in the array, which might be only
+ * binary-compatible with the passed-in atttype. The info we extract
+ * here should be the same either way, but deconstruct_array is picky
+ * about having an exact type OID match.
+ */
+ arrayelemtype = ARR_ELEMTYPE(statarray);
+ typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(arrayelemtype));
if (!HeapTupleIsValid(typeTuple))
- elog(ERROR, "get_attstatsslot: Cache lookup failed for type %u",
- atttype);
+ elog(ERROR, "cache lookup failed for type %u", arrayelemtype);
typeForm = (Form_pg_type) GETSTRUCT(typeTuple);
- /* Deconstruct array into Datum elements */
+ /* Deconstruct array into Datum elements; NULLs not expected */
deconstruct_array(statarray,
- atttype,
+ arrayelemtype,
typeForm->typlen,
typeForm->typbyval,
typeForm->typalign,
- values, nvalues);
+ values, NULL, nvalues);
/*
- * If the element type is pass-by-reference, we now have a bunch
- * of Datums that are pointers into the syscache value. Copy them
- * to avoid problems if syscache decides to drop the entry.
+ * If the element type is pass-by-reference, we now have a bunch of
+ * Datums that are pointers into the syscache value. Copy them to
+ * avoid problems if syscache decides to drop the entry.
*/
if (!typeForm->typbyval)
{
if (numbers)
{
- val = SysCacheGetAttr(STATRELATT, statstuple,
+ val = SysCacheGetAttr(STATRELATTINH, statstuple,
Anum_pg_statistic_stanumbers1 + i,
&isnull);
if (isnull)
- elog(ERROR, "get_attstatsslot: stanumbers is null");
+ elog(ERROR, "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 ||
+ ARR_HASNULL(statarray) ||
ARR_ELEMTYPE(statarray) != FLOAT4OID)
- elog(ERROR, "get_attstatsslot: stanumbers is not a 1-D float4 array");
+ elog(ERROR, "stanumbers is not a 1-D float4 array");
*numbers = (float4 *) palloc(narrayelem * sizeof(float4));
memcpy(*numbers, ARR_DATA_PTR(statarray), narrayelem * sizeof(float4));
*nnumbers = narrayelem;
return true;
}
+/*
+ * free_attstatsslot
+ * Free data allocated by get_attstatsslot
+ *
+ * atttype need be valid only if values != NULL.
+ */
void
free_attstatsslot(Oid atttype,
Datum *values, int nvalues,
{
HeapTuple tp;
- tp = SearchSysCache(NAMESPACEOID,
- ObjectIdGetDatum(nspid),
- 0, 0, 0);
+ tp = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(nspid));
if (HeapTupleIsValid(tp))
{
Form_pg_namespace nsptup = (Form_pg_namespace) GETSTRUCT(tp);
return NULL;
}
-/* ---------- PG_SHADOW CACHE ---------- */
+/* ---------- PG_RANGE 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).
+ * get_range_subtype
+ * Returns the subtype of a given range type
*
- * 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 ...
+ * Returns InvalidOid if the type is not a range type.
*/
-AclId
-get_usesysid(const char *username)
+Oid
+get_range_subtype(Oid rangeOid)
{
- 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;
+ HeapTuple tp;
- ReleaseSysCache(userTup);
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
- return result;
+ result = rngtup->rngsubtype;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
}