/*------------------------------------------------------------------------- * * lsyscache.c * Convenience routines for common queries in the system catalog cache. * * Portions Copyright (c) 1996-2023, 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_subscription.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. * * Passing flags=0 can be useful to quickly check if the requested slot type * exists. In this case no arrays are extracted, so free_attstatsslot need * not be called. */ 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; 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 = SysCacheGetAttrNotNull(STATRELATTINH, statstuple, Anum_pg_statistic_stavalues1 + i); /* * 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 = SysCacheGetAttrNotNull(STATRELATTINH, statstuple, Anum_pg_statistic_stanumbers1 + i); /* * 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; 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 = SysCacheGetAttrNotNull(INDEXRELID, tuple, Anum_pg_index_indclass); 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; } /* * get_publication_oid - given a publication name, look up the OID * * If missing_ok is false, throw an error if name not found. If true, just * return InvalidOid. */ Oid get_publication_oid(const char *pubname, bool missing_ok) { Oid oid; oid = GetSysCacheOid1(PUBLICATIONNAME, Anum_pg_publication_oid, CStringGetDatum(pubname)); if (!OidIsValid(oid) && !missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("publication \"%s\" does not exist", pubname))); return oid; } /* * get_publication_name - given a publication Oid, look up the name * * If missing_ok is false, throw an error if name not found. If true, just * return NULL. */ char * get_publication_name(Oid pubid, bool missing_ok) { HeapTuple tup; char *pubname; Form_pg_publication pubform; tup = SearchSysCache1(PUBLICATIONOID, ObjectIdGetDatum(pubid)); if (!HeapTupleIsValid(tup)) { if (!missing_ok) elog(ERROR, "cache lookup failed for publication %u", pubid); return NULL; } pubform = (Form_pg_publication) GETSTRUCT(tup); pubname = pstrdup(NameStr(pubform->pubname)); ReleaseSysCache(tup); return pubname; } /* * get_subscription_oid - given a subscription name, look up the OID * * If missing_ok is false, throw an error if name not found. If true, just * return InvalidOid. */ Oid get_subscription_oid(const char *subname, bool missing_ok) { Oid oid; oid = GetSysCacheOid2(SUBSCRIPTIONNAME, Anum_pg_subscription_oid, MyDatabaseId, CStringGetDatum(subname)); if (!OidIsValid(oid) && !missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("subscription \"%s\" does not exist", subname))); return oid; } /* * get_subscription_name - given a subscription OID, look up the name * * If missing_ok is false, throw an error if name not found. If true, just * return NULL. */ char * get_subscription_name(Oid subid, bool missing_ok) { HeapTuple tup; char *subname; Form_pg_subscription subform; tup = SearchSysCache1(SUBSCRIPTIONOID, ObjectIdGetDatum(subid)); if (!HeapTupleIsValid(tup)) { if (!missing_ok) elog(ERROR, "cache lookup failed for subscription %u", subid); return NULL; } subform = (Form_pg_subscription) GETSTRUCT(tup); subname = pstrdup(NameStr(subform->subname)); ReleaseSysCache(tup); return subname; }