/*------------------------------------------------------------------------- * * lsyscache.c * Convenience routines for common queries in the system catalog cache. * * Portions Copyright (c) 1996-2001, 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.69 2002/04/05 00:31:30 tgl Exp $ * * NOTES * Eventually, the index information should go through here, too. *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/tupmacs.h" #include "catalog/pg_amop.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_statistic.h" #include "catalog/pg_type.h" #include "nodes/makefuncs.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/syscache.h" /* ---------- AMOP CACHES ---------- */ /* * op_in_opclass * * Return t iff operator 'opno' is in operator class 'opclass'. */ bool op_in_opclass(Oid opno, Oid opclass) { return SearchSysCacheExists(AMOPOPID, ObjectIdGetDatum(opclass), ObjectIdGetDatum(opno), 0, 0); } /* * op_requires_recheck * * Return t if operator 'opno' requires a recheck when used as a * member of opclass 'opclass' (ie, this opclass is lossy for this * operator). * * Caller should already have verified that opno is a member of opclass, * therefore we raise an error if the tuple is not found. */ bool op_requires_recheck(Oid opno, Oid opclass) { HeapTuple tp; Form_pg_amop amop_tup; bool result; tp = SearchSysCache(AMOPOPID, ObjectIdGetDatum(opclass), ObjectIdGetDatum(opno), 0, 0); if (!HeapTupleIsValid(tp)) elog(ERROR, "op_requires_recheck: op %u is not a member of opclass %u", opno, opclass); amop_tup = (Form_pg_amop) GETSTRUCT(tp); result = amop_tup->amopreqcheck; 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 operator. */ char * get_attname(Oid relid, AttrNumber attnum) { HeapTuple tp; tp = SearchSysCache(ATTNUM, ObjectIdGetDatum(relid), Int16GetDatum(attnum), 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp); char *result; result = pstrdup(NameStr(att_tup->attname)); ReleaseSysCache(tp); return result; } else return NULL; } /* * get_attnum * * Given the relation id and the attribute name, * return the "attnum" field from the attribute relation. */ AttrNumber get_attnum(Oid relid, char *attname) { HeapTuple tp; tp = SearchSysCache(ATTNAME, ObjectIdGetDatum(relid), PointerGetDatum(attname), 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp); AttrNumber result; result = att_tup->attnum; ReleaseSysCache(tp); return result; } else return InvalidAttrNumber; } /* * get_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 = SearchSysCache(ATTNUM, ObjectIdGetDatum(relid), Int16GetDatum(attnum), 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp); Oid result; result = att_tup->atttypid; ReleaseSysCache(tp); return result; } else return InvalidOid; } /* 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. */ bool get_attisset(Oid relid, char *attname) { HeapTuple tp; tp = SearchSysCache(ATTNAME, ObjectIdGetDatum(relid), PointerGetDatum(attname), 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp); bool result; result = att_tup->attisset; ReleaseSysCache(tp); return result; } else return false; } /* * get_atttypmod * * Given the relation id and the attribute number, * return the "atttypmod" field from the attribute relation. */ int32 get_atttypmod(Oid relid, AttrNumber attnum) { HeapTuple tp; tp = SearchSysCache(ATTNUM, ObjectIdGetDatum(relid), Int16GetDatum(attnum), 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp); int32 result; result = att_tup->atttypmod; ReleaseSysCache(tp); return result; } else return -1; } /* * get_atttypetypmod * * 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. */ void get_atttypetypmod(Oid relid, AttrNumber attnum, Oid *typid, int32 *typmod) { HeapTuple tp; Form_pg_attribute att_tup; tp = SearchSysCache(ATTNUM, ObjectIdGetDatum(relid), Int16GetDatum(attnum), 0, 0); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for relation %u attribute %d", relid, attnum); att_tup = (Form_pg_attribute) GETSTRUCT(tp); *typid = att_tup->atttypid; *typmod = att_tup->atttypmod; ReleaseSysCache(tp); } /* ---------- INDEX CACHE ---------- */ /* 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; } /* ---------- OPERATOR CACHE ---------- */ /* * get_opcode * * Returns the regproc id of the routine used to implement an * operator given the operator oid. */ RegProcedure get_opcode(Oid opno) { HeapTuple tp; tp = SearchSysCache(OPEROID, ObjectIdGetDatum(opno), 0, 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); RegProcedure result; result = optup->oprcode; ReleaseSysCache(tp); return result; } else return (RegProcedure) InvalidOid; } /* * get_opname * returns the name of the operator with the given opno * * Note: returns a palloc'd copy of the string, or NULL if no such operator. */ char * get_opname(Oid opno) { HeapTuple tp; tp = SearchSysCache(OPEROID, ObjectIdGetDatum(opno), 0, 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); char *result; result = pstrdup(NameStr(optup->oprname)); ReleaseSysCache(tp); return result; } else return NULL; } /* * op_mergejoinable * * Returns the left and right sort operators and types corresponding to a * mergejoinable operator, or nil if the operator is not mergejoinable. */ bool op_mergejoinable(Oid opno, Oid ltype, Oid rtype, Oid *leftOp, Oid *rightOp) { HeapTuple tp; bool result = false; tp = SearchSysCache(OPEROID, ObjectIdGetDatum(opno), 0, 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); if (optup->oprlsortop && optup->oprrsortop && optup->oprleft == ltype && optup->oprright == rtype) { *leftOp = optup->oprlsortop; *rightOp = optup->oprrsortop; result = true; } ReleaseSysCache(tp); } return result; } /* * op_mergejoin_crossops * * 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. * * Raises error if operators cannot be found. Assuming that the operator * had indeed been marked mergejoinable, this indicates that whoever marked * it so was mistaken. */ void op_mergejoin_crossops(Oid opno, Oid *ltop, Oid *gtop, RegProcedure *ltproc, RegProcedure *gtproc) { HeapTuple tp; Form_pg_operator optup; Oid oprleft, oprright; /* * Get the declared left and right operand types of the operator. */ tp = SearchSysCache(OPEROID, ObjectIdGetDatum(opno), 0, 0, 0); if (!HeapTupleIsValid(tp)) /* shouldn't happen */ elog(ERROR, "op_mergejoin_crossops: operator %u not found", opno); optup = (Form_pg_operator) GETSTRUCT(tp); oprleft = optup->oprleft; oprright = optup->oprright; ReleaseSysCache(tp); /* * Look up the "<" operator with the same input types. If there isn't * one, whoever marked the "=" operator mergejoinable was a loser. */ tp = SearchSysCache(OPERNAME, PointerGetDatum("<"), ObjectIdGetDatum(oprleft), ObjectIdGetDatum(oprright), CharGetDatum('b')); if (!HeapTupleIsValid(tp)) elog(ERROR, "op_mergejoin_crossops: mergejoin operator %u has no matching < operator", opno); optup = (Form_pg_operator) GETSTRUCT(tp); *ltop = tp->t_data->t_oid; if (ltproc) *ltproc = optup->oprcode; ReleaseSysCache(tp); /* * And the same for the ">" operator. */ tp = SearchSysCache(OPERNAME, PointerGetDatum(">"), ObjectIdGetDatum(oprleft), ObjectIdGetDatum(oprright), CharGetDatum('b')); if (!HeapTupleIsValid(tp)) elog(ERROR, "op_mergejoin_crossops: mergejoin operator %u has no matching > operator", opno); optup = (Form_pg_operator) GETSTRUCT(tp); *gtop = tp->t_data->t_oid; if (gtproc) *gtproc = optup->oprcode; ReleaseSysCache(tp); } /* * op_hashjoinable * * Returns the hash operator corresponding to a hashjoinable operator, * or InvalidOid if the operator is not hashjoinable. */ Oid op_hashjoinable(Oid opno, Oid ltype, Oid rtype) { HeapTuple tp; Oid result = InvalidOid; 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) result = opno; ReleaseSysCache(tp); } return result; } /* * op_volatile * * Get the provolatile flag for the operator's underlying function. */ char op_volatile(Oid opno) { RegProcedure funcid = get_opcode(opno); if (funcid == (RegProcedure) InvalidOid) elog(ERROR, "Operator OID %u does not exist", opno); return func_volatile((Oid) funcid); } /* * get_commutator * * Returns the corresponding commutator of an operator. */ Oid get_commutator(Oid opno) { HeapTuple tp; tp = SearchSysCache(OPEROID, ObjectIdGetDatum(opno), 0, 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); Oid result; result = optup->oprcom; ReleaseSysCache(tp); return result; } else return InvalidOid; } /* * get_negator * * Returns the corresponding negator of an operator. */ Oid get_negator(Oid opno) { HeapTuple tp; tp = SearchSysCache(OPEROID, ObjectIdGetDatum(opno), 0, 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); Oid result; result = optup->oprnegate; ReleaseSysCache(tp); return result; } else return InvalidOid; } /* * get_oprrest * * Returns procedure id for computing selectivity of an operator. */ RegProcedure get_oprrest(Oid opno) { HeapTuple tp; tp = SearchSysCache(OPEROID, ObjectIdGetDatum(opno), 0, 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); RegProcedure result; result = optup->oprrest; ReleaseSysCache(tp); return result; } else return (RegProcedure) InvalidOid; } /* * get_oprjoin * * Returns procedure id for computing selectivity of a join. */ RegProcedure get_oprjoin(Oid opno) { HeapTuple tp; tp = SearchSysCache(OPEROID, ObjectIdGetDatum(opno), 0, 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); RegProcedure result; result = optup->oprjoin; ReleaseSysCache(tp); return result; } else return (RegProcedure) InvalidOid; } /* ---------- FUNCTION CACHE ---------- */ /* * get_func_rettype * Given procedure id, return the function's result type. */ Oid get_func_rettype(Oid funcid) { HeapTuple tp; Oid result; tp = SearchSysCache(PROCOID, ObjectIdGetDatum(funcid), 0, 0, 0); if (!HeapTupleIsValid(tp)) elog(ERROR, "Function OID %u does not exist", funcid); result = ((Form_pg_proc) GETSTRUCT(tp))->prorettype; 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, "Function OID %u does not exist", 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); } #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_rel_name * Returns the name of a given relation. * * Returns a palloc'd copy of the string, or NULL if no such relation. * * NOTE: since relation name is not unique, be wary of code that uses this * for anything except preparing error messages. */ char * get_rel_name(Oid relid) { HeapTuple tp; tp = SearchSysCache(RELOID, ObjectIdGetDatum(relid), 0, 0, 0); if (HeapTupleIsValid(tp)) { Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp); char *result; result = pstrdup(NameStr(reltup->relname)); ReleaseSysCache(tp); return result; } else return NULL; } /* * get_rel_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; } /* ---------- 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); } #ifdef NOT_USED char get_typalign(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->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_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, "get_typdefault: failed to lookup 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(type->typelem), Int32GetDatum(-1)); /* Build a Const node containing the value */ expr = (Node *) makeConst(typid, type->typlen, datum, false, type->typbyval, false, /* not a set */ false); 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, "getBaseType: failed to lookup 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 named relation * or the generic type 'relation'. * It returns the null char if the cache lookup fails... */ #ifdef NOT_USED 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); char result; result = typtup->typtype; ReleaseSysCache(tp); return result; } else return '\0'; } #endif /* ---------- 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; FmgrInfo inputproc; Oid typelem; 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, "get_attstatsslot: stavalues is null"); statarray = DatumGetArrayTypeP(val); /* * Do initial examination of the array. This produces a list of * text Datums --- ie, pointers into the text array value. */ deconstruct_array(statarray, false, -1, 'i', values, nvalues); narrayelem = *nvalues; /* * We now need to replace each text Datum by its internal * equivalent. * * Get the type input proc and typelem for the column datatype. */ typeTuple = SearchSysCache(TYPEOID, ObjectIdGetDatum(atttype), 0, 0, 0); if (!HeapTupleIsValid(typeTuple)) elog(ERROR, "get_attstatsslot: Cache lookup failed for type %u", atttype); fmgr_info(((Form_pg_type) GETSTRUCT(typeTuple))->typinput, &inputproc); typelem = ((Form_pg_type) GETSTRUCT(typeTuple))->typelem; ReleaseSysCache(typeTuple); /* * Do the conversions. The palloc'd array of Datums is reused in * place. */ for (j = 0; j < narrayelem; j++) { char *strval; strval = DatumGetCString(DirectFunctionCall1(textout, (*values)[j])); (*values)[j] = FunctionCall3(&inputproc, CStringGetDatum(strval), ObjectIdGetDatum(typelem), Int32GetDatum(atttypmod)); pfree(strval); } /* * 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, "get_attstatsslot: 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_SIZE(statarray) != (ARR_OVERHEAD(1) + narrayelem * sizeof(float4))) elog(ERROR, "get_attstatsslot: stanumbers is bogus"); *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 ... */ int32 get_usesysid(const char *username) { int32 result; HeapTuple userTup; userTup = SearchSysCache(SHADOWNAME, PointerGetDatum(username), 0, 0, 0); if (!HeapTupleIsValid(userTup)) elog(ERROR, "user \"%s\" does not exist", username); result = ((Form_pg_shadow) GETSTRUCT(userTup))->usesysid; ReleaseSysCache(userTup); return result; }