postgresql/src/backend/parser/catalog_utils.c

1528 lines
39 KiB
C

/*-------------------------------------------------------------------------
*
* catalog_utils.c--
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/Attic/catalog_utils.c,v 1.13 1996/12/11 03:17:49 bryanh Exp $
*
*-------------------------------------------------------------------------
*/
#include <string.h>
#include "postgres.h"
#include "lib/dllist.h"
#include "utils/datum.h"
#include "utils/builtins.h"
#include "utils/elog.h"
#include "utils/palloc.h"
#include "fmgr.h"
#include "nodes/pg_list.h"
#include "nodes/parsenodes.h"
#include "utils/syscache.h"
#include "catalog/catname.h"
#include "parser/catalog_utils.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
#include "catalog/pg_proc.h"
#include "catalog/indexing.h"
#include "catalog/catname.h"
#include "access/skey.h"
#include "access/relscan.h"
#include "access/tupdesc.h"
#include "access/htup.h"
#include "access/heapam.h"
#include "access/genam.h"
#include "access/itup.h"
#include "access/tupmacs.h"
#include "storage/buf.h"
#include "storage/bufmgr.h"
#include "utils/lsyscache.h"
#include "storage/lmgr.h"
#include "port-protos.h" /* strdup() */
struct {
char *field;
int code;
} special_attr[] = {
{ "ctid", SelfItemPointerAttributeNumber },
{ "oid", ObjectIdAttributeNumber },
{ "xmin", MinTransactionIdAttributeNumber },
{ "cmin", MinCommandIdAttributeNumber },
{ "xmax", MaxTransactionIdAttributeNumber },
{ "cmax", MaxCommandIdAttributeNumber },
{ "chain", ChainItemPointerAttributeNumber },
{ "anchor", AnchorItemPointerAttributeNumber },
{ "tmin", MinAbsoluteTimeAttributeNumber },
{ "tmax", MaxAbsoluteTimeAttributeNumber },
{ "vtype", VersionTypeAttributeNumber }
};
#define SPECIALS (sizeof(special_attr)/sizeof(*special_attr))
static char *attnum_type[SPECIALS] = {
"tid",
"oid",
"xid",
"cid",
"xid",
"cid",
"tid",
"tid",
"abstime",
"abstime",
"char"
};
#define MAXFARGS 8 /* max # args to a c or postquel function */
/*
* This structure is used to explore the inheritance hierarchy above
* nodes in the type tree in order to disambiguate among polymorphic
* functions.
*/
typedef struct _InhPaths {
int nsupers; /* number of superclasses */
Oid self; /* this class */
Oid *supervec; /* vector of superclasses */
} InhPaths;
/*
* This structure holds a list of possible functions or operators that
* agree with the known name and argument types of the function/operator.
*/
typedef struct _CandidateList {
Oid *args;
struct _CandidateList *next;
} *CandidateList;
static Oid **argtype_inherit(int nargs, Oid *oid_array);
static Oid **genxprod(InhPaths *arginh, int nargs);
static int findsupers(Oid relid, Oid **supervec);
static bool is_lowercase(char *string);
static void make_lowercase(char *string);
/* check to see if a type id is valid,
* returns true if it is. By using this call before calling
* get_id_type or get_id_typname, more meaningful error messages
* can be produced because the caller typically has more context of
* what's going on - jolly
*/
bool
check_typeid(Oid id)
{
return (SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(id),
0,0,0) != NULL);
}
/* return a Type structure, given an typid */
Type
get_id_type(Oid id)
{
HeapTuple tup;
if (!(tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(id),
0,0,0))) {
elog ( WARN, "type id lookup of %ud failed", id);
return(NULL);
}
return((Type) tup);
}
/* return a type name, given a typeid */
char*
get_id_typname(Oid id)
{
HeapTuple tup;
TypeTupleForm typetuple;
if (!(tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(id),
0,0,0))) {
elog ( WARN, "type id lookup of %ud failed", id);
return(NULL);
}
typetuple = (TypeTupleForm)GETSTRUCT(tup);
return (typetuple->typname).data;
}
/* return a Type structure, given type name */
Type
type(char *s)
{
HeapTuple tup;
if (s == NULL) {
elog ( WARN , "type(): Null type" );
}
if (!(tup = SearchSysCacheTuple(TYPNAME, PointerGetDatum(s), 0,0,0))) {
elog (WARN , "type name lookup of %s failed", s);
}
return((Type) tup);
}
/* given attribute id, return type of that attribute */
/* XXX Special case for pseudo-attributes is a hack */
Oid
att_typeid(Relation rd, int attid)
{
if (attid < 0) {
return(typeid(type(attnum_type[-attid-1])));
}
/* -1 because varattno (where attid comes from) returns one
more than index */
return(rd->rd_att->attrs[attid-1]->atttypid);
}
int
att_attnelems(Relation rd, int attid)
{
return(rd->rd_att->attrs[attid-1]->attnelems);
}
/* given type, return the type OID */
Oid
typeid(Type tp)
{
if (tp == NULL) {
elog ( WARN , "typeid() called with NULL type struct");
}
return(tp->t_oid);
}
/* given type (as type struct), return the length of type */
int16
tlen(Type t)
{
TypeTupleForm typ;
typ = (TypeTupleForm)GETSTRUCT(t);
return(typ->typlen);
}
/* given type (as type struct), return the value of its 'byval' attribute.*/
bool
tbyval(Type t)
{
TypeTupleForm typ;
typ = (TypeTupleForm)GETSTRUCT(t);
return(typ->typbyval);
}
/* given type (as type struct), return the name of type */
char*
tname(Type t)
{
TypeTupleForm typ;
typ = (TypeTupleForm)GETSTRUCT(t);
return (typ->typname).data;
}
/* given type (as type struct), return wether type is passed by value */
int
tbyvalue(Type t)
{
TypeTupleForm typ;
typ = (TypeTupleForm) GETSTRUCT(t);
return(typ->typbyval);
}
/* given a type, return its typetype ('c' for 'c'atalog types) */
static char
typetypetype(Type t)
{
TypeTupleForm typ;
typ = (TypeTupleForm) GETSTRUCT(t);
return(typ->typtype);
}
/* given operator, return the operator OID */
Oid
oprid(Operator op)
{
return(op->t_oid);
}
/*
* given opname, leftTypeId and rightTypeId,
* find all possible (arg1, arg2) pairs for which an operator named
* opname exists, such that leftTypeId can be coerced to arg1 and
* rightTypeId can be coerced to arg2
*/
static int
binary_oper_get_candidates(char *opname,
Oid leftTypeId,
Oid rightTypeId,
CandidateList *candidates)
{
CandidateList current_candidate;
Relation pg_operator_desc;
HeapScanDesc pg_operator_scan;
HeapTuple tup;
OperatorTupleForm oper;
Buffer buffer;
int nkeys;
int ncandidates = 0;
ScanKeyData opKey[3];
*candidates = NULL;
ScanKeyEntryInitialize(&opKey[0], 0,
Anum_pg_operator_oprname,
NameEqualRegProcedure,
NameGetDatum(opname));
ScanKeyEntryInitialize(&opKey[1], 0,
Anum_pg_operator_oprkind,
CharacterEqualRegProcedure,
CharGetDatum('b'));
if (leftTypeId == UNKNOWNOID) {
if (rightTypeId == UNKNOWNOID) {
nkeys = 2;
} else {
nkeys = 3;
ScanKeyEntryInitialize(&opKey[2], 0,
Anum_pg_operator_oprright,
ObjectIdEqualRegProcedure,
ObjectIdGetDatum(rightTypeId));
}
} else if (rightTypeId == UNKNOWNOID) {
nkeys = 3;
ScanKeyEntryInitialize(&opKey[2], 0,
Anum_pg_operator_oprleft,
ObjectIdEqualRegProcedure,
ObjectIdGetDatum(leftTypeId));
} else {
/* currently only "unknown" can be coerced */
return 0;
}
pg_operator_desc = heap_openr(OperatorRelationName);
pg_operator_scan = heap_beginscan(pg_operator_desc,
0,
SelfTimeQual,
nkeys,
opKey);
do {
tup = heap_getnext(pg_operator_scan, 0, &buffer);
if (HeapTupleIsValid(tup)) {
current_candidate = (CandidateList)palloc(sizeof(struct _CandidateList));
current_candidate->args = (Oid *)palloc(2 * sizeof(Oid));
oper = (OperatorTupleForm)GETSTRUCT(tup);
current_candidate->args[0] = oper->oprleft;
current_candidate->args[1] = oper->oprright;
current_candidate->next = *candidates;
*candidates = current_candidate;
ncandidates++;
ReleaseBuffer(buffer);
}
} while(HeapTupleIsValid(tup));
heap_endscan(pg_operator_scan);
heap_close(pg_operator_desc);
return ncandidates;
}
/*
* equivalentOpersAfterPromotion -
* checks if a list of candidate operators obtained from
* binary_oper_get_candidates() contain equivalent operators. If
* this routine is called, we have more than 1 candidate and need to
* decided whether to pick one of them. This routine returns true if
* the all the candidates operate on the same data types after
* promotion (int2, int4, float4 -> float8).
*/
static bool
equivalentOpersAfterPromotion(CandidateList candidates)
{
CandidateList result;
CandidateList promotedCandidates = NULL;
Oid leftarg, rightarg;
for (result = candidates; result != NULL; result = result->next) {
CandidateList c;
c = (CandidateList)palloc(sizeof(*c));
c->args = (Oid *)palloc(2 * sizeof(Oid));
switch (result->args[0]) {
case FLOAT4OID:
case INT4OID:
case INT2OID:
c->args[0] = FLOAT8OID;
break;
default:
c->args[0] = result->args[0];
break;
}
switch (result->args[1]) {
case FLOAT4OID:
case INT4OID:
case INT2OID:
c->args[1] = FLOAT8OID;
break;
default:
c->args[1] = result->args[1];
break;
}
c->next = promotedCandidates;
promotedCandidates = c;
}
/* if we get called, we have more than 1 candidates so we can do the
following safely */
leftarg = promotedCandidates->args[0];
rightarg = promotedCandidates->args[1];
for (result=promotedCandidates->next; result!=NULL; result=result->next) {
if (result->args[0]!=leftarg || result->args[1]!=rightarg)
/*
* this list contains operators that operate on different
* data types even after promotion. Hence we can't decide on
* which one to pick. The user must do explicit type casting.
*/
return FALSE;
}
/* all the candidates are equivalent in the following sense: they operate
on equivalent data types and picking any one of them is as good. */
return TRUE;
}
/*
* given a choice of argument type pairs for a binary operator,
* try to choose a default pair
*/
static CandidateList
binary_oper_select_candidate(Oid arg1,
Oid arg2,
CandidateList candidates)
{
CandidateList result;
/*
* if both are "unknown", there is no way to select a candidate
*
* current wisdom holds that the default operator should be one
* in which both operands have the same type (there will only
* be one such operator)
*
* 7.27.93 - I have decided not to do this; it's too hard to
* justify, and it's easy enough to typecast explicitly -avi
* [the rest of this routine were commented out since then -ay]
*/
if (arg1 == UNKNOWNOID && arg2 == UNKNOWNOID)
return (NULL);
/*
* 6/23/95 - I don't complete agree with avi. In particular, casting
* floats is a pain for users. Whatever the rationale behind not doing
* this is, I need the following special case to work.
*
* In the WHERE clause of a query, if a float is specified without
* quotes, we treat it as float8. I added the float48* operators so
* that we can operate on float4 and float8. But now we have more
* than one matching operator if the right arg is unknown (eg. float
* specified with quotes). This break some stuff in the regression
* test where there are floats in quotes not properly casted. Below
* is the solution. In addition to requiring the operator operates
* on the same type for both operands [as in the code Avi originally
* commented out], we also require that the operators be equivalent
* in some sense. (see equivalentOpersAfterPromotion for details.)
* - ay 6/95
*/
if (!equivalentOpersAfterPromotion(candidates))
return NULL;
/* if we get here, any one will do but we're more picky and require
both operands be the same. */
for (result = candidates; result != NULL; result = result->next) {
if (result->args[0] == result->args[1])
return result;
}
return (NULL);
}
/* Given operator, types of arg1, and arg2, return oper struct */
/* arg1, arg2 --typeids */
Operator
oper(char *op, Oid arg1, Oid arg2)
{
HeapTuple tup;
CandidateList candidates;
int ncandidates;
if (!arg2) arg2=arg1;
if (!arg1) arg1=arg2;
if (!(tup = SearchSysCacheTuple(OPRNAME,
PointerGetDatum(op),
ObjectIdGetDatum(arg1),
ObjectIdGetDatum(arg2),
Int8GetDatum('b')))) {
ncandidates = binary_oper_get_candidates(op, arg1, arg2, &candidates);
if (ncandidates == 0) {
/*
* no operators of the desired types found
*/
op_error(op, arg1, arg2);
return(NULL);
} else if (ncandidates == 1) {
/*
* exactly one operator of the desired types found
*/
tup = SearchSysCacheTuple(OPRNAME,
PointerGetDatum(op),
ObjectIdGetDatum(candidates->args[0]),
ObjectIdGetDatum(candidates->args[1]),
Int8GetDatum('b'));
Assert(HeapTupleIsValid(tup));
} else {
/*
* multiple operators of the desired types found
*/
candidates = binary_oper_select_candidate(arg1, arg2, candidates);
if (candidates != NULL) {
/* we chose one of them */
tup = SearchSysCacheTuple(OPRNAME,
PointerGetDatum(op),
ObjectIdGetDatum(candidates->args[0]),
ObjectIdGetDatum(candidates->args[1]),
Int8GetDatum('b'));
Assert(HeapTupleIsValid(tup));
} else {
Type tp1, tp2;
/* we chose none of them */
tp1 = get_id_type(arg1);
tp2 = get_id_type(arg2);
elog(NOTICE, "there is more than one operator %s for types", op);
elog(NOTICE, "%s and %s. You will have to retype this query",
tname(tp1), tname(tp2));
elog(WARN, "using an explicit cast");
return(NULL);
}
}
}
return((Operator) tup);
}
/*
* given opname and typeId, find all possible types for which
* a right/left unary operator named opname exists,
* such that typeId can be coerced to it
*/
static int
unary_oper_get_candidates(char *op,
Oid typeId,
CandidateList *candidates,
char rightleft)
{
CandidateList current_candidate;
Relation pg_operator_desc;
HeapScanDesc pg_operator_scan;
HeapTuple tup;
OperatorTupleForm oper;
Buffer buffer;
int ncandidates = 0;
static ScanKeyData opKey[2] = {
{ 0, Anum_pg_operator_oprname, NameEqualRegProcedure },
{ 0, Anum_pg_operator_oprkind, CharacterEqualRegProcedure } };
*candidates = NULL;
fmgr_info(NameEqualRegProcedure, (func_ptr *) &opKey[0].sk_func,
&opKey[0].sk_nargs);
opKey[0].sk_argument = NameGetDatum(op);
fmgr_info(CharacterEqualRegProcedure, (func_ptr *) &opKey[1].sk_func,
&opKey[1].sk_nargs);
opKey[1].sk_argument = CharGetDatum(rightleft);
/* currently, only "unknown" can be coerced */
if (typeId != UNKNOWNOID) {
return 0;
}
pg_operator_desc = heap_openr(OperatorRelationName);
pg_operator_scan = heap_beginscan(pg_operator_desc,
0,
SelfTimeQual,
2,
opKey);
do {
tup = heap_getnext(pg_operator_scan, 0, &buffer);
if (HeapTupleIsValid(tup)) {
current_candidate = (CandidateList)palloc(sizeof(struct _CandidateList));
current_candidate->args = (Oid *)palloc(sizeof(Oid));
oper = (OperatorTupleForm)GETSTRUCT(tup);
if (rightleft == 'r')
current_candidate->args[0] = oper->oprleft;
else
current_candidate->args[0] = oper->oprright;
current_candidate->next = *candidates;
*candidates = current_candidate;
ncandidates++;
ReleaseBuffer(buffer);
}
} while(HeapTupleIsValid(tup));
heap_endscan(pg_operator_scan);
heap_close(pg_operator_desc);
return ncandidates;
}
/* Given unary right-side operator (operator on right), return oper struct */
/* arg-- type id */
Operator
right_oper(char *op, Oid arg)
{
HeapTuple tup;
CandidateList candidates;
int ncandidates;
/*
if (!OpCache) {
init_op_cache();
}
*/
if (!(tup = SearchSysCacheTuple(OPRNAME,
PointerGetDatum(op),
ObjectIdGetDatum(arg),
ObjectIdGetDatum(InvalidOid),
Int8GetDatum('r')))) {
ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'r');
if (ncandidates == 0) {
elog ( WARN ,
"Can't find right op: %s for type %d", op, arg );
return(NULL);
}
else if (ncandidates == 1) {
tup = SearchSysCacheTuple(OPRNAME,
PointerGetDatum(op),
ObjectIdGetDatum(candidates->args[0]),
ObjectIdGetDatum(InvalidOid),
Int8GetDatum('r'));
Assert(HeapTupleIsValid(tup));
}
else {
elog(NOTICE, "there is more than one right operator %s", op);
elog(NOTICE, "you will have to retype this query");
elog(WARN, "using an explicit cast");
return(NULL);
}
}
return((Operator) tup);
}
/* Given unary left-side operator (operator on left), return oper struct */
/* arg--type id */
Operator
left_oper(char *op, Oid arg)
{
HeapTuple tup;
CandidateList candidates;
int ncandidates;
/*
if (!OpCache) {
init_op_cache();
}
*/
if (!(tup = SearchSysCacheTuple(OPRNAME,
PointerGetDatum(op),
ObjectIdGetDatum(InvalidOid),
ObjectIdGetDatum(arg),
Int8GetDatum('l')))) {
ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'l');
if (ncandidates == 0) {
elog ( WARN ,
"Can't find left op: %s for type %d", op, arg );
return(NULL);
}
else if (ncandidates == 1) {
tup = SearchSysCacheTuple(OPRNAME,
PointerGetDatum(op),
ObjectIdGetDatum(InvalidOid),
ObjectIdGetDatum(candidates->args[0]),
Int8GetDatum('l'));
Assert(HeapTupleIsValid(tup));
}
else {
elog(NOTICE, "there is more than one left operator %s", op);
elog(NOTICE, "you will have to retype this query");
elog(WARN, "using an explicit cast");
return(NULL);
}
}
return((Operator) tup);
}
/* given range variable, return id of variable */
int
varattno(Relation rd, char *a)
{
int i;
for (i = 0; i < rd->rd_rel->relnatts; i++) {
if (!namestrcmp(&(rd->rd_att->attrs[i]->attname), a)) {
return(i+1);
}
}
for (i = 0; i < SPECIALS; i++) {
if (!strcmp(special_attr[i].field, a)) {
return(special_attr[i].code);
}
}
elog(WARN,"Relation %s does not have attribute %s\n",
RelationGetRelationName(rd), a );
return(-1);
}
/* Given range variable, return whether attribute of this name
* is a set.
* NOTE the ASSUMPTION here that no system attributes are, or ever
* will be, sets.
*/
bool
varisset(Relation rd, char *name)
{
int i;
/* First check if this is a system attribute */
for (i = 0; i < SPECIALS; i++) {
if (! strcmp(special_attr[i].field, name)) {
return(false); /* no sys attr is a set */
}
}
return (get_attisset(rd->rd_id, name));
}
/* given range variable, return id of variable */
int
nf_varattno(Relation rd, char *a)
{
int i;
for (i = 0; i < rd->rd_rel->relnatts; i++) {
if (!namestrcmp(&(rd->rd_att->attrs[i]->attname), a)) {
return(i+1);
}
}
for (i = 0; i < SPECIALS; i++) {
if (!strcmp(special_attr[i].field, a)) {
return(special_attr[i].code);
}
}
return InvalidAttrNumber;
}
/*-------------
* given an attribute number and a relation, return its relation name
*/
char*
getAttrName(Relation rd, int attrno)
{
char *name;
int i;
if (attrno<0) {
for (i = 0; i < SPECIALS; i++) {
if (special_attr[i].code == attrno) {
name = special_attr[i].field;
return(name);
}
}
elog(WARN, "Illegal attr no %d for relation %s\n",
attrno, RelationGetRelationName(rd));
} else if (attrno >=1 && attrno<= RelationGetNumberOfAttributes(rd)) {
name = (rd->rd_att->attrs[attrno-1]->attname).data;
return(name);
} else {
elog(WARN, "Illegal attr no %d for relation %s\n",
attrno, RelationGetRelationName(rd));
}
/*
* Shouldn't get here, but we want lint to be happy...
*/
return(NULL);
}
/* Given a typename and value, returns the ascii form of the value */
char *
outstr(char *typename, /* Name of type of value */
char *value) /* Could be of any type */
{
TypeTupleForm tp;
Oid op;
tp = (TypeTupleForm ) GETSTRUCT(type(typename));
op = tp->typoutput;
return((char *) fmgr(op, value));
}
/* Given a Type and a string, return the internal form of that string */
char *
instr2(Type tp, char *string, int typlen)
{
return(instr1((TypeTupleForm ) GETSTRUCT(tp), string, typlen));
}
/* Given a type structure and a string, returns the internal form of
that string */
char *
instr1(TypeTupleForm tp, char *string, int typlen)
{
Oid op;
Oid typelem;
op = tp->typinput;
typelem = tp->typelem; /* XXX - used for array_in */
/* typlen is for bpcharin() and varcharin() */
return((char *) fmgr(op, string, typelem, typlen));
}
/* Given the attribute type of an array return the arrtribute type of
an element of the array */
Oid
GetArrayElementType(Oid typearray)
{
HeapTuple type_tuple;
TypeTupleForm type_struct_array;
type_tuple = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(typearray),
0,0,0);
if (!HeapTupleIsValid(type_tuple))
elog(WARN, "GetArrayElementType: Cache lookup failed for type %d\n",
typearray);
/* get the array type struct from the type tuple */
type_struct_array = (TypeTupleForm) GETSTRUCT(type_tuple);
if (type_struct_array->typelem == InvalidOid) {
elog(WARN, "GetArrayElementType: type %s is not an array",
(Name)&(type_struct_array->typname.data[0]));
}
return(type_struct_array->typelem);
}
Oid
funcid_get_rettype(Oid funcid)
{
HeapTuple func_tuple = NULL;
Oid funcrettype = (Oid)0;
func_tuple = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(funcid),
0,0,0);
if ( !HeapTupleIsValid ( func_tuple ))
elog (WARN, "function %d does not exist", funcid);
funcrettype = (Oid)
((Form_pg_proc)GETSTRUCT(func_tuple))->prorettype ;
return (funcrettype);
}
/*
* get a list of all argument type vectors for which a function named
* funcname taking nargs arguments exists
*/
static CandidateList
func_get_candidates(char *funcname, int nargs)
{
Relation heapRelation;
Relation idesc;
ScanKeyData skey;
HeapTuple tuple;
IndexScanDesc sd;
RetrieveIndexResult indexRes;
Buffer buffer;
Form_pg_proc pgProcP;
bool bufferUsed = FALSE;
CandidateList candidates = NULL;
CandidateList current_candidate;
int i;
heapRelation = heap_openr(ProcedureRelationName);
ScanKeyEntryInitialize(&skey,
(bits16)0x0,
(AttrNumber)1,
(RegProcedure)NameEqualRegProcedure,
(Datum)funcname);
idesc = index_openr(ProcedureNameIndex);
sd = index_beginscan(idesc, false, 1, &skey);
do {
tuple = (HeapTuple)NULL;
if (bufferUsed) {
ReleaseBuffer(buffer);
bufferUsed = FALSE;
}
indexRes = index_getnext(sd, ForwardScanDirection);
if (indexRes) {
ItemPointer iptr;
iptr = &indexRes->heap_iptr;
tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer);
pfree(indexRes);
if (HeapTupleIsValid(tuple)) {
pgProcP = (Form_pg_proc)GETSTRUCT(tuple);
bufferUsed = TRUE;
if (pgProcP->pronargs == nargs) {
current_candidate = (CandidateList)
palloc(sizeof(struct _CandidateList));
current_candidate->args = (Oid *)
palloc(8 * sizeof(Oid));
memset(current_candidate->args, 0, 8 * sizeof(Oid));
for (i=0; i<nargs; i++) {
current_candidate->args[i] =
pgProcP->proargtypes[i];
}
current_candidate->next = candidates;
candidates = current_candidate;
}
}
}
} while (indexRes);
index_endscan(sd);
index_close(idesc);
heap_close(heapRelation);
return candidates;
}
/*
* can input_typeids be coerced to func_typeids?
*/
static bool
can_coerce(int nargs, Oid *input_typeids, Oid *func_typeids)
{
int i;
Type tp;
/*
* right now, we only coerce "unknown", and we cannot coerce it to a
* relation type
*/
for (i=0; i<nargs; i++) {
if (input_typeids[i] != func_typeids[i]) {
if (input_typeids[i] != UNKNOWNOID || func_typeids[i] == 0)
return false;
tp = get_id_type(input_typeids[i]);
if (typetypetype(tp) == 'c' )
return false;
}
}
return true;
}
/*
* given a list of possible typeid arrays to a function and an array of
* input typeids, produce a shortlist of those function typeid arrays
* that match the input typeids (either exactly or by coercion), and
* return the number of such arrays
*/
static int
match_argtypes(int nargs,
Oid *input_typeids,
CandidateList function_typeids,
CandidateList *candidates) /* return value */
{
CandidateList current_candidate;
CandidateList matching_candidate;
Oid *current_typeids;
int ncandidates = 0;
*candidates = NULL;
for (current_candidate = function_typeids;
current_candidate != NULL;
current_candidate = current_candidate->next) {
current_typeids = current_candidate->args;
if (can_coerce(nargs, input_typeids, current_typeids)) {
matching_candidate = (CandidateList)
palloc(sizeof(struct _CandidateList));
matching_candidate->args = current_typeids;
matching_candidate->next = *candidates;
*candidates = matching_candidate;
ncandidates++;
}
}
return ncandidates;
}
/*
* given the input argtype array and more than one candidate
* for the function argtype array, attempt to resolve the conflict.
* returns the selected argtype array if the conflict can be resolved,
* otherwise returns NULL
*/
static Oid *
func_select_candidate(int nargs,
Oid *input_typeids,
CandidateList candidates)
{
/* XXX no conflict resolution implemeneted yet */
return (NULL);
}
static
bool is_lowercase(char *string)
{
int i;
for(i = 0; i < strlen(string); i++) {
if(string[i] >= 'A' && string[i] <= 'Z') {
return false;
}
}
return true;
}
static
void make_lowercase(char *string)
{
int i;
for(i = 0; i < strlen(string); i++) {
if(string[i] >= 'A' && string[i] <= 'Z') {
string[i] = (string[i] - 'A') + 'a';
}
}
}
bool
func_get_detail(char *funcname,
int nargs,
Oid *oid_array,
Oid *funcid, /* return value */
Oid *rettype, /* return value */
bool *retset, /* return value */
Oid **true_typeids) /* return value */
{
Oid **input_typeid_vector;
Oid *current_input_typeids;
CandidateList function_typeids;
CandidateList current_function_typeids;
HeapTuple ftup;
Form_pg_proc pform;
/*
* attempt to find named function in the system catalogs
* with arguments exactly as specified - so that the normal
* case is just as quick as before
*/
ftup = SearchSysCacheTuple(PRONAME,
PointerGetDatum(funcname),
Int32GetDatum(nargs),
PointerGetDatum(oid_array),
0);
*true_typeids = oid_array;
/*
* If an exact match isn't found :
* 1) get a vector of all possible input arg type arrays constructed
* from the superclasses of the original input arg types
* 2) get a list of all possible argument type arrays to the
* function with given name and number of arguments
* 3) for each input arg type array from vector #1 :
* a) find how many of the function arg type arrays from list #2
* it can be coerced to
* b) - if the answer is one, we have our function
* - if the answer is more than one, attempt to resolve the
* conflict
* - if the answer is zero, try the next array from vector #1
*/
if (!HeapTupleIsValid(ftup)) {
function_typeids = func_get_candidates(funcname, nargs);
if (function_typeids != NULL) {
int ncandidates = 0;
input_typeid_vector = argtype_inherit(nargs, oid_array);
current_input_typeids = oid_array;
do {
ncandidates = match_argtypes(nargs, current_input_typeids,
function_typeids,
&current_function_typeids);
if (ncandidates == 1) {
*true_typeids = current_function_typeids->args;
ftup = SearchSysCacheTuple(PRONAME,
PointerGetDatum(funcname),
Int32GetDatum(nargs),
PointerGetDatum(*true_typeids),
0);
Assert(HeapTupleIsValid(ftup));
}
else if (ncandidates > 1) {
*true_typeids =
func_select_candidate(nargs,
current_input_typeids,
current_function_typeids);
if (*true_typeids == NULL) {
elog(NOTICE, "there is more than one function named \"%s\"",
funcname);
elog(NOTICE, "that satisfies the given argument types. you will have to");
elog(NOTICE, "retype your query using explicit typecasts.");
func_error("func_get_detail", funcname, nargs, oid_array);
}
else {
ftup = SearchSysCacheTuple(PRONAME,
PointerGetDatum(funcname),
Int32GetDatum(nargs),
PointerGetDatum(*true_typeids),
0);
Assert(HeapTupleIsValid(ftup));
}
}
current_input_typeids = *input_typeid_vector++;
}
while (current_input_typeids !=
InvalidOid && ncandidates == 0);
}
}
if (!HeapTupleIsValid(ftup)) {
Type tp;
/*
* everything else has failed--try converting the function
* name to lowercase, and do everything one more time
* (if it's not already lowercase). so ODBC applications
* that expect uppercase names to work can work. --djm 8/17/96
*/
if(!is_lowercase(funcname)) {
char *lowercase_funcname = strdup(funcname);
bool result;
make_lowercase(lowercase_funcname);
result = func_get_detail(lowercase_funcname, nargs, oid_array,
funcid, rettype, retset,
true_typeids);
free(lowercase_funcname);
return result;
} else {
if (nargs == 1) {
tp = get_id_type(oid_array[0]);
if (typetypetype(tp) == 'c')
elog(WARN, "no such attribute or function \"%s\"",
funcname);
}
func_error("func_get_detail", funcname, nargs, oid_array);
}
} else {
pform = (Form_pg_proc) GETSTRUCT(ftup);
*funcid = ftup->t_oid;
*rettype = pform->prorettype;
*retset = pform->proretset;
return (true);
}
/* shouldn't reach here */
return (false);
}
/*
* argtype_inherit() -- Construct an argtype vector reflecting the
* inheritance properties of the supplied argv.
*
* This function is used to disambiguate among functions with the
* same name but different signatures. It takes an array of eight
* type ids. For each type id in the array that's a complex type
* (a class), it walks up the inheritance tree, finding all
* superclasses of that type. A vector of new Oid type arrays
* is returned to the caller, reflecting the structure of the
* inheritance tree above the supplied arguments.
*
* The order of this vector is as follows: all superclasses of the
* rightmost complex class are explored first. The exploration
* continues from right to left. This policy means that we favor
* keeping the leftmost argument type as low in the inheritance tree
* as possible. This is intentional; it is exactly what we need to
* do for method dispatch. The last type array we return is all
* zeroes. This will match any functions for which return types are
* not defined. There are lots of these (mostly builtins) in the
* catalogs.
*/
static Oid **
argtype_inherit(int nargs, Oid *oid_array)
{
Oid relid;
int i;
InhPaths arginh[MAXFARGS];
for (i = 0; i < MAXFARGS; i++) {
if (i < nargs) {
arginh[i].self = oid_array[i];
if ((relid = typeid_get_relid(oid_array[i])) != InvalidOid) {
arginh[i].nsupers = findsupers(relid, &(arginh[i].supervec));
} else {
arginh[i].nsupers = 0;
arginh[i].supervec = (Oid *) NULL;
}
} else {
arginh[i].self = InvalidOid;
arginh[i].nsupers = 0;
arginh[i].supervec = (Oid *) NULL;
}
}
/* return an ordered cross-product of the classes involved */
return (genxprod(arginh, nargs));
}
typedef struct _SuperQE {
Oid sqe_relid;
} SuperQE;
static int
findsupers(Oid relid, Oid **supervec)
{
Oid *relidvec;
Relation inhrel;
HeapScanDesc inhscan;
ScanKeyData skey;
HeapTuple inhtup;
TupleDesc inhtupdesc;
int nvisited;
SuperQE *qentry, *vnode;
Dllist *visited, *queue;
Dlelem *qe, *elt;
Relation rd;
Buffer buf;
Datum d;
bool newrelid;
char isNull;
nvisited = 0;
queue = DLNewList();
visited = DLNewList();
inhrel = heap_openr(InheritsRelationName);
RelationSetLockForRead(inhrel);
inhtupdesc = RelationGetTupleDescriptor(inhrel);
/*
* Use queue to do a breadth-first traversal of the inheritance
* graph from the relid supplied up to the root.
*/
do {
ScanKeyEntryInitialize(&skey, 0x0, Anum_pg_inherits_inhrel,
ObjectIdEqualRegProcedure,
ObjectIdGetDatum(relid));
inhscan = heap_beginscan(inhrel, 0, NowTimeQual, 1, &skey);
while (HeapTupleIsValid(inhtup = heap_getnext(inhscan, 0, &buf))) {
qentry = (SuperQE *) palloc(sizeof(SuperQE));
d = (Datum) fastgetattr(inhtup, Anum_pg_inherits_inhparent,
inhtupdesc, &isNull);
qentry->sqe_relid = DatumGetObjectId(d);
/* put this one on the queue */
DLAddTail(queue, DLNewElem(qentry));
ReleaseBuffer(buf);
}
heap_endscan(inhscan);
/* pull next unvisited relid off the queue */
do {
qe = DLRemHead(queue);
qentry = qe ? (SuperQE*)DLE_VAL(qe) : NULL;
if (qentry == (SuperQE *) NULL)
break;
relid = qentry->sqe_relid;
newrelid = true;
for (elt = DLGetHead(visited); elt; elt = DLGetSucc(elt)) {
vnode = (SuperQE*)DLE_VAL(elt);
if (vnode && (qentry->sqe_relid == vnode->sqe_relid)) {
newrelid = false;
break;
}
}
} while (!newrelid);
if (qentry != (SuperQE *) NULL) {
/* save the type id, rather than the relation id */
if ((rd = heap_open(qentry->sqe_relid)) == (Relation) NULL)
elog(WARN, "relid %d does not exist", qentry->sqe_relid);
qentry->sqe_relid = typeid(type(RelationGetRelationName(rd)->data));
heap_close(rd);
DLAddTail(visited, qe);
nvisited++;
}
} while (qentry != (SuperQE *) NULL);
RelationUnsetLockForRead(inhrel);
heap_close(inhrel);
if (nvisited > 0) {
relidvec = (Oid *) palloc(nvisited * sizeof(Oid));
*supervec = relidvec;
for (elt = DLGetHead(visited); elt; elt = DLGetSucc(elt)) {
vnode = (SuperQE*)DLE_VAL(elt);
*relidvec++ = vnode->sqe_relid;
}
} else {
*supervec = (Oid *) NULL;
}
return (nvisited);
}
static Oid **
genxprod(InhPaths *arginh, int nargs)
{
int nanswers;
Oid **result, **iter;
Oid *oneres;
int i, j;
int cur[MAXFARGS];
nanswers = 1;
for (i = 0; i < nargs; i++) {
nanswers *= (arginh[i].nsupers + 2);
cur[i] = 0;
}
iter = result = (Oid **) palloc(sizeof(Oid *) * nanswers);
/* compute the cross product from right to left */
for (;;) {
oneres = (Oid *) palloc(MAXFARGS * sizeof(Oid));
memset(oneres, 0, MAXFARGS * sizeof(Oid));
for (i = nargs - 1; i >= 0 && cur[i] > arginh[i].nsupers; i--)
continue;
/* if we're done, terminate with NULL pointer */
if (i < 0) {
*iter = NULL;
return (result);
}
/* no, increment this column and zero the ones after it */
cur[i] = cur[i] + 1;
for (j = nargs - 1; j > i; j--)
cur[j] = 0;
for (i = 0; i < nargs; i++) {
if (cur[i] == 0)
oneres[i] = arginh[i].self;
else if (cur[i] > arginh[i].nsupers)
oneres[i] = 0; /* wild card */
else
oneres[i] = arginh[i].supervec[cur[i] - 1];
}
*iter++ = oneres;
}
}
/* Given a type id, returns the in-conversion function of the type */
Oid
typeid_get_retinfunc(Oid type_id)
{
HeapTuple typeTuple;
TypeTupleForm type;
Oid infunc;
typeTuple = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(type_id),
0,0,0);
if ( !HeapTupleIsValid ( typeTuple ))
elog(WARN,
"typeid_get_retinfunc: Invalid type - oid = %ud",
type_id);
type = (TypeTupleForm) GETSTRUCT(typeTuple);
infunc = type->typinput;
return(infunc);
}
Oid
typeid_get_relid(Oid type_id)
{
HeapTuple typeTuple;
TypeTupleForm type;
Oid infunc;
typeTuple = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(type_id),
0,0,0);
if ( !HeapTupleIsValid ( typeTuple ))
elog(WARN, "typeid_get_relid: Invalid type - oid = %ud ", type_id);
type = (TypeTupleForm) GETSTRUCT(typeTuple);
infunc = type->typrelid;
return(infunc);
}
Oid
get_typrelid(Type typ)
{
TypeTupleForm typtup;
typtup = (TypeTupleForm) GETSTRUCT(typ);
return (typtup->typrelid);
}
Oid
get_typelem(Oid type_id)
{
HeapTuple typeTuple;
TypeTupleForm type;
if (!(typeTuple = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(type_id),
0,0,0))) {
elog (WARN , "type id lookup of %ud failed", type_id);
}
type = (TypeTupleForm) GETSTRUCT(typeTuple);
return (type->typelem);
}
char
FindDelimiter(char *typename)
{
char delim;
HeapTuple typeTuple;
TypeTupleForm type;
if (!(typeTuple = SearchSysCacheTuple(TYPNAME,
PointerGetDatum(typename),
0,0,0))) {
elog (WARN , "type name lookup of %s failed", typename);
}
type = (TypeTupleForm) GETSTRUCT(typeTuple);
delim = type->typdelim;
return (delim);
}
/*
* Give a somewhat useful error message when the operator for two types
* is not found.
*/
void
op_error(char *op, Oid arg1, Oid arg2)
{
Type tp1 = NULL, tp2 = NULL;
if (check_typeid(arg1)) {
tp1 = get_id_type(arg1);
} else {
elog(WARN, "left hand side of operator %s has an unknown type, probably a bad attribute name", op);
}
if (check_typeid(arg2)) {
tp2 = get_id_type(arg2);
} else {
elog(WARN, "right hand side of operator %s has an unknown type, probably a bad attribute name", op);
}
elog(NOTICE, "there is no operator %s for types %s and %s",
op, tname(tp1),tname(tp2));
elog(NOTICE, "You will either have to retype this query using an");
elog(NOTICE, "explicit cast, or you will have to define the operator");
elog(WARN, "%s for %s and %s using CREATE OPERATOR",
op, tname(tp1),tname(tp2));
}
/*
* Error message when function lookup fails that gives details of the
* argument types
*/
void
func_error(char *caller, char *funcname, int nargs, Oid *argtypes)
{
Type get_id_type();
char p[(NAMEDATALEN+2)*MAXFMGRARGS], *ptr;
int i;
ptr = p;
*ptr = '\0';
for (i=0; i<nargs; i++) {
if (i) {
*ptr++ = ',';
*ptr++ = ' ';
}
if (argtypes[i] != 0) {
(void) strcpy(ptr, tname(get_id_type(argtypes[i])));
*(ptr + NAMEDATALEN) = '\0';
} else
strcpy(ptr, "opaque");
ptr += strlen(ptr);
}
elog(WARN, "%s: function %s(%s) does not exist", caller, funcname, p);
}