aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/catalog_utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/catalog_utils.c')
-rw-r--r--src/backend/parser/catalog_utils.c1470
1 files changed, 1470 insertions, 0 deletions
diff --git a/src/backend/parser/catalog_utils.c b/src/backend/parser/catalog_utils.c
new file mode 100644
index 00000000000..a4fc775c452
--- /dev/null
+++ b/src/backend/parser/catalog_utils.c
@@ -0,0 +1,1470 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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.1.1.1 1996/07/09 06:21:40 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#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 "catalog_utils.h"
+#include "catalog/pg_inherits.h"
+#include "catalog/pg_type.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"
+
+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 */
+
+static Oid **argtype_inherit();
+static Oid **genxprod();
+
+static int findsupers(Oid relid, Oid **supervec);
+/*
+ * 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;
+
+/* 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(long id)
+{
+ return (SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(id),
+ 0,0,0) != NULL);
+}
+
+
+/* return a Type structure, given an typid */
+Type
+get_id_type(long id)
+{
+ HeapTuple tup;
+
+ if (!(tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(id),
+ 0,0,0))) {
+ elog ( WARN, "type id lookup of %d failed", id);
+ return(NULL);
+ }
+ return((Type) tup);
+}
+
+/* return a type name, given a typeid */
+char*
+get_id_typname(long id)
+{
+ HeapTuple tup;
+ TypeTupleForm typetuple;
+
+ if (!(tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(id),
+ 0,0,0))) {
+ elog ( WARN, "type id lookup of %d 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) */
+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,
+ int leftTypeId,
+ int 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;
+ int 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(int arg1,
+ int 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, int arg1, int arg2)
+{
+ HeapTuple tup;
+ CandidateList candidates;
+ int ncandidates;
+
+ 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,
+ int 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, int 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, int 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);
+}
+
+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, (int*)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;
+
+ 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, (int*)oid_array);
+ } else {
+ pform = (Form_pg_proc) GETSTRUCT(ftup);
+ *funcid = ftup->t_oid;
+ *rettype = (Oid) pform->prorettype;
+ *retset = (Oid) 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(int 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 = %d",
+ type_id);
+
+ type = (TypeTupleForm) GETSTRUCT(typeTuple);
+ infunc = type->typinput;
+ return(infunc);
+}
+
+Oid
+typeid_get_relid(int 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 = %d ", 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 %d 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, int arg1, int arg2)
+{
+ Type tp1, tp2;
+
+ 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 DEFINE 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, int *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);
+}
+