X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Futils%2Fcache%2Flsyscache.c;h=7a4306e93f5545764cbd02e6aa042120b385790c;hb=c6e3ac11b60ac4a8942ab964252d51c1c0bd8845;hp=bed4fcdbce68d250b9b71c95b49bbbc729b9214c;hpb=c1d62bfd00f4d1ea0647e12947ca1de9fea39b33;p=postgresql diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index bed4fcdbce..7a4306e93f 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -3,102 +3,169 @@ * lsyscache.c * Convenience routines for common queries in the system catalog cache. * - * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.109 2003/11/09 21:30:37 tgl Exp $ + * src/backend/utils/cache/lsyscache.c * * NOTES * Eventually, the index information should go through here, too. *------------------------------------------------------------------------- */ #include "postgres.h" -#include "miscadmin.h" #include "access/hash.h" -#include "access/tupmacs.h" +#include "access/nbtree.h" +#include "bootstrap/bootstrap.h" #include "catalog/pg_amop.h" #include "catalog/pg_amproc.h" +#include "catalog/pg_collation.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" #include "catalog/pg_proc.h" -#include "catalog/pg_shadow.h" +#include "catalog/pg_range.h" #include "catalog/pg_statistic.h" #include "catalog/pg_type.h" +#include "miscadmin.h" #include "nodes/makefuncs.h" #include "utils/array.h" #include "utils/builtins.h" -#include "utils/catcache.h" #include "utils/datum.h" +#include "utils/fmgroids.h" #include "utils/lsyscache.h" +#include "utils/rel.h" #include "utils/syscache.h" +#include "utils/typcache.h" + +/* Hook for plugins to get control in get_attavgwidth() */ +get_attavgwidth_hook_type get_attavgwidth_hook = NULL; /* ---------- AMOP CACHES ---------- */ /* - * op_in_opclass + * op_in_opfamily * - * Return t iff operator 'opno' is in operator class 'opclass'. + * Return t iff operator 'opno' is in operator family 'opfamily'. + * + * This function only considers search operators, not ordering operators. */ bool -op_in_opclass(Oid opno, Oid opclass) +op_in_opfamily(Oid opno, Oid opfamily) +{ + return 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) { - return SearchSysCacheExists(AMOPOPID, - ObjectIdGetDatum(opno), - ObjectIdGetDatum(opclass), - 0, 0); + 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_opclass_properties + * get_op_opfamily_properties * - * Get the operator's strategy number and recheck (lossy) flag - * within the specified opclass. + * 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 opclass, + * 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_opclass_properties(Oid opno, Oid opclass, - int *strategy, bool *recheck) +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 = SearchSysCache(AMOPOPID, - ObjectIdGetDatum(opno), - ObjectIdGetDatum(opclass), - 0, 0); + 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 opclass %u", - opno, opclass); + elog(ERROR, "operator %u is not a member of opfamily %u", + opno, opfamily); amop_tup = (Form_pg_amop) GETSTRUCT(tp); *strategy = amop_tup->amopstrategy; - *recheck = amop_tup->amopreqcheck; + *lefttype = amop_tup->amoplefttype; + *righttype = amop_tup->amoprighttype; ReleaseSysCache(tp); } /* - * get_opclass_member + * get_opfamily_member * Get the OID of the operator that implements the specified strategy - * for the specified opclass. + * with the specified datatypes for the specified opfamily. * * Returns InvalidOid if there is no pg_amop entry for the given keys. */ Oid -get_opclass_member(Oid opclass, int16 strategy) +get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, + int16 strategy) { HeapTuple tp; Form_pg_amop amop_tup; Oid result; - tp = SearchSysCache(AMOPSTRATEGY, - ObjectIdGetDatum(opclass), - Int16GetDatum(strategy), - 0, 0); + tp = SearchSysCache4(AMOPSTRATEGY, + ObjectIdGetDatum(opfamily), + ObjectIdGetDatum(lefttype), + ObjectIdGetDatum(righttype), + Int16GetDatum(strategy)); if (!HeapTupleIsValid(tp)) return InvalidOid; amop_tup = (Form_pg_amop) GETSTRUCT(tp); @@ -108,76 +175,633 @@ get_opclass_member(Oid opclass, int16 strategy) } /* - * get_op_hash_function - * Get the OID of the datatype-specific hash function associated with - * a hashable equality operator. + * get_ordering_op_properties + * Given the OID of an ordering operator (a btree "<" or ">" operator), + * determine its opfamily, its declared input datatype, and its + * strategy number (BTLessStrategyNumber or BTGreaterStrategyNumber). + * + * Returns TRUE if successful, FALSE if no matching pg_amop entry exists. + * (This indicates that the operator is not a valid ordering operator.) + * + * Note: the operator could be registered in multiple families, for example + * if someone were to build a "reverse sort" opfamily. This would result in + * uncertainty as to whether "ORDER BY USING op" would default to NULLS FIRST + * or NULLS LAST, as well as inefficient planning due to failure to match up + * pathkeys that should be the same. So we want a determinate result here. + * Because of the way the syscache search works, we'll use the interpretation + * associated with the opfamily with smallest OID, which is probably + * determinate enough. Since there is no longer any particularly good reason + * to build reverse-sort opfamilies, it doesn't seem worth expending any + * additional effort on ensuring consistency. + */ +bool +get_ordering_op_properties(Oid opno, + Oid *opfamily, Oid *opcintype, int16 *strategy) +{ + bool result = false; + CatCList *catlist; + int i; + + /* ensure outputs are initialized on failure */ + *opfamily = InvalidOid; + *opcintype = InvalidOid; + *strategy = 0; + + /* + * Search pg_amop to see if the target operator is registered as the "<" + * or ">" operator of any btree opfamily. + */ + catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno)); + + for (i = 0; i < catlist->n_members; i++) + { + HeapTuple tuple = &catlist->members[i]->tuple; + Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple); + + /* must be btree */ + if (aform->amopmethod != BTREE_AM_OID) + continue; + + if (aform->amopstrategy == BTLessStrategyNumber || + aform->amopstrategy == BTGreaterStrategyNumber) + { + /* Found it ... should have consistent input types */ + if (aform->amoplefttype == aform->amoprighttype) + { + /* Found a suitable opfamily, return info */ + *opfamily = aform->amopfamily; + *opcintype = aform->amoplefttype; + *strategy = aform->amopstrategy; + result = true; + break; + } + } + } + + ReleaseSysCacheList(catlist); + + return result; +} + +/* + * get_sort_function_for_ordering_op + * Get the OID of the datatype-specific btree sort support function, + * or if there is none, the btree comparison function, + * associated with an ordering operator (a "<" or ">" operator). + * + * *sortfunc receives the support or comparison function OID. + * *issupport is set TRUE if it's a support func, FALSE if a comparison func. + * *reverse is set FALSE if the operator is "<", TRUE if it's ">" + * (indicating that comparison results must be negated before use). + * + * Returns TRUE if successful, FALSE if no btree function can be found. + * (This indicates that the operator is not a valid ordering operator.) + */ +bool +get_sort_function_for_ordering_op(Oid opno, Oid *sortfunc, + bool *issupport, bool *reverse) +{ + Oid opfamily; + Oid opcintype; + int16 strategy; + + /* Find the operator in pg_amop */ + if (get_ordering_op_properties(opno, + &opfamily, &opcintype, &strategy)) + { + /* Found a suitable opfamily, get matching support function */ + *sortfunc = get_opfamily_proc(opfamily, + opcintype, + opcintype, + BTSORTSUPPORT_PROC); + if (OidIsValid(*sortfunc)) + *issupport = true; + else + { + /* opfamily doesn't provide sort support, get comparison func */ + *sortfunc = get_opfamily_proc(opfamily, + opcintype, + opcintype, + BTORDER_PROC); + if (!OidIsValid(*sortfunc)) /* should not happen */ + elog(ERROR, "missing support function %d(%u,%u) in opfamily %u", + BTORDER_PROC, opcintype, opcintype, opfamily); + *issupport = false; + } + *reverse = (strategy == BTGreaterStrategyNumber); + return true; + } + + /* ensure outputs are set on failure */ + *sortfunc = InvalidOid; + *issupport = false; + *reverse = false; + return false; +} + +/* + * get_equality_op_for_ordering_op + * Get the OID of the datatype-specific btree equality operator + * associated with an ordering operator (a "<" or ">" operator). + * + * If "reverse" isn't NULL, also set *reverse to FALSE if the operator is "<", + * TRUE if it's ">" * - * Returns InvalidOid if no hash function can be found. (This indicates - * that the operator should not have been marked oprcanhash.) + * Returns InvalidOid if no matching equality operator can be found. + * (This indicates that the operator is not a valid ordering operator.) */ Oid -get_op_hash_function(Oid opno) +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; - Oid opclass = InvalidOid; /* - * Search pg_amop to see if the target operator is registered as the - * "=" operator of any hash opclass. If the operator is registered in - * multiple opclasses, assume we can use the associated hash function - * from any one. + * Search pg_amop to see if the target operator is registered as the "=" + * operator of any btree opfamily. */ - catlist = SearchSysCacheList(AMOPOPID, 1, - ObjectIdGetDatum(opno), - 0, 0, 0); + catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno)); for (i = 0; i < catlist->n_members; i++) { HeapTuple tuple = &catlist->members[i]->tuple; Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple); - if (aform->amopstrategy == HTEqualStrategyNumber && - opclass_is_hash(aform->amopclaid)) + /* must be btree */ + if (aform->amopmethod != BTREE_AM_OID) + continue; + + if (aform->amopstrategy == BTEqualStrategyNumber) { - opclass = aform->amopclaid; - break; + /* Found a suitable opfamily, get matching ordering operator */ + Oid typid; + + typid = use_lhs_type ? aform->amoplefttype : aform->amoprighttype; + result = get_opfamily_member(aform->amopfamily, + typid, typid, + BTLessStrategyNumber); + if (OidIsValid(result)) + break; + /* failure probably shouldn't happen, but keep looking if so */ } } ReleaseSysCacheList(catlist); - if (OidIsValid(opclass)) + 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++) { - /* Found a suitable opclass, get its hash support function */ - return get_opclass_proc(opclass, HASHPROC); + 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); } - /* Didn't find a match... */ - return InvalidOid; + 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 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, + HASHPROC); + if (!OidIsValid(*lhs_procno)) + continue; + /* Matching LHS found, done if caller doesn't want RHS */ + if (!rhs_procno) + { + result = true; + break; + } + /* Only one lookup needed if given operator is single-type */ + if (aform->amoplefttype == aform->amoprighttype) + { + *rhs_procno = *lhs_procno; + result = true; + break; + } + } + if (rhs_procno) + { + *rhs_procno = get_opfamily_proc(aform->amopfamily, + aform->amoprighttype, + aform->amoprighttype, + HASHPROC); + if (!OidIsValid(*rhs_procno)) + { + /* Forget any LHS function from this opfamily */ + if (lhs_procno) + *lhs_procno = InvalidOid; + continue; + } + /* Matching RHS found, so done */ + result = true; + break; + } + } + } + + 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; } /* ---------- AMPROC CACHES ---------- */ /* - * get_opclass_proc + * get_opfamily_proc * Get the OID of the specified support function - * for the specified opclass. + * for the specified opfamily and datatypes. * * Returns InvalidOid if there is no pg_amproc entry for the given keys. */ Oid -get_opclass_proc(Oid opclass, int16 procnum) +get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, int16 procnum) { HeapTuple tp; Form_pg_amproc amproc_tup; RegProcedure result; - tp = SearchSysCache(AMPROCNUM, - ObjectIdGetDatum(opclass), - Int16GetDatum(procnum), - 0, 0); + tp = SearchSysCache4(AMPROCNUM, + ObjectIdGetDatum(opfamily), + ObjectIdGetDatum(lefttype), + ObjectIdGetDatum(righttype), + Int16GetDatum(procnum)); if (!HeapTupleIsValid(tp)) return InvalidOid; amproc_tup = (Form_pg_amproc) GETSTRUCT(tp); @@ -201,10 +825,9 @@ get_attname(Oid relid, AttrNumber attnum) { HeapTuple tp; - tp = SearchSysCache(ATTNUM, - ObjectIdGetDatum(relid), - Int16GetDatum(attnum), - 0, 0); + tp = SearchSysCache2(ATTNUM, + ObjectIdGetDatum(relid), + Int16GetDatum(attnum)); if (HeapTupleIsValid(tp)) { Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp); @@ -274,10 +897,9 @@ get_atttype(Oid relid, AttrNumber attnum) { HeapTuple tp; - tp = SearchSysCache(ATTNUM, - ObjectIdGetDatum(relid), - Int16GetDatum(attnum), - 0, 0); + tp = SearchSysCache2(ATTNUM, + ObjectIdGetDatum(relid), + Int16GetDatum(attnum)); if (HeapTupleIsValid(tp)) { Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp); @@ -302,10 +924,9 @@ get_atttypmod(Oid relid, AttrNumber attnum) { HeapTuple tp; - tp = SearchSysCache(ATTNUM, - ObjectIdGetDatum(relid), - Int16GetDatum(attnum), - 0, 0); + tp = SearchSysCache2(ATTNUM, + ObjectIdGetDatum(relid), + Int16GetDatum(attnum)); if (HeapTupleIsValid(tp)) { Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp); @@ -320,25 +941,24 @@ get_atttypmod(Oid relid, AttrNumber attnum) } /* - * get_atttypetypmod + * get_atttypetypmodcoll * - * A two-fer: given the relation id and the attribute number, - * fetch both type OID and atttypmod in a single cache lookup. + * A three-fer: given the relation id and the attribute number, + * fetch atttypid, atttypmod, and attcollation in a single cache lookup. * * Unlike the otherwise-similar get_atttype/get_atttypmod, this routine * raises an error if it can't obtain the information. */ void -get_atttypetypmod(Oid relid, AttrNumber attnum, - Oid *typid, int32 *typmod) +get_atttypetypmodcoll(Oid relid, AttrNumber attnum, + Oid *typid, int32 *typmod, Oid *collid) { HeapTuple tp; Form_pg_attribute att_tup; - tp = SearchSysCache(ATTNUM, - ObjectIdGetDatum(relid), - Int16GetDatum(attnum), - 0, 0); + tp = SearchSysCache2(ATTNUM, + ObjectIdGetDatum(relid), + Int16GetDatum(attnum)); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for attribute %d of relation %u", attnum, relid); @@ -346,62 +966,112 @@ get_atttypetypmod(Oid relid, AttrNumber attnum, *typid = att_tup->atttypid; *typmod = att_tup->atttypmod; + *collid = att_tup->attcollation; ReleaseSysCache(tp); } -/* ---------- INDEX CACHE ---------- */ +/* ---------- COLLATION CACHE ---------- */ -/* watch this space... +/* + * get_collation_name + * Returns the name of a given pg_collation entry. + * + * Returns a palloc'd copy of the string, or NULL if no such constraint. + * + * NOTE: since collation name is not unique, be wary of code that uses this + * for anything except preparing error messages. */ +char * +get_collation_name(Oid colloid) +{ + HeapTuple tp; + + tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(colloid)); + if (HeapTupleIsValid(tp)) + { + Form_pg_collation colltup = (Form_pg_collation) GETSTRUCT(tp); + char *result; + + result = pstrdup(NameStr(colltup->collname)); + ReleaseSysCache(tp); + return result; + } + else + return NULL; +} + +/* ---------- CONSTRAINT CACHE ---------- */ + +/* + * get_constraint_name + * Returns the name of a given pg_constraint entry. + * + * Returns a palloc'd copy of the string, or NULL if no such constraint. + * + * NOTE: since constraint name is not unique, be wary of code that uses this + * for anything except preparing error messages. + */ +char * +get_constraint_name(Oid conoid) +{ + HeapTuple tp; + + tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(conoid)); + if (HeapTupleIsValid(tp)) + { + Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp); + char *result; + + result = pstrdup(NameStr(contup->conname)); + ReleaseSysCache(tp); + return result; + } + else + return NULL; +} /* ---------- OPCLASS CACHE ---------- */ /* - * opclass_is_btree + * get_opclass_family * - * Returns TRUE iff the specified opclass is associated with the - * btree index access method. + * Returns the OID of the operator family the opclass belongs to. */ -bool -opclass_is_btree(Oid opclass) +Oid +get_opclass_family(Oid opclass) { HeapTuple tp; Form_pg_opclass cla_tup; - bool result; + Oid result; - tp = SearchSysCache(CLAOID, - ObjectIdGetDatum(opclass), - 0, 0, 0); + tp = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass)); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for opclass %u", opclass); cla_tup = (Form_pg_opclass) GETSTRUCT(tp); - result = (cla_tup->opcamid == BTREE_AM_OID); + result = cla_tup->opcfamily; ReleaseSysCache(tp); return result; } /* - * opclass_is_hash + * get_opclass_input_type * - * Returns TRUE iff the specified opclass is associated with the - * hash index access method. + * Returns the OID of the datatype the opclass indexes. */ -bool -opclass_is_hash(Oid opclass) +Oid +get_opclass_input_type(Oid opclass) { HeapTuple tp; Form_pg_opclass cla_tup; - bool result; + Oid result; - tp = SearchSysCache(CLAOID, - ObjectIdGetDatum(opclass), - 0, 0, 0); + tp = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass)); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for opclass %u", opclass); cla_tup = (Form_pg_opclass) GETSTRUCT(tp); - result = (cla_tup->opcamid == HASH_AM_OID); + result = cla_tup->opcintype; ReleaseSysCache(tp); return result; } @@ -419,9 +1089,7 @@ get_opcode(Oid opno) { HeapTuple tp; - tp = SearchSysCache(OPEROID, - ObjectIdGetDatum(opno), - 0, 0, 0); + tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno)); if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); @@ -446,9 +1114,7 @@ get_opname(Oid opno) { HeapTuple tp; - tp = SearchSysCache(OPEROID, - ObjectIdGetDatum(opno), - 0, 0, 0); + tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno)); if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); @@ -463,99 +1129,116 @@ get_opname(Oid opno) } /* - * op_mergejoinable + * op_input_types * - * Returns the left and right sort operators corresponding to a - * mergejoinable operator, or false if the operator is not mergejoinable. + * Returns the left and right input datatypes for an operator + * (InvalidOid if not relevant). */ -bool -op_mergejoinable(Oid opno, Oid *leftOp, Oid *rightOp) +void +op_input_types(Oid opno, Oid *lefttype, Oid *righttype) { HeapTuple tp; - bool result = false; - - tp = SearchSysCache(OPEROID, - ObjectIdGetDatum(opno), - 0, 0, 0); - if (HeapTupleIsValid(tp)) - { - Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); + Form_pg_operator optup; - if (optup->oprlsortop && - optup->oprrsortop) - { - *leftOp = optup->oprlsortop; - *rightOp = optup->oprrsortop; - result = true; - } - ReleaseSysCache(tp); - } - return result; + tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno)); + if (!HeapTupleIsValid(tp)) /* shouldn't happen */ + elog(ERROR, "cache lookup failed for operator %u", opno); + optup = (Form_pg_operator) GETSTRUCT(tp); + *lefttype = optup->oprleft; + *righttype = optup->oprright; + ReleaseSysCache(tp); } /* - * op_mergejoin_crossops + * op_mergejoinable + * + * Returns true if the operator is potentially mergejoinable. (The planner + * will fail to find any mergejoin plans unless there are suitable btree + * opfamily entries for this operator and associated sortops. The pg_operator + * flag is just a hint to tell the planner whether to bother looking.) * - * Returns the cross-type comparison operators (ltype "<" rtype and - * ltype ">" rtype) for an operator previously determined to be - * mergejoinable. Optionally, fetches the regproc ids of these - * operators, as well as their operator OIDs. + * In some cases (currently only array_eq and record_eq), mergejoinability + * depends on the specific input data type the operator is invoked for, so + * that must be passed as well. We currently assume that only one input's type + * is needed to check this --- by convention, pass the left input's data type. */ -void -op_mergejoin_crossops(Oid opno, Oid *ltop, Oid *gtop, - RegProcedure *ltproc, RegProcedure *gtproc) +bool +op_mergejoinable(Oid opno, Oid inputtype) { + bool result = false; HeapTuple tp; - Form_pg_operator optup; + TypeCacheEntry *typentry; /* - * Get the declared comparison operators of the operator. + * For array_eq or record_eq, we can sort if the element or field types + * are all sortable. We could implement all the checks for that here, but + * the typcache already does that and caches the results too, so let's + * rely on the typcache. */ - tp = SearchSysCache(OPEROID, - ObjectIdGetDatum(opno), - 0, 0, 0); - if (!HeapTupleIsValid(tp)) /* shouldn't happen */ - elog(ERROR, "cache lookup failed for operator %u", opno); - optup = (Form_pg_operator) GETSTRUCT(tp); - *ltop = optup->oprltcmpop; - *gtop = optup->oprgtcmpop; - ReleaseSysCache(tp); - - /* Check < op provided */ - if (!OidIsValid(*ltop)) - elog(ERROR, "mergejoin operator %u has no matching < operator", - opno); - if (ltproc) - *ltproc = get_opcode(*ltop); + if (opno == ARRAY_EQ_OP) + { + typentry = lookup_type_cache(inputtype, TYPECACHE_CMP_PROC); + if (typentry->cmp_proc == F_BTARRAYCMP) + result = true; + } + else if (opno == RECORD_EQ_OP) + { + typentry = lookup_type_cache(inputtype, TYPECACHE_CMP_PROC); + if (typentry->cmp_proc == F_BTRECORDCMP) + result = true; + } + else + { + /* For all other operators, rely on pg_operator.oprcanmerge */ + tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno)); + if (HeapTupleIsValid(tp)) + { + Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); - /* Check > op provided */ - if (!OidIsValid(*gtop)) - elog(ERROR, "mergejoin operator %u has no matching > operator", - opno); - if (gtproc) - *gtproc = get_opcode(*gtop); + result = optup->oprcanmerge; + ReleaseSysCache(tp); + } + } + return result; } /* * op_hashjoinable * - * Returns true if the operator is hashjoinable. + * Returns true if the operator is hashjoinable. (There must be a suitable + * hash opfamily entry for this operator if it is so marked.) + * + * In some cases (currently only array_eq), hashjoinability depends on the + * specific input data type the operator is invoked for, so that must be + * passed as well. We currently assume that only one input's type is needed + * to check this --- by convention, pass the left input's data type. */ bool -op_hashjoinable(Oid opno) +op_hashjoinable(Oid opno, Oid inputtype) { - HeapTuple tp; bool result = false; + HeapTuple tp; + TypeCacheEntry *typentry; - tp = SearchSysCache(OPEROID, - ObjectIdGetDatum(opno), - 0, 0, 0); - if (HeapTupleIsValid(tp)) + /* As in op_mergejoinable, let the typcache handle the hard cases */ + /* Eventually we'll need a similar case for record_eq ... */ + if (opno == ARRAY_EQ_OP) { - Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); + typentry = lookup_type_cache(inputtype, TYPECACHE_HASH_PROC); + if (typentry->hash_proc == F_HASH_ARRAY) + result = true; + } + else + { + /* For all other operators, rely on pg_operator.oprcanhash */ + tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno)); + if (HeapTupleIsValid(tp)) + { + Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); - result = optup->oprcanhash; - ReleaseSysCache(tp); + result = optup->oprcanhash; + ReleaseSysCache(tp); + } } return result; } @@ -602,9 +1285,7 @@ get_commutator(Oid opno) { HeapTuple tp; - tp = SearchSysCache(OPEROID, - ObjectIdGetDatum(opno), - 0, 0, 0); + tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno)); if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); @@ -628,9 +1309,7 @@ get_negator(Oid opno) { HeapTuple tp; - tp = SearchSysCache(OPEROID, - ObjectIdGetDatum(opno), - 0, 0, 0); + tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno)); if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); @@ -654,9 +1333,7 @@ get_oprrest(Oid opno) { HeapTuple tp; - tp = SearchSysCache(OPEROID, - ObjectIdGetDatum(opno), - 0, 0, 0); + tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno)); if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); @@ -680,9 +1357,7 @@ get_oprjoin(Oid opno) { HeapTuple tp; - tp = SearchSysCache(OPEROID, - ObjectIdGetDatum(opno), - 0, 0, 0); + tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno)); if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); @@ -709,9 +1384,7 @@ get_func_name(Oid funcid) { HeapTuple tp; - tp = SearchSysCache(PROCOID, - ObjectIdGetDatum(funcid), - 0, 0, 0); + tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); if (HeapTupleIsValid(tp)) { Form_pg_proc functup = (Form_pg_proc) GETSTRUCT(tp); @@ -725,6 +1398,30 @@ get_func_name(Oid funcid) 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. @@ -733,15 +1430,32 @@ Oid get_func_rettype(Oid funcid) { HeapTuple tp; - Oid result; + 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 = SearchSysCache(PROCOID, - ObjectIdGetDatum(funcid), - 0, 0, 0); + tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for function %u", funcid); - result = ((Form_pg_proc) GETSTRUCT(tp))->prorettype; + result = ((Form_pg_proc) GETSTRUCT(tp))->pronargs; ReleaseSysCache(tp); return result; } @@ -751,26 +1465,26 @@ get_func_rettype(Oid funcid) * Given procedure id, return the function's argument and result types. * (The return value is the result type.) * - * argtypes must point to a vector of size FUNC_MAX_ARGS. + * The arguments are returned as a palloc'd array. */ Oid -get_func_signature(Oid funcid, Oid *argtypes, int *nargs) +get_func_signature(Oid funcid, Oid **argtypes, int *nargs) { HeapTuple tp; Form_pg_proc procstruct; Oid result; - tp = SearchSysCache(PROCOID, - ObjectIdGetDatum(funcid), - 0, 0, 0); + 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; - memcpy(argtypes, procstruct->proargtypes, FUNC_MAX_ARGS * sizeof(Oid)); *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; @@ -786,9 +1500,7 @@ get_func_retset(Oid funcid) HeapTuple tp; bool result; - tp = SearchSysCache(PROCOID, - ObjectIdGetDatum(funcid), - 0, 0, 0); + tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for function %u", funcid); @@ -807,9 +1519,7 @@ func_strict(Oid funcid) HeapTuple tp; bool result; - tp = SearchSysCache(PROCOID, - ObjectIdGetDatum(funcid), - 0, 0, 0); + tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for function %u", funcid); @@ -828,9 +1538,7 @@ func_volatile(Oid funcid) HeapTuple tp; char result; - tp = SearchSysCache(PROCOID, - ObjectIdGetDatum(funcid), - 0, 0, 0); + tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for function %u", funcid); @@ -839,6 +1547,44 @@ func_volatile(Oid funcid) return result; } +/* + * get_func_cost + * Given procedure id, return the function's procost field. + */ +float4 +get_func_cost(Oid funcid) +{ + HeapTuple tp; + float4 result; + + tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for function %u", funcid); + + result = ((Form_pg_proc) GETSTRUCT(tp))->procost; + ReleaseSysCache(tp); + return result; +} + +/* + * get_func_rows + * Given procedure id, return the function's prorows field. + */ +float4 +get_func_rows(Oid funcid) +{ + HeapTuple tp; + float4 result; + + tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for function %u", funcid); + + result = ((Form_pg_proc) GETSTRUCT(tp))->prorows; + ReleaseSysCache(tp); + return result; +} + /* ---------- RELATION CACHE ---------- */ /* @@ -850,29 +1596,9 @@ func_volatile(Oid funcid) Oid get_relname_relid(const char *relname, Oid relnamespace) { - return GetSysCacheOid(RELNAMENSP, - PointerGetDatum(relname), - ObjectIdGetDatum(relnamespace), - 0, 0); -} - -/* - * get_system_catalog_relid - * Get the OID of a system catalog identified by name. - */ -Oid -get_system_catalog_relid(const char *catname) -{ - Oid relid; - - relid = GetSysCacheOid(RELNAMENSP, - PointerGetDatum(catname), - ObjectIdGetDatum(PG_CATALOG_NAMESPACE), - 0, 0); - if (!OidIsValid(relid)) - elog(ERROR, "cache lookup failed for system relation %s", catname); - - return relid; + return GetSysCacheOid2(RELNAMENSP, + PointerGetDatum(relname), + ObjectIdGetDatum(relnamespace)); } #ifdef NOT_USED @@ -886,9 +1612,7 @@ get_relnatts(Oid relid) { HeapTuple tp; - tp = SearchSysCache(RELOID, - ObjectIdGetDatum(relid), - 0, 0, 0); + tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); if (HeapTupleIsValid(tp)) { Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp); @@ -917,9 +1641,7 @@ get_rel_name(Oid relid) { HeapTuple tp; - tp = SearchSysCache(RELOID, - ObjectIdGetDatum(relid), - 0, 0, 0); + tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); if (HeapTupleIsValid(tp)) { Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp); @@ -943,9 +1665,7 @@ get_rel_namespace(Oid relid) { HeapTuple tp; - tp = SearchSysCache(RELOID, - ObjectIdGetDatum(relid), - 0, 0, 0); + tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); if (HeapTupleIsValid(tp)) { Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp); @@ -972,9 +1692,7 @@ get_rel_type_id(Oid relid) { HeapTuple tp; - tp = SearchSysCache(RELOID, - ObjectIdGetDatum(relid), - 0, 0, 0); + tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); if (HeapTupleIsValid(tp)) { Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp); @@ -998,9 +1716,7 @@ get_rel_relkind(Oid relid) { HeapTuple tp; - tp = SearchSysCache(RELOID, - ObjectIdGetDatum(relid), - 0, 0, 0); + tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); if (HeapTupleIsValid(tp)) { Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp); @@ -1014,6 +1730,33 @@ get_rel_relkind(Oid relid) return '\0'; } +/* + * get_rel_tablespace + * + * Returns the pg_tablespace OID associated with a given relation. + * + * Note: InvalidOid might mean either that we couldn't find the relation, + * or that it is in the database's default tablespace. + */ +Oid +get_rel_tablespace(Oid relid) +{ + HeapTuple tp; + + tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); + if (HeapTupleIsValid(tp)) + { + Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp); + Oid result; + + result = reltup->reltablespace; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; +} + /* ---------- TYPE CACHE ---------- */ @@ -1028,9 +1771,7 @@ get_typisdefined(Oid typid) { HeapTuple tp; - tp = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typid), - 0, 0, 0); + tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); if (HeapTupleIsValid(tp)) { Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); @@ -1054,9 +1795,7 @@ get_typlen(Oid typid) { HeapTuple tp; - tp = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typid), - 0, 0, 0); + tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); if (HeapTupleIsValid(tp)) { Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); @@ -1081,9 +1820,7 @@ get_typbyval(Oid typid) { HeapTuple tp; - tp = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typid), - 0, 0, 0); + tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); if (HeapTupleIsValid(tp)) { Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); @@ -1113,9 +1850,7 @@ get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval) HeapTuple tp; Form_pg_type typtup; - tp = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typid), - 0, 0, 0); + tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for type %u", typid); typtup = (Form_pg_type) GETSTRUCT(tp); @@ -1136,9 +1871,7 @@ get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, HeapTuple tp; Form_pg_type typtup; - tp = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typid), - 0, 0, 0); + tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for type %u", typid); typtup = (Form_pg_type) GETSTRUCT(tp); @@ -1148,11 +1881,41 @@ get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, ReleaseSysCache(tp); } +/* + * getTypeIOParam + * Given a pg_type row, select the type OID to pass to I/O functions + * + * Formerly, all I/O functions were passed pg_type.typelem as their second + * parameter, but we now have a more complex rule about what to pass. + * This knowledge is intended to be centralized here --- direct references + * to typelem elsewhere in the code are wrong, if they are associated with + * I/O calls and not with actual subscripting operations! (But see + * bootstrap.c's boot_get_type_io_data() if you need to change this.) + * + * As of PostgreSQL 8.1, output functions receive only the value itself + * and not any auxiliary parameters, so the name of this routine is now + * a bit of a misnomer ... it should be getTypeInputParam. + */ +Oid +getTypeIOParam(HeapTuple typeTuple) +{ + Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); + + /* + * Array types get their typelem as parameter; everybody else gets their + * own type OID as parameter. + */ + if (OidIsValid(typeStruct->typelem)) + return typeStruct->typelem; + else + return HeapTupleGetOid(typeTuple); +} + /* * get_type_io_data * * A six-fer: given the type OID, return typlen, typbyval, typalign, - * typdelim, typelem, and IO function OID. The IO function + * typdelim, typioparam, and IO function OID. The IO function * returned is controlled by IOFuncSelector */ void @@ -1162,15 +1925,45 @@ get_type_io_data(Oid typid, bool *typbyval, char *typalign, char *typdelim, - Oid *typelem, + Oid *typioparam, Oid *func) { HeapTuple typeTuple; Form_pg_type typeStruct; - typeTuple = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typid), - 0, 0, 0); + /* + * 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); @@ -1179,7 +1972,7 @@ get_type_io_data(Oid typid, *typbyval = typeStruct->typbyval; *typalign = typeStruct->typalign; *typdelim = typeStruct->typdelim; - *typelem = typeStruct->typelem; + *typioparam = getTypeIOParam(typeTuple); switch (which_func) { case IOFunc_input: @@ -1204,9 +1997,7 @@ get_typalign(Oid typid) { HeapTuple tp; - tp = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typid), - 0, 0, 0); + tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); if (HeapTupleIsValid(tp)) { Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); @@ -1226,9 +2017,7 @@ get_typstorage(Oid typid) { HeapTuple tp; - tp = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typid), - 0, 0, 0); + tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); if (HeapTupleIsValid(tp)) { Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); @@ -1242,33 +2031,6 @@ get_typstorage(Oid typid) return 'p'; } -/* - * get_typtypmod - * - * Given the type OID, return the typtypmod field (domain's typmod - * for base type) - */ -int32 -get_typtypmod(Oid typid) -{ - HeapTuple tp; - - tp = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typid), - 0, 0, 0); - if (HeapTupleIsValid(tp)) - { - Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); - int32 result; - - result = typtup->typtypmod; - ReleaseSysCache(tp); - return result; - } - else - return -1; -} - /* * get_typdefault * Given a type OID, return the type's default value, if any. @@ -1288,9 +2050,7 @@ get_typdefault(Oid typid) bool isNull; Node *expr; - typeTuple = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typid), - 0, 0, 0); + typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); if (!HeapTupleIsValid(typeTuple)) elog(ERROR, "cache lookup failed for type %u", typid); type = (Form_pg_type) GETSTRUCT(typeTuple); @@ -1308,8 +2068,7 @@ get_typdefault(Oid typid) if (!isNull) { /* We have an expression default */ - expr = stringToNode(DatumGetCString(DirectFunctionCall1(textout, - datum))); + expr = stringToNode(TextDatumGetCString(datum)); } else { @@ -1324,15 +2083,14 @@ get_typdefault(Oid typid) char *strDefaultVal; /* Convert text datum to C string */ - strDefaultVal = DatumGetCString(DirectFunctionCall1(textout, - datum)); + strDefaultVal = TextDatumGetCString(datum); /* Convert C string to a value of the given type */ - datum = OidFunctionCall3(type->typinput, - CStringGetDatum(strDefaultVal), - ObjectIdGetDatum(type->typelem), - Int32GetDatum(-1)); + datum = OidInputFunctionCall(type->typinput, strDefaultVal, + getTypeIOParam(typeTuple), -1); /* Build a Const node containing the value */ expr = (Node *) makeConst(typid, + -1, + type->typcollation, type->typlen, datum, false, @@ -1358,6 +2116,23 @@ get_typdefault(Oid typid) */ 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. @@ -1367,20 +2142,21 @@ getBaseType(Oid typid) HeapTuple tup; Form_pg_type typTup; - tup = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typid), - 0, 0, 0); + 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 != 'd') + if (typTup->typtype != TYPTYPE_DOMAIN) { /* Not a domain, so done */ ReleaseSysCache(tup); break; } + Assert(*typmod == -1); typid = typTup->typbasetype; + *typmod = typTup->typtypmod; + ReleaseSysCache(tup); } @@ -1416,8 +2192,8 @@ get_typavgwidth(Oid typid, int32 typmod) { /* * For BPCHAR, the max width is also the only width. Otherwise we - * need to guess about the typical data width given the max. A - * sliding scale for percentage of max width seems reasonable. + * need to guess about the typical data width given the max. A sliding + * scale for percentage of max width seems reasonable. */ if (typid == BPCHAROID) return maxwidth; @@ -1428,8 +2204,8 @@ get_typavgwidth(Oid typid, int32 typmod) /* * Beyond 1000, assume we're looking at something like - * "varchar(10000)" where the limit isn't actually reached often, - * and use a fixed estimate. + * "varchar(10000)" where the limit isn't actually reached often, and + * use a fixed estimate. */ return 32 + (1000 - 32) / 2; } @@ -1451,9 +2227,7 @@ get_typtype(Oid typid) { HeapTuple tp; - tp = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typid), - 0, 0, 0); + tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); if (HeapTupleIsValid(tp)) { Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); @@ -1467,6 +2241,59 @@ get_typtype(Oid typid) return '\0'; } +/* + * type_is_rowtype + * + * Convenience function to determine whether a type OID represents + * a "rowtype" type --- either RECORD or a named composite type. + */ +bool +type_is_rowtype(Oid typid) +{ + return (typid == RECORDOID || get_typtype(typid) == TYPTYPE_COMPOSITE); +} + +/* + * type_is_enum + * Returns true if the given type is an enum type. + */ +bool +type_is_enum(Oid typid) +{ + return (get_typtype(typid) == TYPTYPE_ENUM); +} + +/* + * type_is_range + * Returns true if the given type is a range type. + */ +bool +type_is_range(Oid typid) +{ + return (get_typtype(typid) == TYPTYPE_RANGE); +} + +/* + * get_type_category_preferred + * + * Given the type OID, fetch its category and preferred-type status. + * Throws error on failure. + */ +void +get_type_category_preferred(Oid typid, char *typcategory, bool *typispreferred) +{ + HeapTuple tp; + Form_pg_type typtup; + + tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for type %u", typid); + typtup = (Form_pg_type) GETSTRUCT(tp); + *typcategory = typtup->typcategory; + *typispreferred = typtup->typispreferred; + ReleaseSysCache(tp); +} + /* * get_typ_typrelid * @@ -1478,9 +2305,7 @@ get_typ_typrelid(Oid typid) { HeapTuple tp; - tp = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typid), - 0, 0, 0); + tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); if (HeapTupleIsValid(tp)) { Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); @@ -1507,9 +2332,7 @@ get_element_type(Oid typid) { HeapTuple tp; - tp = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typid), - 0, 0, 0); + tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); if (HeapTupleIsValid(tp)) { Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); @@ -1529,49 +2352,67 @@ get_element_type(Oid typid) /* * get_array_type * - * Given the type OID, get the corresponding array type. + * Given the type OID, get the corresponding "true" array type. * Returns InvalidOid if no array type can be found. - * - * NB: this only considers varlena arrays to be true arrays. */ Oid get_array_type(Oid typid) { HeapTuple tp; + Oid result = InvalidOid; - tp = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typid), - 0, 0, 0); + tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); if (HeapTupleIsValid(tp)) { - Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); - char *array_typename; - Oid namespaceId; - - array_typename = makeArrayTypeName(NameStr(typtup->typname)); - namespaceId = typtup->typnamespace; + result = ((Form_pg_type) GETSTRUCT(tp))->typarray; ReleaseSysCache(tp); + } + return result; +} - tp = SearchSysCache(TYPENAMENSP, - PointerGetDatum(array_typename), - ObjectIdGetDatum(namespaceId), - 0, 0); - - pfree(array_typename); +/* + * get_base_element_type + * Given the type OID, get the typelem, looking "through" any domain + * to its underlying array type. + * + * This is equivalent to get_element_type(getBaseType(typid)), but avoids + * an extra cache lookup. Note that it fails to provide any information + * about the typmod of the array. + */ +Oid +get_base_element_type(Oid typid) +{ + /* + * We loop to find the bottom base type in a stack of domains. + */ + for (;;) + { + HeapTuple tup; + Form_pg_type typTup; - if (HeapTupleIsValid(tp)) + tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); + if (!HeapTupleIsValid(tup)) + break; + typTup = (Form_pg_type) GETSTRUCT(tup); + if (typTup->typtype != TYPTYPE_DOMAIN) { + /* Not a domain, so stop descending */ Oid result; - typtup = (Form_pg_type) GETSTRUCT(tp); - if (typtup->typlen == -1 && typtup->typelem == typid) - result = HeapTupleGetOid(tp); + /* This test must match get_element_type */ + if (typTup->typlen == -1) + result = typTup->typelem; else result = InvalidOid; - ReleaseSysCache(tp); + ReleaseSysCache(tup); return result; } + + typid = typTup->typbasetype; + ReleaseSysCache(tup); } + + /* Like get_element_type, silently return InvalidOid for bogus input */ return InvalidOid; } @@ -1581,14 +2422,12 @@ get_array_type(Oid typid) * Get info needed for converting values of a type to internal form */ void -getTypeInputInfo(Oid type, Oid *typInput, Oid *typElem) +getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam) { HeapTuple typeTuple; Form_pg_type pt; - typeTuple = SearchSysCache(TYPEOID, - ObjectIdGetDatum(type), - 0, 0, 0); + typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type)); if (!HeapTupleIsValid(typeTuple)) elog(ERROR, "cache lookup failed for type %u", type); pt = (Form_pg_type) GETSTRUCT(typeTuple); @@ -1605,7 +2444,7 @@ getTypeInputInfo(Oid type, Oid *typInput, Oid *typElem) format_type_be(type)))); *typInput = pt->typinput; - *typElem = pt->typelem; + *typIOParam = getTypeIOParam(typeTuple); ReleaseSysCache(typeTuple); } @@ -1616,15 +2455,12 @@ getTypeInputInfo(Oid type, Oid *typInput, Oid *typElem) * Get info needed for printing values of a type */ void -getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem, - bool *typIsVarlena) +getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena) { HeapTuple typeTuple; Form_pg_type pt; - typeTuple = SearchSysCache(TYPEOID, - ObjectIdGetDatum(type), - 0, 0, 0); + typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type)); if (!HeapTupleIsValid(typeTuple)) elog(ERROR, "cache lookup failed for type %u", type); pt = (Form_pg_type) GETSTRUCT(typeTuple); @@ -1641,7 +2477,6 @@ getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem, format_type_be(type)))); *typOutput = pt->typoutput; - *typElem = pt->typelem; *typIsVarlena = (!pt->typbyval) && (pt->typlen == -1); ReleaseSysCache(typeTuple); @@ -1653,14 +2488,12 @@ getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem, * Get info needed for binary input of values of a type */ void -getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typElem) +getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typIOParam) { HeapTuple typeTuple; Form_pg_type pt; - typeTuple = SearchSysCache(TYPEOID, - ObjectIdGetDatum(type), - 0, 0, 0); + typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type)); if (!HeapTupleIsValid(typeTuple)) elog(ERROR, "cache lookup failed for type %u", type); pt = (Form_pg_type) GETSTRUCT(typeTuple); @@ -1677,7 +2510,7 @@ getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typElem) format_type_be(type)))); *typReceive = pt->typreceive; - *typElem = pt->typelem; + *typIOParam = getTypeIOParam(typeTuple); ReleaseSysCache(typeTuple); } @@ -1688,15 +2521,12 @@ getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typElem) * Get info needed for binary output of values of a type */ void -getTypeBinaryOutputInfo(Oid type, Oid *typSend, Oid *typElem, - bool *typIsVarlena) +getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena) { HeapTuple typeTuple; Form_pg_type pt; - typeTuple = SearchSysCache(TYPEOID, - ObjectIdGetDatum(type), - 0, 0, 0); + typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type)); if (!HeapTupleIsValid(typeTuple)) elog(ERROR, "cache lookup failed for type %u", type); pt = (Form_pg_type) GETSTRUCT(typeTuple); @@ -1713,12 +2543,97 @@ getTypeBinaryOutputInfo(Oid type, Oid *typSend, Oid *typElem, format_type_be(type)))); *typSend = pt->typsend; - *typElem = pt->typelem; *typIsVarlena = (!pt->typbyval) && (pt->typlen == -1); ReleaseSysCache(typeTuple); } +/* + * get_typmodin + * + * Given the type OID, return the type's typmodin procedure, if any. + */ +Oid +get_typmodin(Oid typid) +{ + HeapTuple tp; + + tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); + if (HeapTupleIsValid(tp)) + { + Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); + Oid result; + + result = typtup->typmodin; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; +} + +#ifdef NOT_USED +/* + * get_typmodout + * + * Given the type OID, return the type's typmodout procedure, if any. + */ +Oid +get_typmodout(Oid typid) +{ + HeapTuple tp; + + tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); + if (HeapTupleIsValid(tp)) + { + Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); + Oid result; + + result = typtup->typmodout; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; +} +#endif /* NOT_USED */ + +/* + * get_typcollation + * + * Given the type OID, return the type's typcollation attribute. + */ +Oid +get_typcollation(Oid typid) +{ + HeapTuple tp; + + tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); + if (HeapTupleIsValid(tp)) + { + Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); + Oid result; + + result = typtup->typcollation; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; +} + + +/* + * type_is_collatable + * + * Return whether the type cares about collations + */ +bool +type_is_collatable(Oid typid) +{ + return OidIsValid(get_typcollation(typid)); +} + /* ---------- STATISTICS CACHE ---------- */ @@ -1727,20 +2642,33 @@ getTypeBinaryOutputInfo(Oid type, Oid *typSend, Oid *typElem, * * Given the table and attribute number of a column, get the average * width of entries in the column. Return zero if no data available. + * + * Currently this is only consulted for individual tables, not for inheritance + * trees, so we don't need an "inh" parameter. + * + * Calling a hook at this point looks somewhat strange, but is required + * because the optimizer calls this function without any other way for + * plug-ins to control the result. */ int32 get_attavgwidth(Oid relid, AttrNumber attnum) { HeapTuple tp; + int32 stawidth; - tp = SearchSysCache(STATRELATT, - ObjectIdGetDatum(relid), - Int16GetDatum(attnum), - 0, 0); + if (get_attavgwidth_hook) + { + stawidth = (*get_attavgwidth_hook) (relid, attnum); + if (stawidth > 0) + return stawidth; + } + tp = SearchSysCache3(STATRELATTINH, + ObjectIdGetDatum(relid), + Int16GetDatum(attnum), + BoolGetDatum(false)); if (HeapTupleIsValid(tp)) { - int32 stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth; - + stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth; ReleaseSysCache(tp); if (stawidth > 0) return stawidth; @@ -1758,12 +2686,16 @@ get_attavgwidth(Oid relid, AttrNumber attnum) * already-looked-up tuple in the pg_statistic cache. We do this since * most callers will want to extract more than one value from the cache * entry, and we don't want to repeat the cache lookup unnecessarily. + * Also, this API allows this routine to be used with statistics tuples + * that have been provided by a stats hook and didn't really come from + * pg_statistic. * * statstuple: pg_statistics tuple to be examined. - * atttype: type OID of attribute. - * atttypmod: typmod of attribute. + * atttype: type OID of attribute (can be InvalidOid if values == NULL). + * atttypmod: typmod of attribute (can be 0 if values == NULL). * reqkind: STAKIND code for desired statistics slot kind. * reqop: STAOP value wanted, or InvalidOid if don't care. + * actualop: if not NULL, *actualop receives the actual STAOP value. * values, nvalues: if not NULL, the slot's stavalues are extracted. * numbers, nnumbers: if not NULL, the slot's stanumbers are extracted. * @@ -1771,11 +2703,16 @@ get_attavgwidth(Oid relid, AttrNumber attnum) * If the attribute type is pass-by-reference, the values referenced by * the values array are themselves palloc'd. The palloc'd stuff can be * freed by calling free_attstatsslot. + * + * Note: at present, atttype/atttypmod aren't actually used here at all. + * But the caller must have the correct (or at least binary-compatible) + * type ID to pass to free_attstatsslot later. */ bool get_attstatsslot(HeapTuple statstuple, Oid atttype, int32 atttypmod, int reqkind, Oid reqop, + Oid *actualop, Datum **values, int *nvalues, float4 **numbers, int *nnumbers) { @@ -1785,6 +2722,7 @@ get_attstatsslot(HeapTuple statstuple, Datum val; bool isnull; ArrayType *statarray; + Oid arrayelemtype; int narrayelem; HeapTuple typeTuple; Form_pg_type typeForm; @@ -1798,35 +2736,43 @@ get_attstatsslot(HeapTuple statstuple, if (i >= STATISTIC_NUM_SLOTS) return false; /* not there */ + if (actualop) + *actualop = (&stats->staop1)[i]; + if (values) { - val = SysCacheGetAttr(STATRELATT, statstuple, + val = SysCacheGetAttr(STATRELATTINH, statstuple, Anum_pg_statistic_stavalues1 + i, &isnull); if (isnull) elog(ERROR, "stavalues is null"); statarray = DatumGetArrayTypeP(val); - /* Need to get info about the array element type */ - typeTuple = SearchSysCache(TYPEOID, - ObjectIdGetDatum(atttype), - 0, 0, 0); + /* + * Need to get info about the array element type. We look at the + * actual element type embedded in the array, which might be only + * binary-compatible with the passed-in atttype. The info we extract + * here should be the same either way, but deconstruct_array is picky + * about having an exact type OID match. + */ + arrayelemtype = ARR_ELEMTYPE(statarray); + typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(arrayelemtype)); if (!HeapTupleIsValid(typeTuple)) - elog(ERROR, "cache lookup failed for type %u", atttype); + elog(ERROR, "cache lookup failed for type %u", arrayelemtype); typeForm = (Form_pg_type) GETSTRUCT(typeTuple); - /* Deconstruct array into Datum elements */ + /* Deconstruct array into Datum elements; NULLs not expected */ deconstruct_array(statarray, - atttype, + arrayelemtype, typeForm->typlen, typeForm->typbyval, typeForm->typalign, - values, nvalues); + values, NULL, nvalues); /* - * If the element type is pass-by-reference, we now have a bunch - * of Datums that are pointers into the syscache value. Copy them - * to avoid problems if syscache decides to drop the entry. + * If the element type is pass-by-reference, we now have a bunch of + * Datums that are pointers into the syscache value. Copy them to + * avoid problems if syscache decides to drop the entry. */ if (!typeForm->typbyval) { @@ -1849,7 +2795,7 @@ get_attstatsslot(HeapTuple statstuple, if (numbers) { - val = SysCacheGetAttr(STATRELATT, statstuple, + val = SysCacheGetAttr(STATRELATTINH, statstuple, Anum_pg_statistic_stanumbers1 + i, &isnull); if (isnull) @@ -1857,12 +2803,13 @@ get_attstatsslot(HeapTuple statstuple, statarray = DatumGetArrayTypeP(val); /* - * We expect the array to be a 1-D float4 array; verify that. We - * don't need to use deconstruct_array() since the array data is - * just going to look like a C array of float4 values. + * We expect the array to be a 1-D float4 array; verify that. We don't + * need to use deconstruct_array() since the array data is just going + * to look like a C array of float4 values. */ narrayelem = ARR_DIMS(statarray)[0]; if (ARR_NDIM(statarray) != 1 || narrayelem <= 0 || + ARR_HASNULL(statarray) || ARR_ELEMTYPE(statarray) != FLOAT4OID) elog(ERROR, "stanumbers is not a 1-D float4 array"); *numbers = (float4 *) palloc(narrayelem * sizeof(float4)); @@ -1879,6 +2826,12 @@ get_attstatsslot(HeapTuple statstuple, return true; } +/* + * free_attstatsslot + * Free data allocated by get_attstatsslot + * + * atttype need be valid only if values != NULL. + */ void free_attstatsslot(Oid atttype, Datum *values, int nvalues, @@ -1912,9 +2865,7 @@ get_namespace_name(Oid nspid) { HeapTuple tp; - tp = SearchSysCache(NAMESPACEOID, - ObjectIdGetDatum(nspid), - 0, 0, 0); + tp = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(nspid)); if (HeapTupleIsValid(tp)) { Form_pg_namespace nsptup = (Form_pg_namespace) GETSTRUCT(tp); @@ -1928,36 +2879,29 @@ get_namespace_name(Oid nspid) return NULL; } -/* ---------- PG_SHADOW CACHE ---------- */ +/* ---------- PG_RANGE CACHE ---------- */ /* - * get_usesysid - * - * Given a user name, look up the user's sysid. - * Raises an error if no such user (rather than returning zero, - * which might possibly be a valid usesysid). + * get_range_subtype + * Returns the subtype of a given range type * - * Note: the type of usesysid is currently int4, but may change to Oid - * someday. It'd be reasonable to return zero on failure if we were - * using Oid ... + * Returns InvalidOid if the type is not a range type. */ -AclId -get_usesysid(const char *username) +Oid +get_range_subtype(Oid rangeOid) { - int32 result; - HeapTuple userTup; - - userTup = SearchSysCache(SHADOWNAME, - PointerGetDatum(username), - 0, 0, 0); - if (!HeapTupleIsValid(userTup)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("user \"%s\" does not exist", username))); - - result = ((Form_pg_shadow) GETSTRUCT(userTup))->usesysid; + HeapTuple tp; - ReleaseSysCache(userTup); + tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid)); + if (HeapTupleIsValid(tp)) + { + Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp); + Oid result; - return result; + result = rngtup->rngsubtype; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; }