Newer
Older
/*-------------------------------------------------------------------------
*
* lsyscache.c
* Convenience routines for common queries in the system catalog cache.
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.135 2006/07/14 14:52:25 momjian Exp $
* Eventually, the index information should go through here, too.
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/hash.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_type.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
/* ---------- AMOP CACHES ---------- */
* op_in_opclass
* Return t iff operator 'opno' is in operator class 'opclass'.
op_in_opclass(Oid opno, Oid opclass)
return SearchSysCacheExists(AMOPOPID,
ObjectIdGetDatum(opno),
ObjectIdGetDatum(opclass),
0, 0);
}
/*
* 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),
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;
}
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/*
* get_op_btree_interpretation
* Given an operator's OID, find out which btree opclasses it belongs to,
* and what strategy number it has within each one. The results are
* returned as an OID list and a parallel integer list.
*
* In addition to the normal btree operators, we consider a <> operator to be
* a "member" of an opclass if its negator is the opclass' equality operator.
* ROWCOMPARE_NE is returned as the strategy number for this case.
*/
void
get_op_btree_interpretation(Oid opno, List **opclasses, List **opstrats)
{
Oid lefttype,
righttype;
CatCList *catlist;
bool op_negated;
int i;
*opclasses = NIL;
*opstrats = NIL;
/*
* Get the nominal left-hand input type of the operator; we will ignore
* opclasses that don't have that as the expected input datatype. This
* is a kluge to avoid being confused by binary-compatible opclasses
* (such as text_ops and varchar_ops, which share the same operators).
*/
op_input_types(opno, &lefttype, &righttype);
Assert(OidIsValid(lefttype));
/*
* Find all the pg_amop entries containing the operator.
*/
catlist = SearchSysCacheList(AMOPOPID, 1,
ObjectIdGetDatum(opno),
0, 0, 0);
/*
* If we can't find any opclass containing the op, perhaps it is a
* <> operator. See if it has a negator that is in an opclass.
*/
op_negated = false;
if (catlist->n_members == 0)
{
Oid op_negator = get_negator(opno);
if (OidIsValid(op_negator))
{
op_negated = true;
ReleaseSysCacheList(catlist);
catlist = SearchSysCacheList(AMOPOPID, 1,
ObjectIdGetDatum(op_negator),
0, 0, 0);
}
}
/* Now search the opclasses */
for (i = 0; i < catlist->n_members; i++)
{
HeapTuple op_tuple = &catlist->members[i]->tuple;
Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple);
Oid opclass_id;
StrategyNumber op_strategy;
opclass_id = op_form->amopclaid;
/* must be btree */
if (!opclass_is_btree(opclass_id))
continue;
/* must match operator input type exactly */
if (get_opclass_input_type(opclass_id) != lefttype)
continue;
/* Get the operator's btree strategy number */
op_strategy = (StrategyNumber) op_form->amopstrategy;
Assert(op_strategy >= 1 && op_strategy <= 5);
if (op_negated)
{
/* Only consider negators that are = */
if (op_strategy != BTEqualStrategyNumber)
continue;
op_strategy = ROWCOMPARE_NE;
}
*opclasses = lappend_oid(*opclasses, opclass_id);
*opstrats = lappend_int(*opstrats, op_strategy);
}
ReleaseSysCacheList(catlist);
}
/* ---------- AMPROC CACHES ---------- */
/*
* 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),
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.
get_attname(Oid relid, AttrNumber attnum)
{
tp = SearchSysCache(ATTNUM,
ObjectIdGetDatum(relid),
Int16GetDatum(attnum),
0, 0);
if (HeapTupleIsValid(tp))
{
Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
result = pstrdup(NameStr(att_tup->attname));
ReleaseSysCache(tp);
return result;
/*
* 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).
get_attnum(Oid relid, const char *attname)
tp = SearchSysCacheAttName(relid, attname);
if (HeapTupleIsValid(tp))
{
Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
AttrNumber result;
result = att_tup->attnum;
ReleaseSysCache(tp);
return result;
* 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)
{
tp = SearchSysCache(ATTNUM,
ObjectIdGetDatum(relid),
Int16GetDatum(attnum),
0, 0);
if (HeapTupleIsValid(tp))
{
Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
result = att_tup->atttypid;
ReleaseSysCache(tp);
return result;
return InvalidOid;
* get_atttypmod
*
* Given the relation id and the attribute number,
* return the "atttypmod" field from the attribute relation.
*/
tp = SearchSysCache(ATTNUM,
ObjectIdGetDatum(relid),
Int16GetDatum(attnum),
0, 0);
if (HeapTupleIsValid(tp))
{
Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
result = att_tup->atttypmod;
ReleaseSysCache(tp);
return result;
/*
* 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 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 ---------- */
/* watch this space...
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
/* ---------- 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);
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
return result;
}
/*
* opclass_is_default
*
* Returns TRUE iff the specified opclass is the default for its
* index access method and input data type.
*/
bool
opclass_is_default(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->opcdefault;
ReleaseSysCache(tp);
return result;
}
/*
* get_opclass_input_type
*
* Returns the OID of the datatype the opclass indexes.
*/
Oid
get_opclass_input_type(Oid opclass)
{
HeapTuple tp;
Form_pg_opclass cla_tup;
Oid 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->opcintype;
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)
{
tp = SearchSysCache(OPEROID,
ObjectIdGetDatum(opno),
0, 0, 0);
if (HeapTupleIsValid(tp))
{
Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
result = optup->oprcode;
ReleaseSysCache(tp);
return result;
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.
get_opname(Oid opno)
{
tp = SearchSysCache(OPEROID,
ObjectIdGetDatum(opno),
0, 0, 0);
if (HeapTupleIsValid(tp))
Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
result = pstrdup(NameStr(optup->oprname));
ReleaseSysCache(tp);
return result;
else
return NULL;
/*
* op_input_types
*
* 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.
op_mergejoinable(Oid opno, Oid *leftOp, Oid *rightOp)
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)
*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, "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.
bool
op_hashjoinable(Oid opno)
bool result = false;
tp = SearchSysCache(OPEROID,
ObjectIdGetDatum(opno),
0, 0, 0);
if (HeapTupleIsValid(tp))
{
Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
result = optup->oprcanhash;
ReleaseSysCache(tp);
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);
}
* 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 %u does not exist", opno);
return func_volatile((Oid) funcid);
* get_commutator
*
* Returns the corresponding commutator of an operator.
*/
Oid
get_commutator(Oid opno)
{
tp = SearchSysCache(OPEROID,
ObjectIdGetDatum(opno),
0, 0, 0);
if (HeapTupleIsValid(tp))
{
Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
result = optup->oprcom;
ReleaseSysCache(tp);
return result;
return InvalidOid;
* get_negator
*
* Returns the corresponding negator of an operator.
*/
Oid
get_negator(Oid opno)
{
tp = SearchSysCache(OPEROID,
ObjectIdGetDatum(opno),
0, 0, 0);
if (HeapTupleIsValid(tp))
{
Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
result = optup->oprnegate;
ReleaseSysCache(tp);
return result;
return InvalidOid;
* get_oprrest
*
* Returns procedure id for computing selectivity of an operator.
*/
RegProcedure
get_oprrest(Oid opno)
{
tp = SearchSysCache(OPEROID,
ObjectIdGetDatum(opno),
0, 0, 0);
if (HeapTupleIsValid(tp))
{
Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
result = optup->oprrest;
ReleaseSysCache(tp);
return result;
return (RegProcedure) InvalidOid;
* get_oprjoin
*
* Returns procedure id for computing selectivity of a join.
*/
RegProcedure
get_oprjoin(Oid opno)
{
tp = SearchSysCache(OPEROID,
ObjectIdGetDatum(opno),
0, 0, 0);
if (HeapTupleIsValid(tp))
{
Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
result = optup->oprjoin;
ReleaseSysCache(tp);
return result;
return (RegProcedure) InvalidOid;
/* ---------- FUNCTION CACHE ---------- */
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
/*
* 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.
*/
char *
get_func_name(Oid funcid)
{
HeapTuple tp;
tp = SearchSysCache(PROCOID,
ObjectIdGetDatum(funcid),
0, 0, 0);
if (HeapTupleIsValid(tp))
{
Form_pg_proc functup = (Form_pg_proc) GETSTRUCT(tp);
char *result;
result = pstrdup(NameStr(functup->proname));
ReleaseSysCache(tp);
return result;
}
else
return NULL;
}
/*
* 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, "cache lookup failed for function %u", funcid);
result = ((Form_pg_proc) GETSTRUCT(tp))->prorettype;
ReleaseSysCache(tp);
return result;
/*
* get_func_nargs
* Given procedure id, return the number of arguments.
*/
int
get_func_nargs(Oid funcid)
{
HeapTuple tp;
int 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))->pronargs;
ReleaseSysCache(tp);
return result;
}
/*
* 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.
*/
Oid
get_func_signature(Oid funcid, Oid **argtypes, int *nargs)
Oid result;
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;
}