mirror of
https://github.com/postgres/postgres.git
synced 2025-05-08 07:21:33 +03:00
qualified operator names directly, for example CREATE OPERATOR myschema.+ ( ... ). To qualify an operator name in an expression you need to write OPERATOR(myschema.+) (thanks to Peter for suggesting an escape hatch). I also took advantage of having to reformat pg_operator to fix something that'd been bugging me for a while: mergejoinable operators should have explicit links to the associated cross-data-type comparison operators, rather than hardwiring an assumption that they are named < and >.
1316 lines
28 KiB
C
1316 lines
28 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* 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.70 2002/04/16 23:08:11 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.
|
|
*/
|
|
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, "op_mergejoin_crossops: operator %u not found", opno);
|
|
optup = (Form_pg_operator) GETSTRUCT(tp);
|
|
*ltop = optup->oprltcmpop;
|
|
*gtop = optup->oprgtcmpop;
|
|
ReleaseSysCache(tp);
|
|
|
|
/* Check < op provided */
|
|
if (!OidIsValid(*ltop))
|
|
elog(ERROR, "op_mergejoin_crossops: mergejoin operator %u has no matching < operator",
|
|
opno);
|
|
if (ltproc)
|
|
*ltproc = get_opcode(*ltop);
|
|
|
|
/* Check > op provided */
|
|
if (!OidIsValid(*gtop))
|
|
elog(ERROR, "op_mergejoin_crossops: mergejoin operator %u has no matching > operator",
|
|
opno);
|
|
if (gtproc)
|
|
*gtproc = get_opcode(*gtop);
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|