diff options
Diffstat (limited to 'src/backend/utils/cache/lsyscache.c')
-rw-r--r-- | src/backend/utils/cache/lsyscache.c | 3580 |
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..5564b53 --- /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-2021, 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 "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 PG_USED_FOR_ASSERTS_ONLY; + 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; +} |