mirror of
https://github.com/postgres/postgres.git
synced 2025-05-20 05:13:53 +03:00
- domain.patch -> source patch against pgsql in cvs - drop_domain.sgml and create_domain.sgml -> New doc/src/sgml/ref docs - dominfo.txt -> basic domain related queries I used for testing [ ADDED TO /doc] Enables domains of array elements -> CREATE DOMAIN dom int4[3][2]; Uses a typbasetype column to describe the origin of the domain. Copies data to attnotnull rather than processing in execMain(). Some documentation differences from earlier. If this is approved, I'll start working on pg_dump, and a \dD <domain> option in psql, and regression tests. I don't really feel like doing those until the system table structure settles for pg_type. CHECKS when added, will also be copied to to the table attributes. FK Constraints (if I ever figure out how) will be done similarly. Both will lbe handled by MergeDomainAttributes() which is called shortly before MergeAttributes(). Rod Taylor
1186 lines
26 KiB
C
1186 lines
26 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.61 2002/03/06 20:34:54 momjian 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_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 "parser/parse_coerce.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_iscachable
|
|
*
|
|
* Get the proiscachable flag for the operator's underlying function.
|
|
*/
|
|
bool
|
|
op_iscachable(Oid opno)
|
|
{
|
|
RegProcedure funcid = get_opcode(opno);
|
|
|
|
if (funcid == (RegProcedure) InvalidOid)
|
|
elog(ERROR, "Operator OID %u does not exist", opno);
|
|
|
|
return func_iscachable((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_iscachable
|
|
* Given procedure id, return the function's proiscachable flag.
|
|
*/
|
|
bool
|
|
func_iscachable(Oid funcid)
|
|
{
|
|
HeapTuple tp;
|
|
bool 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))->proiscachable;
|
|
ReleaseSysCache(tp);
|
|
return result;
|
|
}
|
|
|
|
/* ---------- RELATION CACHE ---------- */
|
|
|
|
#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.
|
|
*
|
|
* Note: returns a palloc'd copy of the string, or NULL if no such relation.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/* ---------- TYPE CACHE ---------- */
|
|
|
|
/*
|
|
* 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.
|
|
* Returns FALSE if there is no default (effectively, default is NULL).
|
|
* The result points to palloc'd storage for pass-by-reference types.
|
|
*/
|
|
Node *
|
|
get_typdefault(Oid typid, int32 atttypmod)
|
|
{
|
|
HeapTuple typeTuple;
|
|
Form_pg_type type;
|
|
Oid typinput;
|
|
Oid typbasetype;
|
|
char typtype;
|
|
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);
|
|
|
|
typinput = type->typinput;
|
|
typbasetype = type->typbasetype;
|
|
typtype = type->typtype;
|
|
|
|
/*
|
|
* typdefaultbin is potentially null, so don't try to access it as a
|
|
* struct field. Must do it the hard way with SysCacheGetAttr.
|
|
*/
|
|
datum = SysCacheGetAttr(TYPEOID,
|
|
typeTuple,
|
|
Anum_pg_type_typdefaultbin,
|
|
&isNull);
|
|
|
|
ReleaseSysCache(typeTuple);
|
|
if (isNull)
|
|
return (Node *) NULL;
|
|
|
|
/* Convert Datum to a Node */
|
|
expr = stringToNode(DatumGetCString(
|
|
DirectFunctionCall1(textout, datum)));
|
|
|
|
|
|
/*
|
|
* Ensure we goto the basetype before the domain type.
|
|
*
|
|
* Prevents scenarios like the below from failing:
|
|
* CREATE DOMAIN dom text DEFAULT random();
|
|
*
|
|
*/
|
|
if (typbasetype != InvalidOid) {
|
|
expr = coerce_type(NULL, expr, typid,
|
|
typbasetype, atttypmod);
|
|
}
|
|
|
|
|
|
return expr;
|
|
}
|
|
|
|
/*
|
|
* 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_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;
|
|
}
|