/*------------------------------------------------------------------------- * * 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 #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; iargs[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; inext) { 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, ¤t_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