X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Futils%2Fcache%2Flsyscache.c;h=7a4306e93f5545764cbd02e6aa042120b385790c;hb=c6e3ac11b60ac4a8942ab964252d51c1c0bd8845;hp=8988d2e10ff7389ec4bee2ada833ec319992f5a1;hpb=f41803bb39bc2949db200116a609fd242d0ec221;p=postgresql diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 8988d2e10f..7a4306e93f 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -3,11 +3,11 @@ * lsyscache.c * Convenience routines for common queries in the system catalog cache. * - * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.144 2007/01/20 20:45:40 tgl Exp $ + * src/backend/utils/cache/lsyscache.c * * NOTES * Eventually, the index information should go through here, too. @@ -20,10 +20,13 @@ #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_range.h" #include "catalog/pg_statistic.h" #include "catalog/pg_type.h" #include "miscadmin.h" @@ -31,8 +34,14 @@ #include "utils/array.h" #include "utils/builtins.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 ---------- */ @@ -41,14 +50,16 @@ * 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 SearchSysCacheExists(AMOPOPID, - ObjectIdGetDatum(opno), - ObjectIdGetDatum(opfamily), - 0, 0); + return SearchSysCacheExists3(AMOPOPID, + ObjectIdGetDatum(opno), + CharGetDatum(AMOP_SEARCH), + ObjectIdGetDatum(opfamily)); } /* @@ -56,6 +67,8 @@ op_in_opfamily(Oid opno, Oid opfamily) * * 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) @@ -64,10 +77,10 @@ get_op_opfamily_strategy(Oid opno, Oid opfamily) Form_pg_amop amop_tup; int result; - tp = SearchSysCache(AMOPOPID, - ObjectIdGetDatum(opno), - ObjectIdGetDatum(opfamily), - 0, 0); + tp = SearchSysCache3(AMOPOPID, + ObjectIdGetDatum(opno), + CharGetDatum(AMOP_SEARCH), + ObjectIdGetDatum(opfamily)); if (!HeapTupleIsValid(tp)) return 0; amop_tup = (Form_pg_amop) GETSTRUCT(tp); @@ -76,29 +89,53 @@ get_op_opfamily_strategy(Oid opno, Oid opfamily) 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, input types, and recheck (lossy) - * flag within the specified opfamily. + * 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, +get_op_opfamily_properties(Oid opno, Oid opfamily, bool ordering_op, int *strategy, Oid *lefttype, - Oid *righttype, - bool *recheck) + Oid *righttype) { HeapTuple tp; Form_pg_amop amop_tup; - tp = SearchSysCache(AMOPOPID, - ObjectIdGetDatum(opno), - ObjectIdGetDatum(opfamily), - 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 opfamily %u", opno, opfamily); @@ -106,7 +143,6 @@ get_op_opfamily_properties(Oid opno, Oid opfamily, *strategy = amop_tup->amopstrategy; *lefttype = amop_tup->amoplefttype; *righttype = amop_tup->amoprighttype; - *recheck = amop_tup->amopreqcheck; ReleaseSysCache(tp); } @@ -125,11 +161,11 @@ get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, Form_pg_amop amop_tup; Oid result; - tp = SearchSysCache(AMOPSTRATEGY, - ObjectIdGetDatum(opfamily), - ObjectIdGetDatum(lefttype), - ObjectIdGetDatum(righttype), - Int16GetDatum(strategy)); + tp = SearchSysCache4(AMOPSTRATEGY, + ObjectIdGetDatum(opfamily), + ObjectIdGetDatum(lefttype), + ObjectIdGetDatum(righttype), + Int16GetDatum(strategy)); if (!HeapTupleIsValid(tp)) return InvalidOid; amop_tup = (Form_pg_amop) GETSTRUCT(tp); @@ -139,44 +175,43 @@ get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, } /* - * get_compare_function_for_ordering_op - * Get the OID of the datatype-specific btree comparison function - * associated with an ordering operator (a "<" or ">" operator). - * - * *cmpfunc receives the comparison function OID. - * *reverse is set FALSE if the operator is "<", TRUE if it's ">" - * (indicating the comparison result must be negated before use). + * 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 btree function can be found. + * 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_compare_function_for_ordering_op(Oid opno, Oid *cmpfunc, bool *reverse) +get_ordering_op_properties(Oid opno, + Oid *opfamily, Oid *opcintype, int16 *strategy) { bool result = false; CatCList *catlist; int i; - /* ensure outputs are set on failure */ - *cmpfunc = InvalidOid; - *reverse = false; + /* 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. It's possible that it might be - * registered both ways (if someone were to build a "reverse sort" - * opfamily); assume we can use either interpretation. (Note: the - * existence of a reverse-sort opfamily would result in uncertainty as - * to whether "ORDER BY USING op" would default to NULLS FIRST or NULLS - * LAST. Since there is no longer any particularly good reason to build - * reverse-sort opfamilies, we don't bother expending any extra work to - * make this more determinate. In practice, 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.) + * or ">" 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++) { @@ -190,18 +225,16 @@ get_compare_function_for_ordering_op(Oid opno, Oid *cmpfunc, bool *reverse) if (aform->amopstrategy == BTLessStrategyNumber || aform->amopstrategy == BTGreaterStrategyNumber) { - /* Found a suitable opfamily, get matching support function */ - *reverse = (aform->amopstrategy == BTGreaterStrategyNumber); - *cmpfunc = get_opfamily_proc(aform->amopfamily, - aform->amoplefttype, - aform->amoprighttype, - BTORDER_PROC); - if (!OidIsValid(*cmpfunc)) /* should not happen */ - elog(ERROR, "missing support function %d(%u,%u) in opfamily %u", - BTORDER_PROC, aform->amoplefttype, aform->amoprighttype, - aform->amopfamily); - result = true; - break; + /* 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; + } } } @@ -210,57 +243,94 @@ get_compare_function_for_ordering_op(Oid opno, Oid *cmpfunc, bool *reverse) 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 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) +get_equality_op_for_ordering_op(Oid opno, bool *reverse) { Oid result = InvalidOid; - CatCList *catlist; - int i; - - /* - * Search pg_amop to see if the target operator is registered as the "<" - * or ">" operator of any btree opfamily. This is exactly like - * get_compare_function_for_ordering_op except we don't care whether the - * ordering op is "<" or ">" ... the equality operator will be the same - * either way. - */ - catlist = SearchSysCacheList(AMOPOPID, 1, - ObjectIdGetDatum(opno), - 0, 0, 0); + Oid opfamily; + Oid opcintype; + int16 strategy; - for (i = 0; i < catlist->n_members; i++) + /* Find the operator in pg_amop */ + if (get_ordering_op_properties(opno, + &opfamily, &opcintype, &strategy)) { - 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 a suitable opfamily, get matching equality operator */ - result = get_opfamily_member(aform->amopfamily, - aform->amoplefttype, - aform->amoprighttype, - BTEqualStrategyNumber); - if (OidIsValid(result)) - break; - /* failure probably shouldn't happen, but keep looking if so */ - } + /* Found a suitable opfamily, get matching equality operator */ + result = get_opfamily_member(opfamily, + opcintype, + opcintype, + BTEqualStrategyNumber); + if (reverse) + *reverse = (strategy == BTGreaterStrategyNumber); } - ReleaseSysCacheList(catlist); - return result; } @@ -289,9 +359,7 @@ get_ordering_op_for_equality_op(Oid opno, bool use_lhs_type) * 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++) { @@ -305,7 +373,7 @@ get_ordering_op_for_equality_op(Oid opno, bool use_lhs_type) if (aform->amopstrategy == BTEqualStrategyNumber) { /* Found a suitable opfamily, get matching ordering operator */ - Oid typid; + Oid typid; typid = use_lhs_type ? aform->amoplefttype : aform->amoprighttype; result = get_opfamily_member(aform->amopfamily, @@ -333,7 +401,7 @@ get_ordering_op_for_equality_op(Oid opno, bool use_lhs_type) * * 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 + * 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 @@ -352,9 +420,7 @@ get_mergejoin_opfamilies(Oid opno) * 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++) { @@ -373,31 +439,40 @@ get_mergejoin_opfamilies(Oid opno) } /* - * get_compatible_hash_operator - * Get the OID of a hash equality operator compatible with the given - * operator, but operating on its LHS or RHS datatype as specified. + * 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. * - * If the given operator is not cross-type, the result should be the same - * operator, but in cross-type situations it is different. + * 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. * - * Returns InvalidOid if no compatible operator can be found. (This indicates - * that the operator should not have been marked oprcanhash.) + * 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.) */ -Oid -get_compatible_hash_operator(Oid opno, bool use_lhs_type) +bool +get_compatible_hash_operators(Oid opno, + Oid *lhs_opno, Oid *rhs_opno) { - Oid result = InvalidOid; + 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 = SearchSysCacheList(AMOPOPID, 1, - ObjectIdGetDatum(opno), - 0, 0, 0); + catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno)); for (i = 0; i < catlist->n_members; i++) { @@ -407,22 +482,54 @@ get_compatible_hash_operator(Oid opno, bool use_lhs_type) if (aform->amopmethod == HASH_AM_OID && aform->amopstrategy == HTEqualStrategyNumber) { - /* Found a suitable opfamily, get matching single-type operator */ - Oid typid; - /* No extra lookup needed if given operator is single-type */ if (aform->amoplefttype == aform->amoprighttype) { - result = opno; + if (lhs_opno) + *lhs_opno = opno; + if (rhs_opno) + *rhs_opno = opno; + result = true; break; } - typid = use_lhs_type ? aform->amoplefttype : aform->amoprighttype; - result = get_opfamily_member(aform->amopfamily, - typid, typid, - HTEqualStrategyNumber); - if (OidIsValid(result)) + + /* + * 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; - /* failure probably shouldn't happen, but keep looking if so */ + } } } @@ -432,32 +539,40 @@ get_compatible_hash_operator(Oid opno, bool use_lhs_type) } /* - * get_op_hash_function - * Get the OID of the datatype-specific hash function associated with - * a hashable equality operator. + * 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. * - * XXX API needs to be generalized for the case of different left and right - * datatypes. + * 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. * - * Returns InvalidOid if no hash function can be found. (This indicates - * that the operator should not have been marked oprcanhash.) + * 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.) */ -Oid -get_op_hash_function(Oid opno) +bool +get_op_hash_functions(Oid opno, + RegProcedure *lhs_procno, RegProcedure *rhs_procno) { - Oid result = InvalidOid; + 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 the associated hash function from - * any one. + * multiple opfamilies, assume we can use any one. */ - catlist = SearchSysCacheList(AMOPOPID, 1, - ObjectIdGetDatum(opno), - 0, 0, 0); + catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno)); for (i = 0; i < catlist->n_members; i++) { @@ -467,12 +582,50 @@ get_op_hash_function(Oid opno) if (aform->amopmethod == HASH_AM_OID && aform->amopstrategy == HTEqualStrategyNumber) { - /* Found a suitable opfamily, get matching hash support function */ - result = get_opfamily_proc(aform->amopfamily, - aform->amoplefttype, - aform->amoprighttype, - HASHPROC); - break; + /* + * 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; + } } } @@ -484,55 +637,30 @@ get_op_hash_function(Oid opno) /* * get_op_btree_interpretation * Given an operator's OID, find out which btree opfamilies it belongs to, - * and what strategy number it has within each one. The results are - * returned as an OID list and a parallel integer list. + * 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. */ -void -get_op_btree_interpretation(Oid opno, List **opfamilies, List **opstrats) +List * +get_op_btree_interpretation(Oid opno) { + List *result = NIL; + OpBtreeInterpretation *thisresult; CatCList *catlist; - bool op_negated; int i; - *opfamilies = NIL; - *opstrats = NIL; - /* * Find all the pg_amop entries containing the operator. */ - catlist = SearchSysCacheList(AMOPOPID, 1, - ObjectIdGetDatum(opno), - 0, 0, 0); + catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno)); - /* - * If we can't find any opfamily containing the op, perhaps it is a <> - * operator. See if it has a negator that is in an opfamily. - */ - op_negated = false; - if (catlist->n_members == 0) - { - Oid op_negator = get_negator(opno); - - if (OidIsValid(op_negator)) - { - op_negated = true; - ReleaseSysCacheList(catlist); - catlist = SearchSysCacheList(AMOPOPID, 1, - ObjectIdGetDatum(op_negator), - 0, 0, 0); - } - } - - /* Now search the opfamilies */ 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); - Oid opfamily_id; StrategyNumber op_strategy; /* must be btree */ @@ -540,56 +668,110 @@ get_op_btree_interpretation(Oid opno, List **opfamilies, List **opstrats) continue; /* Get the operator's btree strategy number */ - opfamily_id = op_form->amopfamily; op_strategy = (StrategyNumber) op_form->amopstrategy; Assert(op_strategy >= 1 && op_strategy <= 5); - if (op_negated) + 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)) { - /* Only consider negators that are = */ - if (op_strategy != BTEqualStrategyNumber) - continue; - op_strategy = ROWCOMPARE_NE; - } + 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); + } - *opfamilies = lappend_oid(*opfamilies, opfamily_id); - *opstrats = lappend_int(*opstrats, op_strategy); + ReleaseSysCacheList(catlist); + } } - ReleaseSysCacheList(catlist); + return result; } /* - * ops_in_same_btree_opfamily - * Return TRUE if there exists a btree opfamily containing both operators. - * (This implies that they have compatible notions of equality.) + * 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 -ops_in_same_btree_opfamily(Oid opno1, Oid opno2) +equality_ops_are_compatible(Oid opno1, Oid opno2) { - bool result = false; + 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 = SearchSysCacheList(AMOPOPID, 1, - ObjectIdGetDatum(opno1), - 0, 0, 0); + 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 */ - if (op_form->amopmethod != BTREE_AM_OID) - continue; - - if (op_in_opfamily(opno2, op_form->amopfamily)) + /* must be btree or hash */ + if (op_form->amopmethod == BTREE_AM_OID || + op_form->amopmethod == HASH_AM_OID) { - result = true; - break; + if (op_in_opfamily(opno2, op_form->amopfamily)) + { + result = true; + break; + } } } @@ -615,11 +797,11 @@ get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, int16 procnum) Form_pg_amproc amproc_tup; RegProcedure result; - tp = SearchSysCache(AMPROCNUM, - ObjectIdGetDatum(opfamily), - ObjectIdGetDatum(lefttype), - ObjectIdGetDatum(righttype), - Int16GetDatum(procnum)); + tp = SearchSysCache4(AMPROCNUM, + ObjectIdGetDatum(opfamily), + ObjectIdGetDatum(lefttype), + ObjectIdGetDatum(righttype), + Int16GetDatum(procnum)); if (!HeapTupleIsValid(tp)) return InvalidOid; amproc_tup = (Form_pg_amproc) GETSTRUCT(tp); @@ -643,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); @@ -716,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); @@ -744,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); @@ -762,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); @@ -788,13 +966,69 @@ 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 ---------- */ @@ -810,9 +1044,7 @@ get_opclass_family(Oid opclass) Form_pg_opclass cla_tup; 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); @@ -834,9 +1066,7 @@ get_opclass_input_type(Oid opclass) Form_pg_opclass cla_tup; 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); @@ -859,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); @@ -886,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); @@ -914,9 +1140,7 @@ op_input_types(Oid opno, Oid *lefttype, Oid *righttype) HeapTuple tp; Form_pg_operator optup; - tp = SearchSysCache(OPEROID, - ObjectIdGetDatum(opno), - 0, 0, 0); + 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); @@ -932,22 +1156,48 @@ op_input_types(Oid opno, Oid *lefttype, Oid *righttype) * 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) +op_mergejoinable(Oid opno, Oid inputtype) { - HeapTuple tp; bool result = false; + HeapTuple tp; + TypeCacheEntry *typentry; - tp = SearchSysCache(OPEROID, - ObjectIdGetDatum(opno), - 0, 0, 0); - if (HeapTupleIsValid(tp)) + /* + * 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) { - Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); + 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); + result = optup->oprcanmerge; + ReleaseSysCache(tp); + } } return result; } @@ -957,22 +1207,38 @@ op_mergejoinable(Oid opno) * * 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; } @@ -1019,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); @@ -1045,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); @@ -1071,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); @@ -1097,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); @@ -1126,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); @@ -1142,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. @@ -1152,9 +1432,7 @@ get_func_rettype(Oid funcid) HeapTuple tp; 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); @@ -1173,9 +1451,7 @@ 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); @@ -1198,9 +1474,7 @@ get_func_signature(Oid funcid, Oid **argtypes, int *nargs) 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); @@ -1226,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); @@ -1247,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); @@ -1268,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); @@ -1279,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 ---------- */ /* @@ -1290,10 +1596,9 @@ func_volatile(Oid funcid) Oid get_relname_relid(const char *relname, Oid relnamespace) { - return GetSysCacheOid(RELNAMENSP, - PointerGetDatum(relname), - ObjectIdGetDatum(relnamespace), - 0, 0); + return GetSysCacheOid2(RELNAMENSP, + PointerGetDatum(relname), + ObjectIdGetDatum(relnamespace)); } #ifdef NOT_USED @@ -1307,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); @@ -1338,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); @@ -1364,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); @@ -1393,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); @@ -1419,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); @@ -1435,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 ---------- */ @@ -1449,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); @@ -1475,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); @@ -1502,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); @@ -1534,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); @@ -1557,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); @@ -1591,8 +1903,7 @@ getTypeIOParam(HeapTuple typeTuple) /* * Array types get their typelem as parameter; everybody else gets their - * own type OID as parameter. (This is a change from 8.0, in which only - * composite types got their own OID as parameter.) + * own type OID as parameter. */ if (OidIsValid(typeStruct->typelem)) return typeStruct->typelem; @@ -1652,9 +1963,7 @@ get_type_io_data(Oid typid, return; } - 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); typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); @@ -1688,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); @@ -1710,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); @@ -1745,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); @@ -1765,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 { @@ -1781,13 +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 = 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, @@ -1839,13 +2142,11 @@ getBaseTypeAndTypmod(Oid typid, int32 *typmod) 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); @@ -1926,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); @@ -1951,7 +2250,48 @@ get_typtype(Oid typid) bool type_is_rowtype(Oid typid) { - return (typid == RECORDOID || get_typtype(typid) == 'c'); + 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); } /* @@ -1965,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); @@ -1994,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); @@ -2016,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; } @@ -2073,9 +2427,7 @@ 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); @@ -2108,9 +2460,7 @@ 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); @@ -2143,9 +2493,7 @@ 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); @@ -2178,9 +2526,7 @@ 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); @@ -2212,9 +2558,7 @@ get_typmodin(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); @@ -2239,9 +2583,7 @@ get_typmodout(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); @@ -2254,7 +2596,43 @@ get_typmodout(Oid typid) else return InvalidOid; } -#endif /* NOT_USED */ +#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 ---------- */ @@ -2264,20 +2642,33 @@ get_typmodout(Oid typid) * * 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; @@ -2295,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 (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. * @@ -2308,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) { @@ -2322,6 +2722,7 @@ get_attstatsslot(HeapTuple statstuple, Datum val; bool isnull; ArrayType *statarray; + Oid arrayelemtype; int narrayelem; HeapTuple typeTuple; Form_pg_type typeForm; @@ -2335,26 +2736,34 @@ 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; NULLs not expected */ deconstruct_array(statarray, - atttype, + arrayelemtype, typeForm->typlen, typeForm->typbyval, typeForm->typalign, @@ -2386,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) @@ -2456,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); @@ -2472,35 +2879,29 @@ get_namespace_name(Oid nspid) return NULL; } -/* ---------- PG_AUTHID CACHE ---------- */ +/* ---------- PG_RANGE CACHE ---------- */ /* - * get_roleid - * Given a role name, look up the role's OID. - * Returns InvalidOid if no such role. + * get_range_subtype + * Returns the subtype of a given range type + * + * Returns InvalidOid if the type is not a range type. */ Oid -get_roleid(const char *rolname) +get_range_subtype(Oid rangeOid) { - return GetSysCacheOid(AUTHNAME, - PointerGetDatum(rolname), - 0, 0, 0); -} + HeapTuple tp; -/* - * get_roleid_checked - * Given a role name, look up the role's OID. - * ereports if no such role. - */ -Oid -get_roleid_checked(const char *rolname) -{ - Oid roleid; + tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid)); + if (HeapTupleIsValid(tp)) + { + Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp); + Oid result; - roleid = get_roleid(rolname); - if (!OidIsValid(roleid)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("role \"%s\" does not exist", rolname))); - return roleid; + result = rngtup->rngsubtype; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; }