X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Futils%2Fcache%2Flsyscache.c;h=2c4d20576a5552da22c9e6912c261d646a711798;hb=addc42c339208d6a7a1d652fbf388e8aea7f80b9;hp=be59004441883860e87f16944af613ffafd55f86;hpb=ce2586dbc935e4c9e60f818c8aabae8fa0dd7fe8;p=postgresql diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index be59004441..2c4d20576a 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -1,178 +1,381 @@ /*------------------------------------------------------------------------- * * lsyscache.c - * Routines to access information within system caches - * - * Copyright (c) 1994, Regents of the University of California + * Convenience routines for common queries in the system catalog cache. * + * Portions Copyright (c) 1996-2005, 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.27 1999/05/29 01:45:21 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.123 2005/04/11 23:06:56 tgl Exp $ * * NOTES * Eventually, the index information should go through here, too. *------------------------------------------------------------------------- */ -#include #include "postgres.h" +#include "miscadmin.h" -#include "nodes/pg_list.h" -#include "utils/syscache.h" -#include "utils/lsyscache.h" +#include "access/hash.h" #include "access/tupmacs.h" -#include "utils/rel.h" -#include "utils/palloc.h" -#include "utils/elog.h" -#include "access/attnum.h" -#include "access/heapam.h" - #include "catalog/pg_amop.h" +#include "catalog/pg_amproc.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_group.h" +#include "catalog/pg_statistic.h" #include "catalog/pg_type.h" +#include "nodes/makefuncs.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/catcache.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" + /* ---------- AMOP CACHES ---------- */ /* - * op_class - + * op_in_opclass * * Return t iff operator 'opno' is in operator class 'opclass'. - * */ bool -op_class(Oid oprno, int32 opclass, Oid amopid) -{ - if (HeapTupleIsValid(SearchSysCacheTuple(AMOPOPID, - ObjectIdGetDatum(opclass), - ObjectIdGetDatum(oprno), - ObjectIdGetDatum(amopid), - 0))) - return true; - else - return false; +op_in_opclass(Oid opno, Oid opclass) +{ + return SearchSysCacheExists(AMOPOPID, + ObjectIdGetDatum(opno), + ObjectIdGetDatum(opclass), + 0, 0); } -/* ---------- ATTRIBUTE CACHES ---------- */ +/* + * get_op_opclass_strategy + * + * Get the operator's strategy number within the specified opclass, + * or 0 if it's not a member of the opclass. + */ +int +get_op_opclass_strategy(Oid opno, Oid opclass) +{ + HeapTuple tp; + Form_pg_amop amop_tup; + int result; + + tp = SearchSysCache(AMOPOPID, + ObjectIdGetDatum(opno), + ObjectIdGetDatum(opclass), + 0, 0); + if (!HeapTupleIsValid(tp)) + return 0; + amop_tup = (Form_pg_amop) GETSTRUCT(tp); + result = amop_tup->amopstrategy; + ReleaseSysCache(tp); + return result; +} + +/* + * get_op_opclass_properties + * + * Get the operator's strategy number, subtype, and recheck (lossy) flag + * within the specified opclass. + * + * Caller should already have verified that opno is a member of opclass, + * therefore we raise an error if the tuple is not found. + */ +void +get_op_opclass_properties(Oid opno, Oid opclass, + int *strategy, Oid *subtype, bool *recheck) +{ + HeapTuple tp; + Form_pg_amop amop_tup; + + tp = SearchSysCache(AMOPOPID, + ObjectIdGetDatum(opno), + ObjectIdGetDatum(opclass), + 0, 0); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "operator %u is not a member of opclass %u", + opno, opclass); + amop_tup = (Form_pg_amop) GETSTRUCT(tp); + *strategy = amop_tup->amopstrategy; + *subtype = amop_tup->amopsubtype; + *recheck = amop_tup->amopreqcheck; + ReleaseSysCache(tp); +} + +/* + * get_opclass_member + * Get the OID of the operator that implements the specified strategy + * with the specified subtype for the specified opclass. + * + * Returns InvalidOid if there is no pg_amop entry for the given keys. + */ +Oid +get_opclass_member(Oid opclass, Oid subtype, int16 strategy) +{ + HeapTuple tp; + Form_pg_amop amop_tup; + Oid result; + + tp = SearchSysCache(AMOPSTRATEGY, + ObjectIdGetDatum(opclass), + ObjectIdGetDatum(subtype), + Int16GetDatum(strategy), + 0); + if (!HeapTupleIsValid(tp)) + return InvalidOid; + amop_tup = (Form_pg_amop) GETSTRUCT(tp); + result = amop_tup->amopopr; + ReleaseSysCache(tp); + return result; +} + +/* + * get_op_hash_function + * Get the OID of the datatype-specific hash function associated with + * a hashable equality operator. + * + * Returns InvalidOid if no hash function can be found. (This indicates + * that the operator should not have been marked oprcanhash.) + */ +Oid +get_op_hash_function(Oid opno) +{ + 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. + */ + catlist = SearchSysCacheList(AMOPOPID, 1, + ObjectIdGetDatum(opno), + 0, 0, 0); + + 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)) + { + opclass = aform->amopclaid; + break; + } + } + + ReleaseSysCacheList(catlist); + + if (OidIsValid(opclass)) + { + /* Found a suitable opclass, get its default hash support function */ + return get_opclass_proc(opclass, InvalidOid, HASHPROC); + } + + /* Didn't find a match... */ + return InvalidOid; +} + + +/* ---------- AMPROC CACHES ---------- */ /* - * get_attname - + * get_opclass_proc + * Get the OID of the specified support function + * for the specified opclass and subtype. * + * Returns InvalidOid if there is no pg_amproc entry for the given keys. + */ +Oid +get_opclass_proc(Oid opclass, Oid subtype, int16 procnum) +{ + HeapTuple tp; + Form_pg_amproc amproc_tup; + RegProcedure result; + + tp = SearchSysCache(AMPROCNUM, + ObjectIdGetDatum(opclass), + ObjectIdGetDatum(subtype), + Int16GetDatum(procnum), + 0); + if (!HeapTupleIsValid(tp)) + return InvalidOid; + amproc_tup = (Form_pg_amproc) GETSTRUCT(tp); + result = amproc_tup->amproc; + ReleaseSysCache(tp); + return result; +} + + +/* ---------- ATTRIBUTE CACHES ---------- */ + +/* + * get_attname * Given the relation id and the attribute number, * return the "attname" field from the attribute relation. * + * Note: returns a palloc'd copy of the string, or NULL if no such attribute. */ char * get_attname(Oid relid, AttrNumber attnum) { HeapTuple tp; - tp = SearchSysCacheTuple(ATTNUM, - ObjectIdGetDatum(relid), - UInt16GetDatum(attnum), - 0, 0); + tp = SearchSysCache(ATTNUM, + ObjectIdGetDatum(relid), + Int16GetDatum(attnum), + 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp); - return pstrdup(att_tup->attname.data); + char *result; + + result = pstrdup(NameStr(att_tup->attname)); + ReleaseSysCache(tp); + return result; } else return NULL; } /* - * get_attnum - + * get_relid_attribute_name + * + * Same as above routine get_attname(), except that error + * is handled by elog() instead of returning NULL. + */ +char * +get_relid_attribute_name(Oid relid, AttrNumber attnum) +{ + char *attname; + + attname = get_attname(relid, attnum); + if (attname == NULL) + elog(ERROR, "cache lookup failed for attribute %d of relation %u", + attnum, relid); + return attname; +} + +/* + * get_attnum * * Given the relation id and the attribute name, * return the "attnum" field from the attribute relation. * + * Returns InvalidAttrNumber if the attr doesn't exist (or is dropped). */ AttrNumber -get_attnum(Oid relid, char *attname) +get_attnum(Oid relid, const char *attname) { HeapTuple tp; - tp = SearchSysCacheTuple(ATTNAME, - ObjectIdGetDatum(relid), - PointerGetDatum(attname), - 0, 0); + tp = SearchSysCacheAttName(relid, attname); if (HeapTupleIsValid(tp)) { Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp); - return att_tup->attnum; + AttrNumber result; + + result = att_tup->attnum; + ReleaseSysCache(tp); + return result; } else return InvalidAttrNumber; } /* - * get_atttype - + * get_atttype * * Given the relation OID and the attribute number with the relation, * return the attribute type OID. - * */ Oid get_atttype(Oid relid, AttrNumber attnum) { HeapTuple tp; - tp = SearchSysCacheTuple(ATTNUM, - ObjectIdGetDatum(relid), - UInt16GetDatum(attnum), - 0, 0); + tp = SearchSysCache(ATTNUM, + ObjectIdGetDatum(relid), + Int16GetDatum(attnum), + 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp); - return att_tup->atttypid; + Oid result; + + result = att_tup->atttypid; + ReleaseSysCache(tp); + return result; } else return InvalidOid; } -/* This routine uses the attname instead of the attnum because it - * replaces the routine find_atttype, which is called sometimes when - * only the attname, not the attno, is available. +/* + * get_atttypmod + * + * Given the relation id and the attribute number, + * return the "atttypmod" field from the attribute relation. */ -bool -get_attisset(Oid relid, char *attname) +int32 +get_atttypmod(Oid relid, AttrNumber attnum) { HeapTuple tp; - tp = SearchSysCacheTuple(ATTNAME, - ObjectIdGetDatum(relid), - PointerGetDatum(attname), - 0, 0); + tp = SearchSysCache(ATTNUM, + ObjectIdGetDatum(relid), + Int16GetDatum(attnum), + 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp); - return att_tup->attisset; + int32 result; + + result = att_tup->atttypmod; + ReleaseSysCache(tp); + return result; } else - return false; + return -1; } /* - * get_atttypmod - + * get_atttypetypmod * - * Given the relation id and the attribute number, - * return the "atttypmod" field from the attribute relation. + * A two-fer: given the relation id and the attribute number, + * fetch both type OID and atttypmod 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. */ -int32 -get_atttypmod(Oid relid, AttrNumber attnum) +void +get_atttypetypmod(Oid relid, AttrNumber attnum, + Oid *typid, int32 *typmod) { HeapTuple tp; + Form_pg_attribute att_tup; - tp = SearchSysCacheTuple(ATTNUM, - ObjectIdGetDatum(relid), - UInt16GetDatum(attnum), - 0, 0); - if (HeapTupleIsValid(tp)) - { - Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp); - return att_tup->atttypmod; - } - else - return -1; + tp = SearchSysCache(ATTNUM, + ObjectIdGetDatum(relid), + Int16GetDatum(attnum), + 0, 0); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for attribute %d of relation %u", + attnum, relid); + att_tup = (Form_pg_attribute) GETSTRUCT(tp); + + *typid = att_tup->atttypid; + *typmod = att_tup->atttypmod; + ReleaseSysCache(tp); } /* ---------- INDEX CACHE ---------- */ @@ -180,34 +383,89 @@ get_atttypmod(Oid relid, AttrNumber attnum) /* watch this space... */ +/* ---------- OPCLASS CACHE ---------- */ + +/* + * opclass_is_btree + * + * Returns TRUE iff the specified opclass is associated with the + * btree index access method. + */ +bool +opclass_is_btree(Oid opclass) +{ + HeapTuple tp; + Form_pg_opclass cla_tup; + bool result; + + tp = SearchSysCache(CLAOID, + ObjectIdGetDatum(opclass), + 0, 0, 0); + 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); + ReleaseSysCache(tp); + return result; +} + +/* + * opclass_is_hash + * + * Returns TRUE iff the specified opclass is associated with the + * hash index access method. + */ +bool +opclass_is_hash(Oid opclass) +{ + HeapTuple tp; + Form_pg_opclass cla_tup; + bool result; + + tp = SearchSysCache(CLAOID, + ObjectIdGetDatum(opclass), + 0, 0, 0); + 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); + ReleaseSysCache(tp); + return result; +} + /* ---------- OPERATOR CACHE ---------- */ /* - * get_opcode - + * get_opcode * * Returns the regproc id of the routine used to implement an * operator given the operator oid. - * */ RegProcedure get_opcode(Oid opno) { HeapTuple tp; - tp = SearchSysCacheTuple(OPROID, - ObjectIdGetDatum(opno), - 0, 0, 0); + tp = SearchSysCache(OPEROID, + ObjectIdGetDatum(opno), + 0, 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); - return optup->oprcode; + RegProcedure result; + + result = optup->oprcode; + ReleaseSysCache(tp); + return result; } else - return (RegProcedure) NULL; + return (RegProcedure) InvalidOid; } /* - * get_opname - + * get_opname * returns the name of the operator with the given opno * * Note: returns a palloc'd copy of the string, or NULL if no such operator. @@ -217,337 +475,1620 @@ get_opname(Oid opno) { HeapTuple tp; - tp = SearchSysCacheTuple(OPROID, - ObjectIdGetDatum(opno), - 0, 0, 0); + tp = SearchSysCache(OPEROID, + ObjectIdGetDatum(opno), + 0, 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); - return pstrdup(optup->oprname.data); + char *result; + + result = pstrdup(NameStr(optup->oprname)); + ReleaseSysCache(tp); + return result; } else return NULL; } /* - * op_mergejoinable - + * op_input_types * - * Returns the left and right sort operators and types corresponding to a - * mergejoinable operator, or nil if the operator is not mergejoinable. + * Returns the left and right input datatypes for an operator + * (InvalidOid if not relevant). + */ +void +op_input_types(Oid opno, Oid *lefttype, Oid *righttype) +{ + HeapTuple tp; + Form_pg_operator optup; + + tp = 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); + *lefttype = optup->oprleft; + *righttype = optup->oprright; + ReleaseSysCache(tp); +} + +/* + * op_mergejoinable * + * Returns the left and right sort operators corresponding to a + * mergejoinable operator, or false if the operator is not mergejoinable. */ bool -op_mergejoinable(Oid opno, Oid ltype, Oid rtype, Oid *leftOp, Oid *rightOp) +op_mergejoinable(Oid opno, Oid *leftOp, Oid *rightOp) { HeapTuple tp; + bool result = false; - tp = SearchSysCacheTuple(OPROID, - ObjectIdGetDatum(opno), - 0, 0, 0); + tp = SearchSysCache(OPEROID, + ObjectIdGetDatum(opno), + 0, 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); if (optup->oprlsortop && - optup->oprrsortop && - optup->oprleft == ltype && - optup->oprright == rtype) + optup->oprrsortop) { - *leftOp = ObjectIdGetDatum(optup->oprlsortop); - *rightOp = ObjectIdGetDatum(optup->oprrsortop); - return true; + *leftOp = optup->oprlsortop; + *rightOp = optup->oprrsortop; + result = true; } + ReleaseSysCache(tp); } - return false; + return result; } /* - * op_hashjoinable + * op_mergejoin_crossops * - * Returns the hash operator corresponding to a hashjoinable operator, - * or nil if the operator is not hashjoinable. + * 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. + */ +void +op_mergejoin_crossops(Oid opno, Oid *ltop, Oid *gtop, + RegProcedure *ltproc, RegProcedure *gtproc) +{ + HeapTuple tp; + Form_pg_operator optup; + + /* + * Get the declared comparison operators of the operator. + */ + 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); + + /* Check > op provided */ + if (!OidIsValid(*gtop)) + elog(ERROR, "mergejoin operator %u has no matching > operator", + opno); + if (gtproc) + *gtproc = get_opcode(*gtop); +} + +/* + * op_hashjoinable * + * Returns true if the operator is hashjoinable. */ -Oid -op_hashjoinable(Oid opno, Oid ltype, Oid rtype) +bool +op_hashjoinable(Oid opno) { HeapTuple tp; + bool result = false; - tp = SearchSysCacheTuple(OPROID, - ObjectIdGetDatum(opno), - 0, 0, 0); + tp = SearchSysCache(OPEROID, + ObjectIdGetDatum(opno), + 0, 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); - if (optup->oprcanhash && - optup->oprleft == ltype && - optup->oprright == rtype) - return opno; + result = optup->oprcanhash; + ReleaseSysCache(tp); } - return InvalidOid; + return result; +} + +/* + * op_strict + * + * Get the proisstrict flag for the operator's underlying function. + */ +bool +op_strict(Oid opno) +{ + RegProcedure funcid = get_opcode(opno); + + if (funcid == (RegProcedure) InvalidOid) + elog(ERROR, "operator %u does not exist", opno); + + return func_strict((Oid) funcid); } -HeapTuple -get_operator_tuple(Oid opno) +/* + * op_volatile + * + * Get the provolatile flag for the operator's underlying function. + */ +char +op_volatile(Oid opno) { - HeapTuple optup; + RegProcedure funcid = get_opcode(opno); - if ((optup = SearchSysCacheTuple(OPROID, - ObjectIdGetDatum(opno), - 0, 0, 0))) - return optup; - else - return (HeapTuple) NULL; + if (funcid == (RegProcedure) InvalidOid) + elog(ERROR, "operator %u does not exist", opno); + + return func_volatile((Oid) funcid); } /* - * get_commutator - + * get_commutator * * Returns the corresponding commutator of an operator. - * */ Oid get_commutator(Oid opno) { HeapTuple tp; - tp = SearchSysCacheTuple(OPROID, - ObjectIdGetDatum(opno), - 0, 0, 0); + tp = SearchSysCache(OPEROID, + ObjectIdGetDatum(opno), + 0, 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); - return optup->oprcom; + Oid result; + + result = optup->oprcom; + ReleaseSysCache(tp); + return result; } else return InvalidOid; } /* - * get_negator - + * get_negator * * Returns the corresponding negator of an operator. - * */ Oid get_negator(Oid opno) { HeapTuple tp; - tp = SearchSysCacheTuple(OPROID, - ObjectIdGetDatum(opno), - 0, 0, 0); + tp = SearchSysCache(OPEROID, + ObjectIdGetDatum(opno), + 0, 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); - return optup->oprnegate; + Oid result; + + result = optup->oprnegate; + ReleaseSysCache(tp); + return result; } else return InvalidOid; } /* - * get_oprrest - + * get_oprrest * * Returns procedure id for computing selectivity of an operator. - * */ RegProcedure get_oprrest(Oid opno) { HeapTuple tp; - tp = SearchSysCacheTuple(OPROID, - ObjectIdGetDatum(opno), - 0, 0, 0); + tp = SearchSysCache(OPEROID, + ObjectIdGetDatum(opno), + 0, 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); - return optup->oprrest; + RegProcedure result; + + result = optup->oprrest; + ReleaseSysCache(tp); + return result; } else - return (RegProcedure) NULL; + return (RegProcedure) InvalidOid; } /* - * get_oprjoin - + * get_oprjoin * * Returns procedure id for computing selectivity of a join. - * */ RegProcedure get_oprjoin(Oid opno) { HeapTuple tp; - tp = SearchSysCacheTuple(OPROID, - ObjectIdGetDatum(opno), - 0, 0, 0); + tp = SearchSysCache(OPEROID, + ObjectIdGetDatum(opno), + 0, 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); - return optup->oprjoin; + RegProcedure result; + + result = optup->oprjoin; + ReleaseSysCache(tp); + return result; } else - return (RegProcedure) NULL; + return (RegProcedure) InvalidOid; } -/* ---------- RELATION CACHE ---------- */ +/* ---------- FUNCTION CACHE ---------- */ /* - * get_relnatts - - * - * Returns the number of attributes for a given relation. + * get_func_name + * returns the name of the function with the given funcid * + * Note: returns a palloc'd copy of the string, or NULL if no such function. */ -int -get_relnatts(Oid relid) +char * +get_func_name(Oid funcid) { HeapTuple tp; - tp = SearchSysCacheTuple(RELOID, - ObjectIdGetDatum(relid), - 0, 0, 0); + tp = SearchSysCache(PROCOID, + ObjectIdGetDatum(funcid), + 0, 0, 0); if (HeapTupleIsValid(tp)) { - Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp); - return reltup->relnatts; + Form_pg_proc functup = (Form_pg_proc) GETSTRUCT(tp); + char *result; + + result = pstrdup(NameStr(functup->proname)); + ReleaseSysCache(tp); + return result; } else - return InvalidAttrNumber; + return NULL; } /* - * get_rel_name - - * - * Returns the name of a given relation. - * + * get_func_rettype + * Given procedure id, return the function's result type. */ -char * -get_rel_name(Oid relid) +Oid +get_func_rettype(Oid funcid) { HeapTuple tp; + Oid result; - tp = SearchSysCacheTuple(RELOID, - ObjectIdGetDatum(relid), - 0, 0, 0); - if (HeapTupleIsValid(tp)) - { - Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp); - return pstrdup(reltup->relname.data); - } - else - return NULL; -} + tp = SearchSysCache(PROCOID, + ObjectIdGetDatum(funcid), + 0, 0, 0); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for function %u", funcid); -/* ---------- TYPE CACHE ---------- */ + result = ((Form_pg_proc) GETSTRUCT(tp))->prorettype; + ReleaseSysCache(tp); + return result; +} /* - * get_typlen - - * - * Given the type OID, return the length of the type. - * + * get_func_nargs + * Given procedure id, return the number of arguments. */ -int16 -get_typlen(Oid typid) +int +get_func_nargs(Oid funcid) { HeapTuple tp; + int result; - tp = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(typid), - 0, 0, 0); - if (HeapTupleIsValid(tp)) - { - Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); - return typtup->typlen; - } - else - return 0; + tp = SearchSysCache(PROCOID, + ObjectIdGetDatum(funcid), + 0, 0, 0); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for function %u", funcid); + + result = ((Form_pg_proc) GETSTRUCT(tp))->pronargs; + ReleaseSysCache(tp); + return result; } /* - * get_typbyval - - * - * Given the type OID, determine whether the type is returned by value or - * not. Returns 1 if by value, 0 if by reference. + * get_func_signature + * Given procedure id, return the function's argument and result types. + * (The return value is the result type.) * + * The arguments are returned as a palloc'd array. */ -bool -get_typbyval(Oid typid) +Oid +get_func_signature(Oid funcid, Oid **argtypes, int *nargs) { HeapTuple tp; + Form_pg_proc procstruct; + Oid result; - tp = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(typid), - 0, 0, 0); - if (HeapTupleIsValid(tp)) - { - Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); - return (bool) typtup->typbyval; - } - else - return false; + tp = SearchSysCache(PROCOID, + ObjectIdGetDatum(funcid), + 0, 0, 0); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for function %u", funcid); + + procstruct = (Form_pg_proc) GETSTRUCT(tp); + + result = procstruct->prorettype; + *nargs = (int) procstruct->pronargs; + Assert(*nargs == procstruct->proargtypes.dim1); + *argtypes = (Oid *) palloc(*nargs * sizeof(Oid)); + memcpy(*argtypes, procstruct->proargtypes.values, *nargs * sizeof(Oid)); + + ReleaseSysCache(tp); + return result; } -#ifdef NOT_USED -char -get_typalign(Oid typid) +/* + * get_func_retset + * Given procedure id, return the function's proretset flag. + */ +bool +get_func_retset(Oid funcid) { HeapTuple tp; + bool result; - tp = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(typid), - 0, 0, 0); - if (HeapTupleIsValid(tp)) - { - Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); - return typtup->typalign; - } - else - return 'i'; + tp = SearchSysCache(PROCOID, + ObjectIdGetDatum(funcid), + 0, 0, 0); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for function %u", funcid); + + result = ((Form_pg_proc) GETSTRUCT(tp))->proretset; + ReleaseSysCache(tp); + return result; } +/* + * func_strict + * Given procedure id, return the function's proisstrict flag. + */ +bool +func_strict(Oid funcid) +{ + HeapTuple tp; + bool result; + + tp = SearchSysCache(PROCOID, + ObjectIdGetDatum(funcid), + 0, 0, 0); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for function %u", funcid); + + result = ((Form_pg_proc) GETSTRUCT(tp))->proisstrict; + ReleaseSysCache(tp); + return result; +} + +/* + * func_volatile + * Given procedure id, return the function's provolatile flag. + */ +char +func_volatile(Oid funcid) +{ + HeapTuple tp; + char result; + + tp = SearchSysCache(PROCOID, + ObjectIdGetDatum(funcid), + 0, 0, 0); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for function %u", funcid); + + result = ((Form_pg_proc) GETSTRUCT(tp))->provolatile; + ReleaseSysCache(tp); + return result; +} + +/* ---------- RELATION CACHE ---------- */ + +/* + * get_relname_relid + * Given name and namespace of a relation, look up the OID. + * + * Returns InvalidOid if there is no such relation. + */ +Oid +get_relname_relid(const char *relname, Oid relnamespace) +{ + return 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; +} + +#ifdef NOT_USED +/* + * get_relnatts + * + * Returns the number of attributes for a given relation. + */ +int +get_relnatts(Oid relid) +{ + HeapTuple tp; + + tp = SearchSysCache(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp); + int result; + + result = reltup->relnatts; + ReleaseSysCache(tp); + return result; + } + else + return InvalidAttrNumber; +} #endif /* - * get_typdefault - + * get_rel_name + * Returns the name of a given relation. * - * Given the type OID, return the default value of the ADT. + * Returns a palloc'd copy of the string, or NULL if no such relation. * + * NOTE: since relation name is not unique, be wary of code that uses this + * for anything except preparing error messages. */ -struct varlena * -get_typdefault(Oid typid) +char * +get_rel_name(Oid relid) { - struct varlena *typdefault = (struct varlena *) TypeDefaultRetrieve(typid); + HeapTuple tp; + + tp = SearchSysCache(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp); + char *result; - return typdefault; + result = pstrdup(NameStr(reltup->relname)); + ReleaseSysCache(tp); + return result; + } + else + return NULL; } /* - * get_typtype - + * get_rel_namespace * - * Given the type OID, find if it is a basic type, a named relation - * or the generic type 'relation'. - * It returns the null char if the cache lookup fails... + * Returns the pg_namespace OID associated with a given relation. + */ +Oid +get_rel_namespace(Oid relid) +{ + HeapTuple tp; + + tp = SearchSysCache(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp); + Oid result; + + result = reltup->relnamespace; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; +} + +/* + * get_rel_type_id + * + * Returns the pg_type OID associated with a given relation. * + * Note: not all pg_class entries have associated pg_type OIDs; so be + * careful to check for InvalidOid result. */ +Oid +get_rel_type_id(Oid relid) +{ + HeapTuple tp; + + tp = SearchSysCache(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp); + Oid result; + + result = reltup->reltype; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; +} + +/* + * get_rel_relkind + * + * Returns the relkind associated with a given relation. + */ +char +get_rel_relkind(Oid relid) +{ + HeapTuple tp; + + tp = SearchSysCache(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp); + char result; + + result = reltup->relkind; + ReleaseSysCache(tp); + return result; + } + else + return '\0'; +} + + +/* ---------- TYPE CACHE ---------- */ + +/* + * get_typisdefined + * + * Given the type OID, determine whether the type is defined + * (if not, it's only a shell). + */ +bool +get_typisdefined(Oid typid) +{ + HeapTuple tp; + + tp = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); + bool result; + + result = typtup->typisdefined; + ReleaseSysCache(tp); + return result; + } + else + return false; +} + +/* + * get_typlen + * + * Given the type OID, return the length of the type. + */ +int16 +get_typlen(Oid typid) +{ + HeapTuple tp; + + tp = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); + int16 result; + + result = typtup->typlen; + ReleaseSysCache(tp); + return result; + } + else + return 0; +} + +/* + * get_typbyval + * + * Given the type OID, determine whether the type is returned by value or + * not. Returns true if by value, false if by reference. + */ +bool +get_typbyval(Oid typid) +{ + HeapTuple tp; + + tp = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); + bool result; + + result = typtup->typbyval; + ReleaseSysCache(tp); + return result; + } + else + return false; +} + +/* + * get_typlenbyval + * + * A two-fer: given the type OID, return both typlen and typbyval. + * + * Since both pieces of info are needed to know how to copy a Datum, + * many places need both. Might as well get them with one cache lookup + * instead of two. Also, this routine raises an error instead of + * returning a bogus value when given a bad type OID. + */ +void +get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval) +{ + HeapTuple tp; + Form_pg_type typtup; + + tp = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typid), + 0, 0, 0); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for type %u", typid); + typtup = (Form_pg_type) GETSTRUCT(tp); + *typlen = typtup->typlen; + *typbyval = typtup->typbyval; + ReleaseSysCache(tp); +} + +/* + * get_typlenbyvalalign + * + * A three-fer: given the type OID, return typlen, typbyval, typalign. + */ +void +get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, + char *typalign) +{ + HeapTuple tp; + Form_pg_type typtup; + + tp = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typid), + 0, 0, 0); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for type %u", typid); + typtup = (Form_pg_type) GETSTRUCT(tp); + *typlen = typtup->typlen; + *typbyval = typtup->typbyval; + *typalign = typtup->typalign; + ReleaseSysCache(tp); +} + +/* + * getTypeIOParam + * Given a pg_type row, select the type OID to pass to I/O functions + * + * Formerly, all I/O functions were passed pg_type.typelem as their second + * parameter, but we now have a more complex rule about what to pass. + * This knowledge is intended to be centralized here --- direct references + * to typelem elsewhere in the code are wrong, if they are associated with + * I/O calls and not with actual subscripting operations! (But see + * bootstrap.c, which can't conveniently use this routine.) + */ +Oid +getTypeIOParam(HeapTuple typeTuple) +{ + Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); + + /* + * Composite types get their own OID as parameter; array types get + * their typelem as parameter; everybody else gets zero. + */ + if (typeStruct->typtype == 'c') + return HeapTupleGetOid(typeTuple); + else + return typeStruct->typelem; +} + +/* + * get_type_io_data + * + * A six-fer: given the type OID, return typlen, typbyval, typalign, + * typdelim, typioparam, and IO function OID. The IO function + * returned is controlled by IOFuncSelector + */ +void +get_type_io_data(Oid typid, + IOFuncSelector which_func, + int16 *typlen, + bool *typbyval, + char *typalign, + char *typdelim, + Oid *typioparam, + Oid *func) +{ + HeapTuple typeTuple; + Form_pg_type typeStruct; + + typeTuple = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typid), + 0, 0, 0); + if (!HeapTupleIsValid(typeTuple)) + elog(ERROR, "cache lookup failed for type %u", typid); + typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); + + *typlen = typeStruct->typlen; + *typbyval = typeStruct->typbyval; + *typalign = typeStruct->typalign; + *typdelim = typeStruct->typdelim; + *typioparam = getTypeIOParam(typeTuple); + switch (which_func) + { + case IOFunc_input: + *func = typeStruct->typinput; + break; + case IOFunc_output: + *func = typeStruct->typoutput; + break; + case IOFunc_receive: + *func = typeStruct->typreceive; + break; + case IOFunc_send: + *func = typeStruct->typsend; + break; + } + ReleaseSysCache(typeTuple); +} + #ifdef NOT_USED char -get_typtype(Oid typid) +get_typalign(Oid typid) { HeapTuple tp; - tp = SearchSysCacheTuple(TYPOID, + tp = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); + char result; + + result = typtup->typalign; + ReleaseSysCache(tp); + return result; + } + else + return 'i'; +} +#endif + +char +get_typstorage(Oid typid) +{ + HeapTuple tp; + + tp = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); + char result; + + result = typtup->typstorage; + ReleaseSysCache(tp); + return result; + } + else + 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. + * + * The result is a palloc'd expression node tree, or NULL if there + * is no defined default for the datatype. + * + * NB: caller should be prepared to coerce result to correct datatype; + * the returned expression tree might produce something of the wrong type. + */ +Node * +get_typdefault(Oid typid) +{ + HeapTuple typeTuple; + Form_pg_type type; + Datum datum; + bool isNull; + Node *expr; + + typeTuple = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typid), + 0, 0, 0); + if (!HeapTupleIsValid(typeTuple)) + elog(ERROR, "cache lookup failed for type %u", typid); + type = (Form_pg_type) GETSTRUCT(typeTuple); + + /* + * typdefault and typdefaultbin are potentially null, so don't try to + * access 'em as struct fields. Must do it the hard way with + * SysCacheGetAttr. + */ + datum = SysCacheGetAttr(TYPEOID, + typeTuple, + Anum_pg_type_typdefaultbin, + &isNull); + + if (!isNull) + { + /* We have an expression default */ + expr = stringToNode(DatumGetCString(DirectFunctionCall1(textout, + datum))); + } + else + { + /* Perhaps we have a plain literal default */ + datum = SysCacheGetAttr(TYPEOID, + typeTuple, + Anum_pg_type_typdefault, + &isNull); + + if (!isNull) + { + char *strDefaultVal; + + /* Convert text datum to C string */ + strDefaultVal = DatumGetCString(DirectFunctionCall1(textout, + datum)); + /* Convert C string to a value of the given type */ + datum = OidFunctionCall3(type->typinput, + CStringGetDatum(strDefaultVal), + ObjectIdGetDatum(getTypeIOParam(typeTuple)), + Int32GetDatum(-1)); + /* Build a Const node containing the value */ + expr = (Node *) makeConst(typid, + type->typlen, + datum, + false, + type->typbyval); + pfree(strDefaultVal); + } + else + { + /* No default */ + expr = NULL; + } + } + + ReleaseSysCache(typeTuple); + + return expr; +} + +/* + * getBaseType + * If the given type is a domain, return its base type; + * otherwise return the type's own OID. + */ +Oid +getBaseType(Oid typid) +{ + /* + * We loop to find the bottom base type in a stack of domains. + */ + for (;;) + { + HeapTuple tup; + Form_pg_type typTup; + + tup = SearchSysCache(TYPEOID, ObjectIdGetDatum(typid), 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for type %u", typid); + typTup = (Form_pg_type) GETSTRUCT(tup); + if (typTup->typtype != 'd') + { + /* Not a domain, so done */ + ReleaseSysCache(tup); + break; + } + + typid = typTup->typbasetype; + ReleaseSysCache(tup); + } + + return typid; +} + +/* + * get_typavgwidth + * + * Given a type OID and a typmod value (pass -1 if typmod is unknown), + * estimate the average width of values of the type. This is used by + * the planner, which doesn't require absolutely correct results; + * it's OK (and expected) to guess if we don't know for sure. + */ +int32 +get_typavgwidth(Oid typid, int32 typmod) +{ + int typlen = get_typlen(typid); + int32 maxwidth; + + /* + * Easy if it's a fixed-width type + */ + if (typlen > 0) + return typlen; + + /* + * type_maximum_size knows the encoding of typmod for some datatypes; + * don't duplicate that knowledge here. + */ + maxwidth = type_maximum_size(typid, typmod); + if (maxwidth > 0) + { + /* + * For BPCHAR, the max width is also the only width. Otherwise we + * need to guess about the typical data width given the max. A + * sliding scale for percentage of max width seems reasonable. + */ + if (typid == BPCHAROID) + return maxwidth; + if (maxwidth <= 32) + return maxwidth; /* assume full width */ + if (maxwidth < 1000) + return 32 + (maxwidth - 32) / 2; /* assume 50% */ + + /* + * Beyond 1000, assume we're looking at something like + * "varchar(10000)" where the limit isn't actually reached often, + * and use a fixed estimate. + */ + return 32 + (1000 - 32) / 2; + } + + /* + * Ooops, we have no idea ... wild guess time. + */ + return 32; +} + +/* + * get_typtype + * + * Given the type OID, find if it is a basic type, a complex type, etc. + * It returns the null char if the cache lookup fails... + */ +char +get_typtype(Oid typid) +{ + HeapTuple tp; + + tp = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typid), + 0, 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); - return typtup->typtype; + char result; + + result = typtup->typtype; + ReleaseSysCache(tp); + return result; } else return '\0'; } -#endif +/* + * get_typ_typrelid + * + * Given the type OID, get the typrelid (InvalidOid if not a complex + * type). + */ +Oid +get_typ_typrelid(Oid typid) +{ + HeapTuple tp; + + tp = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); + Oid result; + + result = typtup->typrelid; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; +} + +/* + * get_element_type + * + * Given the type OID, get the typelem (InvalidOid if not an array type). + * + * NB: this only considers varlena arrays to be true arrays; InvalidOid is + * returned if the input is a fixed-length array type. + */ +Oid +get_element_type(Oid typid) +{ + HeapTuple tp; + + tp = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); + Oid result; + + if (typtup->typlen == -1) + result = typtup->typelem; + else + result = InvalidOid; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; +} + +/* + * get_array_type + * + * Given the type OID, get the corresponding 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; + + tp = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typid), + 0, 0, 0); + 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; + ReleaseSysCache(tp); + + tp = SearchSysCache(TYPENAMENSP, + PointerGetDatum(array_typename), + ObjectIdGetDatum(namespaceId), + 0, 0); + + pfree(array_typename); + + if (HeapTupleIsValid(tp)) + { + Oid result; + + typtup = (Form_pg_type) GETSTRUCT(tp); + if (typtup->typlen == -1 && typtup->typelem == typid) + result = HeapTupleGetOid(tp); + else + result = InvalidOid; + ReleaseSysCache(tp); + return result; + } + } + return InvalidOid; +} + +/* + * getTypeInputInfo + * + * Get info needed for converting values of a type to internal form + */ +void +getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam) +{ + HeapTuple typeTuple; + Form_pg_type pt; + + typeTuple = SearchSysCache(TYPEOID, + ObjectIdGetDatum(type), + 0, 0, 0); + if (!HeapTupleIsValid(typeTuple)) + elog(ERROR, "cache lookup failed for type %u", type); + pt = (Form_pg_type) GETSTRUCT(typeTuple); + + if (!pt->typisdefined) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("type %s is only a shell", + format_type_be(type)))); + if (!OidIsValid(pt->typinput)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("no input function available for type %s", + format_type_be(type)))); + + *typInput = pt->typinput; + *typIOParam = getTypeIOParam(typeTuple); + + ReleaseSysCache(typeTuple); +} + +/* + * getTypeOutputInfo + * + * Get info needed for printing values of a type + */ +void +getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typIOParam, + bool *typIsVarlena) +{ + HeapTuple typeTuple; + Form_pg_type pt; + + typeTuple = SearchSysCache(TYPEOID, + ObjectIdGetDatum(type), + 0, 0, 0); + if (!HeapTupleIsValid(typeTuple)) + elog(ERROR, "cache lookup failed for type %u", type); + pt = (Form_pg_type) GETSTRUCT(typeTuple); + + if (!pt->typisdefined) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("type %s is only a shell", + format_type_be(type)))); + if (!OidIsValid(pt->typoutput)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("no output function available for type %s", + format_type_be(type)))); + + *typOutput = pt->typoutput; + *typIOParam = getTypeIOParam(typeTuple); + *typIsVarlena = (!pt->typbyval) && (pt->typlen == -1); + + ReleaseSysCache(typeTuple); +} + +/* + * getTypeBinaryInputInfo + * + * Get info needed for binary input of values of a type + */ +void +getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typIOParam) +{ + HeapTuple typeTuple; + Form_pg_type pt; + + typeTuple = SearchSysCache(TYPEOID, + ObjectIdGetDatum(type), + 0, 0, 0); + if (!HeapTupleIsValid(typeTuple)) + elog(ERROR, "cache lookup failed for type %u", type); + pt = (Form_pg_type) GETSTRUCT(typeTuple); + + if (!pt->typisdefined) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("type %s is only a shell", + format_type_be(type)))); + if (!OidIsValid(pt->typreceive)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("no binary input function available for type %s", + format_type_be(type)))); + + *typReceive = pt->typreceive; + *typIOParam = getTypeIOParam(typeTuple); + + ReleaseSysCache(typeTuple); +} + +/* + * getTypeBinaryOutputInfo + * + * Get info needed for binary output of values of a type + */ +void +getTypeBinaryOutputInfo(Oid type, Oid *typSend, Oid *typIOParam, + bool *typIsVarlena) +{ + HeapTuple typeTuple; + Form_pg_type pt; + + typeTuple = SearchSysCache(TYPEOID, + ObjectIdGetDatum(type), + 0, 0, 0); + if (!HeapTupleIsValid(typeTuple)) + elog(ERROR, "cache lookup failed for type %u", type); + pt = (Form_pg_type) GETSTRUCT(typeTuple); + + if (!pt->typisdefined) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("type %s is only a shell", + format_type_be(type)))); + if (!OidIsValid(pt->typsend)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("no binary output function available for type %s", + format_type_be(type)))); + + *typSend = pt->typsend; + *typIOParam = getTypeIOParam(typeTuple); + *typIsVarlena = (!pt->typbyval) && (pt->typlen == -1); + + ReleaseSysCache(typeTuple); +} + + +/* ---------- STATISTICS CACHE ---------- */ + +/* + * get_attavgwidth + * + * Given the table and attribute number of a column, get the average + * width of entries in the column. Return zero if no data available. + */ +int32 +get_attavgwidth(Oid relid, AttrNumber attnum) +{ + HeapTuple tp; + + tp = SearchSysCache(STATRELATT, + ObjectIdGetDatum(relid), + Int16GetDatum(attnum), + 0, 0); + if (HeapTupleIsValid(tp)) + { + int32 stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth; + + ReleaseSysCache(tp); + if (stawidth > 0) + return stawidth; + } + return 0; +} + +/* + * get_attstatsslot + * + * Extract the contents of a "slot" of a pg_statistic tuple. + * Returns TRUE if requested slot type was found, else FALSE. + * + * Unlike other routines in this file, this takes a pointer to an + * already-looked-up tuple in the pg_statistic cache. We do this since + * most callers will want to extract more than one value from the cache + * entry, and we don't want to repeat the cache lookup unnecessarily. + * + * statstuple: pg_statistics tuple to be examined. + * atttype: type OID of attribute. + * atttypmod: typmod of attribute. + * reqkind: STAKIND code for desired statistics slot kind. + * reqop: STAOP value wanted, or InvalidOid if don't care. + * values, nvalues: if not NULL, the slot's stavalues are extracted. + * numbers, nnumbers: if not NULL, the slot's stanumbers are extracted. + * + * If assigned, values and numbers are set to point to palloc'd arrays. + * 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. + */ +bool +get_attstatsslot(HeapTuple statstuple, + Oid atttype, int32 atttypmod, + int reqkind, Oid reqop, + Datum **values, int *nvalues, + float4 **numbers, int *nnumbers) +{ + Form_pg_statistic stats = (Form_pg_statistic) GETSTRUCT(statstuple); + int i, + j; + Datum val; + bool isnull; + ArrayType *statarray; + int narrayelem; + HeapTuple typeTuple; + Form_pg_type typeForm; + + for (i = 0; i < STATISTIC_NUM_SLOTS; i++) + { + if ((&stats->stakind1)[i] == reqkind && + (reqop == InvalidOid || (&stats->staop1)[i] == reqop)) + break; + } + if (i >= STATISTIC_NUM_SLOTS) + return false; /* not there */ + + if (values) + { + val = SysCacheGetAttr(STATRELATT, 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); + if (!HeapTupleIsValid(typeTuple)) + elog(ERROR, "cache lookup failed for type %u", atttype); + typeForm = (Form_pg_type) GETSTRUCT(typeTuple); + + /* Deconstruct array into Datum elements */ + deconstruct_array(statarray, + atttype, + typeForm->typlen, + typeForm->typbyval, + typeForm->typalign, + values, 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 (!typeForm->typbyval) + { + for (j = 0; j < *nvalues; j++) + { + (*values)[j] = datumCopy((*values)[j], + typeForm->typbyval, + typeForm->typlen); + } + } + + ReleaseSysCache(typeTuple); + + /* + * Free statarray if it's a detoasted copy. + */ + if ((Pointer) statarray != DatumGetPointer(val)) + pfree(statarray); + } + + if (numbers) + { + val = SysCacheGetAttr(STATRELATT, statstuple, + Anum_pg_statistic_stanumbers1 + i, + &isnull); + if (isnull) + elog(ERROR, "stanumbers is null"); + 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. + */ + narrayelem = ARR_DIMS(statarray)[0]; + if (ARR_NDIM(statarray) != 1 || narrayelem <= 0 || + ARR_ELEMTYPE(statarray) != FLOAT4OID) + elog(ERROR, "stanumbers is not a 1-D float4 array"); + *numbers = (float4 *) palloc(narrayelem * sizeof(float4)); + memcpy(*numbers, ARR_DATA_PTR(statarray), narrayelem * sizeof(float4)); + *nnumbers = narrayelem; + + /* + * Free statarray if it's a detoasted copy. + */ + if ((Pointer) statarray != DatumGetPointer(val)) + pfree(statarray); + } + + return true; +} + +void +free_attstatsslot(Oid atttype, + Datum *values, int nvalues, + float4 *numbers, int nnumbers) +{ + if (values) + { + if (!get_typbyval(atttype)) + { + int i; + + for (i = 0; i < nvalues; i++) + pfree(DatumGetPointer(values[i])); + } + pfree(values); + } + if (numbers) + pfree(numbers); +} + +/* ---------- PG_NAMESPACE CACHE ---------- */ + +/* + * get_namespace_name + * Returns the name of a given namespace + * + * Returns a palloc'd copy of the string, or NULL if no such namespace. + */ +char * +get_namespace_name(Oid nspid) +{ + HeapTuple tp; + + tp = SearchSysCache(NAMESPACEOID, + ObjectIdGetDatum(nspid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Form_pg_namespace nsptup = (Form_pg_namespace) GETSTRUCT(tp); + char *result; + + result = pstrdup(NameStr(nsptup->nspname)); + ReleaseSysCache(tp); + return result; + } + else + return NULL; +} + +/* ---------- PG_SHADOW 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). + * + * 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 ... + */ +AclId +get_usesysid(const char *username) +{ + AclId userId; + 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))); + + userId = ((Form_pg_shadow) GETSTRUCT(userTup))->usesysid; + + ReleaseSysCache(userTup); + + return userId; +} + +/* + * get_grosysid + * + * Given a group name, look up the group's sysid. + * Raises an error if no such group (rather than returning zero, + * which might possibly be a valid grosysid). + * + */ +AclId +get_grosysid(char *groname) +{ + AclId groupId; + HeapTuple groupTup; + + groupTup = SearchSysCache(GRONAME, + PointerGetDatum(groname), + 0, 0, 0); + if (!HeapTupleIsValid(groupTup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("group \"%s\" does not exist", groname))); + + groupId = ((Form_pg_group) GETSTRUCT(groupTup))->grosysid; + + ReleaseSysCache(groupTup); + + return groupId; +} +