summaryrefslogtreecommitdiffstats
path: root/src/backend/utils/cache/lsyscache.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:17:33 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:17:33 +0000
commit5e45211a64149b3c659b90ff2de6fa982a5a93ed (patch)
tree739caf8c461053357daa9f162bef34516c7bf452 /src/backend/utils/cache/lsyscache.c
parentInitial commit. (diff)
downloadpostgresql-15-5e45211a64149b3c659b90ff2de6fa982a5a93ed.tar.xz
postgresql-15-5e45211a64149b3c659b90ff2de6fa982a5a93ed.zip
Adding upstream version 15.5.upstream/15.5
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/backend/utils/cache/lsyscache.c')
-rw-r--r--src/backend/utils/cache/lsyscache.c3580
1 files changed, 3580 insertions, 0 deletions
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
new file mode 100644
index 0000000..1b7e11b
--- /dev/null
+++ b/src/backend/utils/cache/lsyscache.c
@@ -0,0 +1,3580 @@
+/*-------------------------------------------------------------------------
+ *
+ * lsyscache.c
+ * Convenience routines for common queries in the system catalog cache.
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/utils/cache/lsyscache.c
+ *
+ * NOTES
+ * Eventually, the index information should go through here, too.
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/hash.h"
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "bootstrap/bootstrap.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_amop.h"
+#include "catalog/pg_amproc.h"
+#include "catalog/pg_cast.h"
+#include "catalog/pg_collation.h"
+#include "catalog/pg_constraint.h"
+#include "catalog/pg_language.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_opclass.h"
+#include "catalog/pg_operator.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_range.h"
+#include "catalog/pg_statistic.h"
+#include "catalog/pg_transform.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_opfamily
+ *
+ * Return t iff operator 'opno' is in operator family 'opfamily'.
+ *
+ * This function only considers search operators, not ordering operators.
+ */
+bool
+op_in_opfamily(Oid opno, Oid opfamily)
+{
+ return SearchSysCacheExists3(AMOPOPID,
+ ObjectIdGetDatum(opno),
+ CharGetDatum(AMOP_SEARCH),
+ ObjectIdGetDatum(opfamily));
+}
+
+/*
+ * get_op_opfamily_strategy
+ *
+ * Get the operator's strategy number within the specified opfamily,
+ * or 0 if it's not a member of the opfamily.
+ *
+ * This function only considers search operators, not ordering operators.
+ */
+int
+get_op_opfamily_strategy(Oid opno, Oid opfamily)
+{
+ HeapTuple tp;
+ Form_pg_amop amop_tup;
+ int result;
+
+ tp = SearchSysCache3(AMOPOPID,
+ ObjectIdGetDatum(opno),
+ CharGetDatum(AMOP_SEARCH),
+ ObjectIdGetDatum(opfamily));
+ if (!HeapTupleIsValid(tp))
+ 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;
+
+ 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_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
+ * with the specified datatypes for the specified opfamily.
+ *
+ * Returns InvalidOid if there is no pg_amop entry for the given keys.
+ */
+Oid
+get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype,
+ int16 strategy)
+{
+ HeapTuple tp;
+ Form_pg_amop amop_tup;
+ Oid result;
+
+ tp = SearchSysCache4(AMOPSTRATEGY,
+ ObjectIdGetDatum(opfamily),
+ ObjectIdGetDatum(lefttype),
+ ObjectIdGetDatum(righttype),
+ Int16GetDatum(strategy));
+ if (!HeapTupleIsValid(tp))
+ return InvalidOid;
+ amop_tup = (Form_pg_amop) GETSTRUCT(tp);
+ result = amop_tup->amopopr;
+ ReleaseSysCache(tp);
+ return result;
+}
+
+/*
+ * 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_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 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_ordering_op_for_equality_op(Oid opno, bool use_lhs_type)
+{
+ Oid result = InvalidOid;
+ 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 */
+ if (aform->amopmethod != BTREE_AM_OID)
+ continue;
+
+ if (aform->amopstrategy == BTEqualStrategyNumber)
+ {
+ /* 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);
+
+ return result;
+}
+
+/*
+ * get_op_hash_functions
+ * Get the OID(s) of the standard 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++)
+ {
+ HeapTuple tuple = &catlist->members[i]->tuple;
+ Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
+
+ if (aform->amopmethod == HASH_AM_OID &&
+ aform->amopstrategy == HTEqualStrategyNumber)
+ {
+ /*
+ * 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,
+ HASHSTANDARD_PROC);
+ 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,
+ HASHSTANDARD_PROC);
+ 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;
+ }
+ }
+ }
+
+ 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);
+ }
+ }
+
+ 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;
+}
+
+/*
+ * comparison_ops_are_compatible
+ * Return true if the two given comparison 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 opfamily.
+ * For example, '<' and '>=' ops match if they belong to the same family.
+ *
+ * (This is identical to equality_ops_are_compatible(), except that we
+ * don't bother to examine hash opclasses.)
+ */
+bool
+comparison_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);
+
+ if (op_form->amopmethod == BTREE_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;
+}
+
+
+/* ---------- ATTRIBUTE CACHES ---------- */
+
+/*
+ * get_attname
+ * Given the relation id and the attribute number, return the "attname"
+ * field from the attribute relation as a palloc'ed string.
+ *
+ * If no such attribute exists and missing_ok is true, NULL is returned;
+ * otherwise a not-intended-for-user-consumption error is thrown.
+ */
+char *
+get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache2(ATTNUM,
+ ObjectIdGetDatum(relid), Int16GetDatum(attnum));
+ 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;
+ }
+
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+ attnum, relid);
+ return NULL;
+}
+
+/*
+ * get_attnum
+ *
+ * Given the relation id and the attribute name,
+ * return the "attnum" field from the attribute relation.
+ *
+ * Returns InvalidAttrNumber if the attr doesn't exist (or is dropped).
+ */
+AttrNumber
+get_attnum(Oid relid, const char *attname)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCacheAttName(relid, attname);
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
+ AttrNumber result;
+
+ result = att_tup->attnum;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidAttrNumber;
+}
+
+/*
+ * get_attstattarget
+ *
+ * Given the relation id and the attribute number,
+ * return the "attstattarget" field from the attribute relation.
+ *
+ * Errors if not found.
+ */
+int
+get_attstattarget(Oid relid, AttrNumber attnum)
+{
+ HeapTuple tp;
+ Form_pg_attribute att_tup;
+ int result;
+
+ tp = SearchSysCache2(ATTNUM,
+ ObjectIdGetDatum(relid),
+ Int16GetDatum(attnum));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+ attnum, relid);
+ att_tup = (Form_pg_attribute) GETSTRUCT(tp);
+ result = att_tup->attstattarget;
+ ReleaseSysCache(tp);
+ return result;
+}
+
+/*
+ * get_attgenerated
+ *
+ * Given the relation id and the attribute number,
+ * return the "attgenerated" field from the attribute relation.
+ *
+ * Errors if not found.
+ *
+ * Since not generated is represented by '\0', this can also be used as a
+ * Boolean test.
+ */
+char
+get_attgenerated(Oid relid, AttrNumber attnum)
+{
+ HeapTuple tp;
+ Form_pg_attribute att_tup;
+ char result;
+
+ tp = SearchSysCache2(ATTNUM,
+ ObjectIdGetDatum(relid),
+ Int16GetDatum(attnum));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+ attnum, relid);
+ att_tup = (Form_pg_attribute) GETSTRUCT(tp);
+ result = att_tup->attgenerated;
+ ReleaseSysCache(tp);
+ return result;
+}
+
+/*
+ * get_atttype
+ *
+ * Given the relation OID and the attribute number with the relation,
+ * return the attribute type OID.
+ */
+Oid
+get_atttype(Oid relid, AttrNumber attnum)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache2(ATTNUM,
+ ObjectIdGetDatum(relid),
+ Int16GetDatum(attnum));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
+ Oid result;
+
+ result = att_tup->atttypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/*
+ * get_atttypetypmodcoll
+ *
+ * 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, this routine
+ * raises an error if it can't obtain the information.
+ */
+void
+get_atttypetypmodcoll(Oid relid, AttrNumber attnum,
+ Oid *typid, int32 *typmod, Oid *collid)
+{
+ HeapTuple tp;
+ Form_pg_attribute att_tup;
+
+ tp = SearchSysCache2(ATTNUM,
+ ObjectIdGetDatum(relid),
+ Int16GetDatum(attnum));
+ if (!HeapTupleIsValid(tp))
+ 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);
+}
+
+/*
+ * get_attoptions
+ *
+ * Given the relation id and the attribute number,
+ * return the attribute options text[] datum, if any.
+ */
+Datum
+get_attoptions(Oid relid, int16 attnum)
+{
+ HeapTuple tuple;
+ Datum attopts;
+ Datum result;
+ bool isnull;
+
+ tuple = SearchSysCache2(ATTNUM,
+ ObjectIdGetDatum(relid),
+ Int16GetDatum(attnum));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+ attnum, relid);
+
+ attopts = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
+ &isnull);
+
+ if (isnull)
+ result = (Datum) 0;
+ else
+ result = datumCopy(attopts, false, -1); /* text[] */
+
+ ReleaseSysCache(tuple);
+
+ return result;
+}
+
+/* ---------- PG_CAST CACHE ---------- */
+
+/*
+ * get_cast_oid - given two type OIDs, look up a cast OID
+ *
+ * If missing_ok is false, throw an error if the cast is not found. If
+ * true, just return InvalidOid.
+ */
+Oid
+get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok)
+{
+ Oid oid;
+
+ oid = GetSysCacheOid2(CASTSOURCETARGET, Anum_pg_cast_oid,
+ ObjectIdGetDatum(sourcetypeid),
+ ObjectIdGetDatum(targettypeid));
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("cast from type %s to type %s does not exist",
+ format_type_be(sourcetypeid),
+ format_type_be(targettypeid))));
+ return oid;
+}
+
+/* ---------- COLLATION CACHE ---------- */
+
+/*
+ * 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 collation.
+ *
+ * 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;
+}
+
+bool
+get_collation_isdeterministic(Oid colloid)
+{
+ HeapTuple tp;
+ Form_pg_collation colltup;
+ bool result;
+
+ tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(colloid));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for collation %u", colloid);
+ colltup = (Form_pg_collation) GETSTRUCT(tp);
+ result = colltup->collisdeterministic;
+ ReleaseSysCache(tp);
+ return result;
+}
+
+/* ---------- 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;
+}
+
+/*
+ * get_constraint_index
+ * Given the OID of a unique, primary-key, or exclusion constraint,
+ * return the OID of the underlying index.
+ *
+ * Returns InvalidOid if the constraint could not be found or is of
+ * the wrong type.
+ *
+ * The intent of this function is to return the index "owned" by the
+ * specified constraint. Therefore we must check contype, since some
+ * pg_constraint entries (e.g. for foreign-key constraints) store the
+ * OID of an index that is referenced but not owned by the constraint.
+ */
+Oid
+get_constraint_index(Oid conoid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(conoid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
+ Oid result;
+
+ if (contup->contype == CONSTRAINT_UNIQUE ||
+ contup->contype == CONSTRAINT_PRIMARY ||
+ contup->contype == CONSTRAINT_EXCLUSION)
+ result = contup->conindid;
+ else
+ result = InvalidOid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/* ---------- LANGUAGE CACHE ---------- */
+
+char *
+get_language_name(Oid langoid, bool missing_ok)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(LANGOID, ObjectIdGetDatum(langoid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_language lantup = (Form_pg_language) GETSTRUCT(tp);
+ char *result;
+
+ result = pstrdup(NameStr(lantup->lanname));
+ ReleaseSysCache(tp);
+ return result;
+ }
+
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for language %u",
+ langoid);
+ return NULL;
+}
+
+/* ---------- OPCLASS CACHE ---------- */
+
+/*
+ * get_opclass_family
+ *
+ * Returns the OID of the operator family the opclass belongs to.
+ */
+Oid
+get_opclass_family(Oid opclass)
+{
+ HeapTuple tp;
+ Form_pg_opclass cla_tup;
+ Oid result;
+
+ 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->opcfamily;
+ ReleaseSysCache(tp);
+ return result;
+}
+
+/*
+ * get_opclass_input_type
+ *
+ * Returns the OID of the datatype the opclass indexes.
+ */
+Oid
+get_opclass_input_type(Oid opclass)
+{
+ HeapTuple tp;
+ Form_pg_opclass cla_tup;
+ Oid result;
+
+ 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->opcintype;
+ ReleaseSysCache(tp);
+ return result;
+}
+
+/*
+ * get_opclass_opfamily_and_input_type
+ *
+ * Returns the OID of the operator family the opclass belongs to,
+ * the OID of the datatype the opclass indexes
+ */
+bool
+get_opclass_opfamily_and_input_type(Oid opclass, Oid *opfamily, Oid *opcintype)
+{
+ HeapTuple tp;
+ Form_pg_opclass cla_tup;
+
+ tp = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
+ if (!HeapTupleIsValid(tp))
+ return false;
+
+ cla_tup = (Form_pg_opclass) GETSTRUCT(tp);
+
+ *opfamily = cla_tup->opcfamily;
+ *opcintype = cla_tup->opcintype;
+
+ ReleaseSysCache(tp);
+
+ return true;
+}
+
+/* ---------- OPERATOR CACHE ---------- */
+
+/*
+ * get_opcode
+ *
+ * Returns the regproc id of the routine used to implement an
+ * operator given the operator oid.
+ */
+RegProcedure
+get_opcode(Oid opno)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
+ RegProcedure result;
+
+ result = optup->oprcode;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return (RegProcedure) InvalidOid;
+}
+
+/*
+ * get_opname
+ * 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.
+ */
+char *
+get_opname(Oid opno)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
+ char *result;
+
+ result = pstrdup(NameStr(optup->oprname));
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return NULL;
+}
+
+/*
+ * get_op_rettype
+ * Given operator oid, return the operator's result type.
+ */
+Oid
+get_op_rettype(Oid opno)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
+ Oid result;
+
+ result = optup->oprresult;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/*
+ * op_input_types
+ *
+ * Returns the left and right input datatypes for an operator
+ * (InvalidOid if not relevant).
+ */
+void
+op_input_types(Oid opno, Oid *lefttype, Oid *righttype)
+{
+ HeapTuple tp;
+ Form_pg_operator optup;
+
+ 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_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.)
+ *
+ * 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.
+ */
+bool
+op_mergejoinable(Oid opno, Oid inputtype)
+{
+ bool result = false;
+ HeapTuple tp;
+ TypeCacheEntry *typentry;
+
+ /*
+ * 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.
+ */
+ 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);
+
+ result = optup->oprcanmerge;
+ ReleaseSysCache(tp);
+ }
+ }
+ return result;
+}
+
+/*
+ * op_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, Oid inputtype)
+{
+ bool result = false;
+ HeapTuple tp;
+ TypeCacheEntry *typentry;
+
+ /* As in op_mergejoinable, let the typcache handle the hard cases */
+ if (opno == ARRAY_EQ_OP)
+ {
+ typentry = lookup_type_cache(inputtype, TYPECACHE_HASH_PROC);
+ if (typentry->hash_proc == F_HASH_ARRAY)
+ result = true;
+ }
+ else if (opno == RECORD_EQ_OP)
+ {
+ typentry = lookup_type_cache(inputtype, TYPECACHE_HASH_PROC);
+ if (typentry->hash_proc == F_HASH_RECORD)
+ 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);
+ }
+ }
+ return result;
+}
+
+/*
+ * op_strict
+ *
+ * Get the proisstrict flag for the operator's underlying function.
+ */
+bool
+op_strict(Oid opno)
+{
+ RegProcedure funcid = get_opcode(opno);
+
+ if (funcid == (RegProcedure) InvalidOid)
+ elog(ERROR, "operator %u does not exist", opno);
+
+ return func_strict((Oid) funcid);
+}
+
+/*
+ * op_volatile
+ *
+ * Get the provolatile flag for the operator's underlying function.
+ */
+char
+op_volatile(Oid opno)
+{
+ RegProcedure funcid = get_opcode(opno);
+
+ if (funcid == (RegProcedure) InvalidOid)
+ elog(ERROR, "operator %u does not exist", opno);
+
+ return func_volatile((Oid) funcid);
+}
+
+/*
+ * get_commutator
+ *
+ * Returns the corresponding commutator of an operator.
+ */
+Oid
+get_commutator(Oid opno)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
+ Oid result;
+
+ result = optup->oprcom;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/*
+ * get_negator
+ *
+ * Returns the corresponding negator of an operator.
+ */
+Oid
+get_negator(Oid opno)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
+ Oid result;
+
+ result = optup->oprnegate;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/*
+ * get_oprrest
+ *
+ * Returns procedure id for computing selectivity of an operator.
+ */
+RegProcedure
+get_oprrest(Oid opno)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
+ RegProcedure result;
+
+ result = optup->oprrest;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return (RegProcedure) InvalidOid;
+}
+
+/*
+ * get_oprjoin
+ *
+ * Returns procedure id for computing selectivity of a join.
+ */
+RegProcedure
+get_oprjoin(Oid opno)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
+ RegProcedure result;
+
+ result = optup->oprjoin;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ 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 = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
+ 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_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.
+ */
+Oid
+get_func_rettype(Oid funcid)
+{
+ HeapTuple tp;
+ Oid result;
+
+ tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
+ if (!HeapTupleIsValid(tp))
+ 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_variadictype
+ * Given procedure id, return the function's provariadic field.
+ */
+Oid
+get_func_variadictype(Oid funcid)
+{
+ HeapTuple tp;
+ Oid result;
+
+ tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for function %u", funcid);
+
+ result = ((Form_pg_proc) GETSTRUCT(tp))->provariadic;
+ 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 = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for function %u", funcid);
+
+ result = ((Form_pg_proc) GETSTRUCT(tp))->proretset;
+ ReleaseSysCache(tp);
+ return result;
+}
+
+/*
+ * func_strict
+ * Given procedure id, return the function's proisstrict flag.
+ */
+bool
+func_strict(Oid funcid)
+{
+ HeapTuple tp;
+ 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;
+}
+
+/*
+ * func_parallel
+ * Given procedure id, return the function's proparallel flag.
+ */
+char
+func_parallel(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))->proparallel;
+ ReleaseSysCache(tp);
+ return result;
+}
+
+/*
+ * get_func_prokind
+ * Given procedure id, return the routine kind.
+ */
+char
+get_func_prokind(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))->prokind;
+ ReleaseSysCache(tp);
+ return result;
+}
+
+/*
+ * get_func_leakproof
+ * Given procedure id, return the function's leakproof field.
+ */
+bool
+get_func_leakproof(Oid funcid)
+{
+ HeapTuple tp;
+ 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))->proleakproof;
+ ReleaseSysCache(tp);
+ return result;
+}
+
+/*
+ * get_func_support
+ *
+ * Returns the support function OID associated with a given function,
+ * or InvalidOid if there is none.
+ */
+RegProcedure
+get_func_support(Oid funcid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_proc functup = (Form_pg_proc) GETSTRUCT(tp);
+ RegProcedure result;
+
+ result = functup->prosupport;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return (RegProcedure) InvalidOid;
+}
+
+/* ---------- 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 GetSysCacheOid2(RELNAMENSP, Anum_pg_class_oid,
+ PointerGetDatum(relname),
+ ObjectIdGetDatum(relnamespace));
+}
+
+#ifdef NOT_USED
+/*
+ * get_relnatts
+ *
+ * Returns the number of attributes for a given relation.
+ */
+int
+get_relnatts(Oid relid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
+ int result;
+
+ result = reltup->relnatts;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidAttrNumber;
+}
+#endif
+
+/*
+ * get_rel_name
+ * 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.
+ */
+char *
+get_rel_name(Oid relid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
+ char *result;
+
+ result = pstrdup(NameStr(reltup->relname));
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return NULL;
+}
+
+/*
+ * get_rel_namespace
+ *
+ * Returns the pg_namespace OID associated with a given relation.
+ */
+Oid
+get_rel_namespace(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->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 = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
+ Oid result;
+
+ result = reltup->reltype;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/*
+ * get_rel_relkind
+ *
+ * Returns the relkind associated with a given relation.
+ */
+char
+get_rel_relkind(Oid relid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
+ char result;
+
+ result = reltup->relkind;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return '\0';
+}
+
+/*
+ * get_rel_relispartition
+ *
+ * Returns the relispartition flag associated with a given relation.
+ */
+bool
+get_rel_relispartition(Oid relid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
+ bool result;
+
+ result = reltup->relispartition;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return false;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * get_rel_persistence
+ *
+ * Returns the relpersistence associated with a given relation.
+ */
+char
+get_rel_persistence(Oid relid)
+{
+ HeapTuple tp;
+ Form_pg_class reltup;
+ char result;
+
+ tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for relation %u", relid);
+ reltup = (Form_pg_class) GETSTRUCT(tp);
+ result = reltup->relpersistence;
+ ReleaseSysCache(tp);
+
+ return result;
+}
+
+
+/* ---------- TRANSFORM CACHE ---------- */
+
+Oid
+get_transform_fromsql(Oid typid, Oid langid, List *trftypes)
+{
+ HeapTuple tup;
+
+ if (!list_member_oid(trftypes, typid))
+ return InvalidOid;
+
+ tup = SearchSysCache2(TRFTYPELANG, typid, langid);
+ if (HeapTupleIsValid(tup))
+ {
+ Oid funcid;
+
+ funcid = ((Form_pg_transform) GETSTRUCT(tup))->trffromsql;
+ ReleaseSysCache(tup);
+ return funcid;
+ }
+ else
+ return InvalidOid;
+}
+
+Oid
+get_transform_tosql(Oid typid, Oid langid, List *trftypes)
+{
+ HeapTuple tup;
+
+ if (!list_member_oid(trftypes, typid))
+ return InvalidOid;
+
+ tup = SearchSysCache2(TRFTYPELANG, typid, langid);
+ if (HeapTupleIsValid(tup))
+ {
+ Oid funcid;
+
+ funcid = ((Form_pg_transform) GETSTRUCT(tup))->trftosql;
+ ReleaseSysCache(tup);
+ return funcid;
+ }
+ 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 = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ 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.
+ */
+int16
+get_typlen(Oid typid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
+ int16 result;
+
+ result = typtup->typlen;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return 0;
+}
+
+/*
+ * get_typbyval
+ *
+ * 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)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
+ bool result;
+
+ result = typtup->typbyval;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ 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 = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ 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);
+}
+
+/*
+ * get_typlenbyvalalign
+ *
+ * A three-fer: given the type OID, return typlen, typbyval, typalign.
+ */
+void
+get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
+ char *typalign)
+{
+ 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);
+ *typlen = typtup->typlen;
+ *typbyval = typtup->typbyval;
+ *typalign = typtup->typalign;
+ 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 typeStruct->oid;
+}
+
+/*
+ * 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 = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
+ char result;
+
+ result = typtup->typalign;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return TYPALIGN_INT;
+}
+#endif
+
+char
+get_typstorage(Oid typid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
+ char result;
+
+ result = typtup->typstorage;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return TYPSTORAGE_PLAIN;
+}
+
+/*
+ * 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.
+ *
+ * NB: caller should be prepared to coerce result to correct datatype;
+ * the returned expression tree might produce something of the wrong type.
+ */
+Node *
+get_typdefault(Oid typid)
+{
+ HeapTuple typeTuple;
+ Form_pg_type type;
+ Datum datum;
+ bool isNull;
+ Node *expr;
+
+ typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (!HeapTupleIsValid(typeTuple))
+ elog(ERROR, "cache lookup failed for type %u", typid);
+ type = (Form_pg_type) GETSTRUCT(typeTuple);
+
+ /*
+ * 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);
+
+ if (!isNull)
+ {
+ /* We have an expression default */
+ expr = stringToNode(TextDatumGetCString(datum));
+ }
+ else
+ {
+ /* Perhaps we have a plain literal default */
+ datum = SysCacheGetAttr(TYPEOID,
+ typeTuple,
+ Anum_pg_type_typdefault,
+ &isNull);
+
+ if (!isNull)
+ {
+ char *strDefaultVal;
+
+ /* Convert text datum to C string */
+ strDefaultVal = TextDatumGetCString(datum);
+ /* Convert C string to a value of the given type */
+ 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,
+ type->typbyval);
+ pfree(strDefaultVal);
+ }
+ else
+ {
+ /* No default */
+ expr = NULL;
+ }
+ }
+
+ ReleaseSysCache(typeTuple);
+
+ 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)
+{
+ 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.
+ */
+ for (;;)
+ {
+ HeapTuple tup;
+ Form_pg_type typTup;
+
+ tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for type %u", typid);
+ typTup = (Form_pg_type) GETSTRUCT(tup);
+ if (typTup->typtype != TYPTYPE_DOMAIN)
+ {
+ /* Not a domain, so done */
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ Assert(*typmod == -1);
+ typid = typTup->typbasetype;
+ *typmod = typTup->typtypmod;
+
+ ReleaseSysCache(tup);
+ }
+
+ return typid;
+}
+
+/*
+ * get_typavgwidth
+ *
+ * Given a type OID and a typmod value (pass -1 if typmod is unknown),
+ * estimate the average width of values of the type. This is used by
+ * the planner, which doesn't require absolutely correct results;
+ * it's OK (and expected) to guess if we don't know for sure.
+ */
+int32
+get_typavgwidth(Oid typid, int32 typmod)
+{
+ int typlen = get_typlen(typid);
+ int32 maxwidth;
+
+ /*
+ * Easy if it's a fixed-width type
+ */
+ if (typlen > 0)
+ return typlen;
+
+ /*
+ * type_maximum_size knows the encoding of typmod for some datatypes;
+ * don't duplicate that knowledge here.
+ */
+ maxwidth = type_maximum_size(typid, typmod);
+ 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.
+ */
+ if (typid == BPCHAROID)
+ return maxwidth;
+ if (maxwidth <= 32)
+ return maxwidth; /* assume full width */
+ if (maxwidth < 1000)
+ 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, and
+ * use a fixed estimate.
+ */
+ return 32 + (1000 - 32) / 2;
+ }
+
+ /*
+ * Oops, we have no idea ... wild guess time.
+ */
+ return 32;
+}
+
+/*
+ * get_typtype
+ *
+ * Given the type OID, find if it is a basic type, a complex type, etc.
+ * It returns the null char if the cache lookup fails...
+ */
+char
+get_typtype(Oid typid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
+ char result;
+
+ result = typtup->typtype;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return '\0';
+}
+
+/*
+ * type_is_rowtype
+ *
+ * Convenience function to determine whether a type OID represents
+ * a "rowtype" type --- either RECORD or a named composite type
+ * (including a domain over a named composite type).
+ */
+bool
+type_is_rowtype(Oid typid)
+{
+ if (typid == RECORDOID)
+ return true; /* easy case */
+ switch (get_typtype(typid))
+ {
+ case TYPTYPE_COMPOSITE:
+ return true;
+ case TYPTYPE_DOMAIN:
+ if (get_typtype(getBaseType(typid)) == TYPTYPE_COMPOSITE)
+ return true;
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+/*
+ * 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);
+}
+
+/*
+ * type_is_multirange
+ * Returns true if the given type is a multirange type.
+ */
+bool
+type_is_multirange(Oid typid)
+{
+ return (get_typtype(typid) == TYPTYPE_MULTIRANGE);
+}
+
+/*
+ * 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
+ *
+ * Given the type OID, get the typrelid (InvalidOid if not a complex
+ * type).
+ */
+Oid
+get_typ_typrelid(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->typrelid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/*
+ * get_element_type
+ *
+ * Given the type OID, get the typelem (InvalidOid if not an array type).
+ *
+ * NB: this only succeeds for "true" arrays having array_subscript_handler
+ * as typsubscript. For other types, InvalidOid is returned independently
+ * of whether they have typelem or typsubscript set.
+ */
+Oid
+get_element_type(Oid typid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
+ Oid result;
+
+ if (IsTrueArrayType(typtup))
+ result = typtup->typelem;
+ else
+ result = InvalidOid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/*
+ * get_array_type
+ *
+ * Given the type OID, get the corresponding "true" array type.
+ * Returns InvalidOid if no array type can be found.
+ */
+Oid
+get_array_type(Oid typid)
+{
+ HeapTuple tp;
+ Oid result = InvalidOid;
+
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (HeapTupleIsValid(tp))
+ {
+ result = ((Form_pg_type) GETSTRUCT(tp))->typarray;
+ ReleaseSysCache(tp);
+ }
+ return result;
+}
+
+/*
+ * get_promoted_array_type
+ *
+ * The "promoted" type is what you'd get from an ARRAY(SELECT ...)
+ * construct, that is, either the corresponding "true" array type
+ * if the input is a scalar type that has such an array type,
+ * or the same type if the input is already a "true" array type.
+ * Returns InvalidOid if neither rule is satisfied.
+ */
+Oid
+get_promoted_array_type(Oid typid)
+{
+ Oid array_type = get_array_type(typid);
+
+ if (OidIsValid(array_type))
+ return array_type;
+ if (OidIsValid(get_element_type(typid)))
+ return typid;
+ return InvalidOid;
+}
+
+/*
+ * 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;
+
+ 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;
+
+ /* This test must match get_element_type */
+ if (IsTrueArrayType(typTup))
+ result = typTup->typelem;
+ else
+ result = InvalidOid;
+ ReleaseSysCache(tup);
+ return result;
+ }
+
+ typid = typTup->typbasetype;
+ ReleaseSysCache(tup);
+ }
+
+ /* Like get_element_type, silently return InvalidOid for bogus input */
+ return InvalidOid;
+}
+
+/*
+ * getTypeInputInfo
+ *
+ * Get info needed for converting values of a type to internal form
+ */
+void
+getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
+{
+ HeapTuple typeTuple;
+ Form_pg_type pt;
+
+ typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type));
+ if (!HeapTupleIsValid(typeTuple))
+ elog(ERROR, "cache lookup failed for type %u", type);
+ pt = (Form_pg_type) GETSTRUCT(typeTuple);
+
+ if (!pt->typisdefined)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("type %s is only a shell",
+ format_type_be(type))));
+ if (!OidIsValid(pt->typinput))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no input function available for type %s",
+ format_type_be(type))));
+
+ *typInput = pt->typinput;
+ *typIOParam = getTypeIOParam(typeTuple);
+
+ ReleaseSysCache(typeTuple);
+}
+
+/*
+ * getTypeOutputInfo
+ *
+ * Get info needed for printing values of a type
+ */
+void
+getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
+{
+ HeapTuple typeTuple;
+ Form_pg_type pt;
+
+ typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type));
+ if (!HeapTupleIsValid(typeTuple))
+ elog(ERROR, "cache lookup failed for type %u", type);
+ pt = (Form_pg_type) GETSTRUCT(typeTuple);
+
+ if (!pt->typisdefined)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("type %s is only a shell",
+ format_type_be(type))));
+ if (!OidIsValid(pt->typoutput))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no output function available for type %s",
+ format_type_be(type))));
+
+ *typOutput = pt->typoutput;
+ *typIsVarlena = (!pt->typbyval) && (pt->typlen == -1);
+
+ ReleaseSysCache(typeTuple);
+}
+
+/*
+ * getTypeBinaryInputInfo
+ *
+ * Get info needed for binary input of values of a type
+ */
+void
+getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typIOParam)
+{
+ HeapTuple typeTuple;
+ Form_pg_type pt;
+
+ typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type));
+ if (!HeapTupleIsValid(typeTuple))
+ elog(ERROR, "cache lookup failed for type %u", type);
+ pt = (Form_pg_type) GETSTRUCT(typeTuple);
+
+ if (!pt->typisdefined)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("type %s is only a shell",
+ format_type_be(type))));
+ if (!OidIsValid(pt->typreceive))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary input function available for type %s",
+ format_type_be(type))));
+
+ *typReceive = pt->typreceive;
+ *typIOParam = getTypeIOParam(typeTuple);
+
+ ReleaseSysCache(typeTuple);
+}
+
+/*
+ * getTypeBinaryOutputInfo
+ *
+ * Get info needed for binary output of values of a type
+ */
+void
+getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena)
+{
+ HeapTuple typeTuple;
+ Form_pg_type pt;
+
+ typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type));
+ if (!HeapTupleIsValid(typeTuple))
+ elog(ERROR, "cache lookup failed for type %u", type);
+ pt = (Form_pg_type) GETSTRUCT(typeTuple);
+
+ if (!pt->typisdefined)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("type %s is only a shell",
+ format_type_be(type))));
+ if (!OidIsValid(pt->typsend))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary output function available for type %s",
+ format_type_be(type))));
+
+ *typSend = pt->typsend;
+ *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));
+}
+
+
+/*
+ * get_typsubscript
+ *
+ * Given the type OID, return the type's subscripting handler's OID,
+ * if it has one.
+ *
+ * If typelemp isn't NULL, we also store the type's typelem value there.
+ * This saves some callers an extra catalog lookup.
+ */
+RegProcedure
+get_typsubscript(Oid typid, Oid *typelemp)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_type typform = (Form_pg_type) GETSTRUCT(tp);
+ RegProcedure handler = typform->typsubscript;
+
+ if (typelemp)
+ *typelemp = typform->typelem;
+ ReleaseSysCache(tp);
+ return handler;
+ }
+ else
+ {
+ if (typelemp)
+ *typelemp = InvalidOid;
+ return InvalidOid;
+ }
+}
+
+/*
+ * getSubscriptingRoutines
+ *
+ * Given the type OID, fetch the type's subscripting methods struct.
+ * Return NULL if type is not subscriptable.
+ *
+ * If typelemp isn't NULL, we also store the type's typelem value there.
+ * This saves some callers an extra catalog lookup.
+ */
+const struct SubscriptRoutines *
+getSubscriptingRoutines(Oid typid, Oid *typelemp)
+{
+ RegProcedure typsubscript = get_typsubscript(typid, typelemp);
+
+ if (!OidIsValid(typsubscript))
+ return NULL;
+
+ return (const struct SubscriptRoutines *)
+ DatumGetPointer(OidFunctionCall0(typsubscript));
+}
+
+
+/* ---------- STATISTICS CACHE ---------- */
+
+/*
+ * get_attavgwidth
+ *
+ * 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;
+
+ 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))
+ {
+ stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth;
+ ReleaseSysCache(tp);
+ if (stawidth > 0)
+ return stawidth;
+ }
+ return 0;
+}
+
+/*
+ * get_attstatsslot
+ *
+ * Extract the contents of a "slot" of a pg_statistic tuple.
+ * Returns true if requested slot type was found, else false.
+ *
+ * Unlike other routines in this file, this takes a pointer to an
+ * 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.
+ *
+ * sslot: pointer to output area (typically, a local variable in the caller).
+ * statstuple: pg_statistic tuple to be examined.
+ * reqkind: STAKIND code for desired statistics slot kind.
+ * reqop: STAOP value wanted, or InvalidOid if don't care.
+ * flags: bitmask of ATTSTATSSLOT_VALUES and/or ATTSTATSSLOT_NUMBERS.
+ *
+ * If a matching slot is found, true is returned, and *sslot is filled thus:
+ * staop: receives the actual STAOP value.
+ * stacoll: receives the actual STACOLL value.
+ * valuetype: receives actual datatype of the elements of stavalues.
+ * values: receives pointer to an array of the slot's stavalues.
+ * nvalues: receives number of stavalues.
+ * numbers: receives pointer to an array of the slot's stanumbers (as float4).
+ * nnumbers: receives number of stanumbers.
+ *
+ * valuetype/values/nvalues are InvalidOid/NULL/0 if ATTSTATSSLOT_VALUES
+ * wasn't specified. Likewise, numbers/nnumbers are NULL/0 if
+ * ATTSTATSSLOT_NUMBERS wasn't specified.
+ *
+ * If no matching slot is found, false is returned, and *sslot is zeroed.
+ *
+ * Note that the current API doesn't allow for searching for a slot with
+ * a particular collation. If we ever actually support recording more than
+ * one collation, we'll have to extend the API, but for now simple is good.
+ *
+ * The data referred to by the fields of sslot is locally palloc'd and
+ * is independent of the original pg_statistic tuple. When the caller
+ * is done with it, call free_attstatsslot to release the palloc'd data.
+ *
+ * If it's desirable to call free_attstatsslot when get_attstatsslot might
+ * not have been called, memset'ing sslot to zeroes will allow that.
+ */
+bool
+get_attstatsslot(AttStatsSlot *sslot, HeapTuple statstuple,
+ int reqkind, Oid reqop, int flags)
+{
+ Form_pg_statistic stats = (Form_pg_statistic) GETSTRUCT(statstuple);
+ int i;
+ Datum val;
+ bool isnull;
+ ArrayType *statarray;
+ Oid arrayelemtype;
+ int narrayelem;
+ HeapTuple typeTuple;
+ Form_pg_type typeForm;
+
+ /* initialize *sslot properly */
+ memset(sslot, 0, sizeof(AttStatsSlot));
+
+ for (i = 0; i < STATISTIC_NUM_SLOTS; i++)
+ {
+ if ((&stats->stakind1)[i] == reqkind &&
+ (reqop == InvalidOid || (&stats->staop1)[i] == reqop))
+ break;
+ }
+ if (i >= STATISTIC_NUM_SLOTS)
+ return false; /* not there */
+
+ sslot->staop = (&stats->staop1)[i];
+ sslot->stacoll = (&stats->stacoll1)[i];
+
+ if (flags & ATTSTATSSLOT_VALUES)
+ {
+ val = SysCacheGetAttr(STATRELATTINH, statstuple,
+ Anum_pg_statistic_stavalues1 + i,
+ &isnull);
+ if (isnull)
+ elog(ERROR, "stavalues is null");
+
+ /*
+ * Detoast the array if needed, and in any case make a copy that's
+ * under control of this AttStatsSlot.
+ */
+ statarray = DatumGetArrayTypePCopy(val);
+
+ /*
+ * Extract the actual array element type, and pass it back in case the
+ * caller needs it.
+ */
+ sslot->valuetype = arrayelemtype = ARR_ELEMTYPE(statarray);
+
+ /* Need info about element type */
+ typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(arrayelemtype));
+ if (!HeapTupleIsValid(typeTuple))
+ elog(ERROR, "cache lookup failed for type %u", arrayelemtype);
+ typeForm = (Form_pg_type) GETSTRUCT(typeTuple);
+
+ /* Deconstruct array into Datum elements; NULLs not expected */
+ deconstruct_array(statarray,
+ arrayelemtype,
+ typeForm->typlen,
+ typeForm->typbyval,
+ typeForm->typalign,
+ &sslot->values, NULL, &sslot->nvalues);
+
+ /*
+ * If the element type is pass-by-reference, we now have a bunch of
+ * Datums that are pointers into the statarray, so we need to keep
+ * that until free_attstatsslot. Otherwise, all the useful info is in
+ * sslot->values[], so we can free the array object immediately.
+ */
+ if (!typeForm->typbyval)
+ sslot->values_arr = statarray;
+ else
+ pfree(statarray);
+
+ ReleaseSysCache(typeTuple);
+ }
+
+ if (flags & ATTSTATSSLOT_NUMBERS)
+ {
+ val = SysCacheGetAttr(STATRELATTINH, statstuple,
+ Anum_pg_statistic_stanumbers1 + i,
+ &isnull);
+ if (isnull)
+ elog(ERROR, "stanumbers is null");
+
+ /*
+ * Detoast the array if needed, and in any case make a copy that's
+ * under control of this AttStatsSlot.
+ */
+ statarray = DatumGetArrayTypePCopy(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.
+ */
+ narrayelem = ARR_DIMS(statarray)[0];
+ if (ARR_NDIM(statarray) != 1 || narrayelem <= 0 ||
+ ARR_HASNULL(statarray) ||
+ ARR_ELEMTYPE(statarray) != FLOAT4OID)
+ elog(ERROR, "stanumbers is not a 1-D float4 array");
+
+ /* Give caller a pointer directly into the statarray */
+ sslot->numbers = (float4 *) ARR_DATA_PTR(statarray);
+ sslot->nnumbers = narrayelem;
+
+ /* We'll free the statarray in free_attstatsslot */
+ sslot->numbers_arr = statarray;
+ }
+
+ return true;
+}
+
+/*
+ * free_attstatsslot
+ * Free data allocated by get_attstatsslot
+ */
+void
+free_attstatsslot(AttStatsSlot *sslot)
+{
+ /* The values[] array was separately palloc'd by deconstruct_array */
+ if (sslot->values)
+ pfree(sslot->values);
+ /* The numbers[] array points into numbers_arr, do not pfree it */
+ /* Free the detoasted array objects, if any */
+ if (sslot->values_arr)
+ pfree(sslot->values_arr);
+ if (sslot->numbers_arr)
+ pfree(sslot->numbers_arr);
+}
+
+/* ---------- PG_NAMESPACE CACHE ---------- */
+
+/*
+ * get_namespace_name
+ * Returns the name of a given namespace
+ *
+ * Returns a palloc'd copy of the string, or NULL if no such namespace.
+ */
+char *
+get_namespace_name(Oid nspid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(nspid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_namespace nsptup = (Form_pg_namespace) GETSTRUCT(tp);
+ char *result;
+
+ result = pstrdup(NameStr(nsptup->nspname));
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return NULL;
+}
+
+/*
+ * get_namespace_name_or_temp
+ * As above, but if it is this backend's temporary namespace, return
+ * "pg_temp" instead.
+ */
+char *
+get_namespace_name_or_temp(Oid nspid)
+{
+ if (isTempNamespace(nspid))
+ return pstrdup("pg_temp");
+ else
+ return get_namespace_name(nspid);
+}
+
+/* ---------- PG_RANGE CACHES ---------- */
+
+/*
+ * get_range_subtype
+ * Returns the subtype of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type.
+ */
+Oid
+get_range_subtype(Oid rangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngsubtype;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/*
+ * get_range_collation
+ * Returns the collation of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type,
+ * or if its subtype is not collatable.
+ */
+Oid
+get_range_collation(Oid rangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngcollation;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/*
+ * get_range_multirange
+ * Returns the multirange type of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type.
+ */
+Oid
+get_range_multirange(Oid rangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngmultitypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/*
+ * get_multirange_range
+ * Returns the range type of a given multirange
+ *
+ * Returns InvalidOid if the type is not a multirange.
+ */
+Oid
+get_multirange_range(Oid multirangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGEMULTIRANGE, ObjectIdGetDatum(multirangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/* ---------- PG_INDEX CACHE ---------- */
+
+/*
+ * get_index_column_opclass
+ *
+ * Given the index OID and column number,
+ * return opclass of the index column
+ * or InvalidOid if the index was not found
+ * or column is non-key one.
+ */
+Oid
+get_index_column_opclass(Oid index_oid, int attno)
+{
+ HeapTuple tuple;
+ Form_pg_index rd_index;
+ Datum datum;
+ bool isnull;
+ oidvector *indclass;
+ Oid opclass;
+
+ /* First we need to know the column's opclass. */
+
+ tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid));
+ if (!HeapTupleIsValid(tuple))
+ return InvalidOid;
+
+ rd_index = (Form_pg_index) GETSTRUCT(tuple);
+
+ /* caller is supposed to guarantee this */
+ Assert(attno > 0 && attno <= rd_index->indnatts);
+
+ /* Non-key attributes don't have an opclass */
+ if (attno > rd_index->indnkeyatts)
+ {
+ ReleaseSysCache(tuple);
+ return InvalidOid;
+ }
+
+ datum = SysCacheGetAttr(INDEXRELID, tuple,
+ Anum_pg_index_indclass, &isnull);
+ Assert(!isnull);
+
+ indclass = ((oidvector *) DatumGetPointer(datum));
+
+ Assert(attno <= indclass->dim1);
+ opclass = indclass->values[attno - 1];
+
+ ReleaseSysCache(tuple);
+
+ return opclass;
+}
+
+/*
+ * get_index_isreplident
+ *
+ * Given the index OID, return pg_index.indisreplident.
+ */
+bool
+get_index_isreplident(Oid index_oid)
+{
+ HeapTuple tuple;
+ Form_pg_index rd_index;
+ bool result;
+
+ tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid));
+ if (!HeapTupleIsValid(tuple))
+ return false;
+
+ rd_index = (Form_pg_index) GETSTRUCT(tuple);
+ result = rd_index->indisreplident;
+ ReleaseSysCache(tuple);
+
+ return result;
+}
+
+/*
+ * get_index_isvalid
+ *
+ * Given the index OID, return pg_index.indisvalid.
+ */
+bool
+get_index_isvalid(Oid index_oid)
+{
+ bool isvalid;
+ HeapTuple tuple;
+ Form_pg_index rd_index;
+
+ tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for index %u", index_oid);
+
+ rd_index = (Form_pg_index) GETSTRUCT(tuple);
+ isvalid = rd_index->indisvalid;
+ ReleaseSysCache(tuple);
+
+ return isvalid;
+}
+
+/*
+ * get_index_isclustered
+ *
+ * Given the index OID, return pg_index.indisclustered.
+ */
+bool
+get_index_isclustered(Oid index_oid)
+{
+ bool isclustered;
+ HeapTuple tuple;
+ Form_pg_index rd_index;
+
+ tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for index %u", index_oid);
+
+ rd_index = (Form_pg_index) GETSTRUCT(tuple);
+ isclustered = rd_index->indisclustered;
+ ReleaseSysCache(tuple);
+
+ return isclustered;
+}