OSDN Git Service

Add capabilities for automatic type conversion.
authorThomas G. Lockhart <lockhart@fourpalms.org>
Sat, 9 May 1998 23:31:34 +0000 (23:31 +0000)
committerThomas G. Lockhart <lockhart@fourpalms.org>
Sat, 9 May 1998 23:31:34 +0000 (23:31 +0000)
src/backend/parser/analyze.c
src/backend/parser/parse_coerce.c [new file with mode: 0644]
src/backend/parser/parse_expr.c
src/backend/parser/parse_func.c
src/backend/parser/parse_node.c
src/backend/parser/parse_oper.c
src/backend/parser/parse_target.c
src/backend/parser/parse_type.c
src/include/parser/parse_coerce.h [new file with mode: 0644]
src/include/parser/parse_func.h

index 7d107e4..0038c39 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.74 1998/03/31 23:31:10 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.75 1998/05/09 23:29:52 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,6 +30,9 @@
 #include "parser/parse_target.h"
 #include "utils/builtins.h"
 #include "utils/mcxt.h"
+#ifdef PARSEDEBUG
+#include "nodes/print.h"
+#endif
 
 static Query *transformStmt(ParseState *pstate, Node *stmt);
 static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
@@ -65,6 +68,10 @@ parse_analyze(List *pl, ParseState *parentParseState)
 
        while (pl != NIL)
        {
+#ifdef PARSEDEBUG
+               elog(DEBUG,"parse tree from yacc:\n---\n%s\n---\n", nodeToString(lfirst(pl)));
+#endif
+
                pstate = make_parsestate(parentParseState);
                result->qtrees[i++] = transformStmt(pstate, lfirst(pl));
                if (pstate->p_target_relation != NULL)
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
new file mode 100644 (file)
index 0000000..77c23ac
--- /dev/null
@@ -0,0 +1,560 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_coerce.c
+ *             handle type coersions/conversions for parser
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.1 1998/05/09 23:29:53 thomas Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"
+#include "utils/builtins.h"
+#include "fmgr.h"
+#include "nodes/makefuncs.h"
+
+#include "parser/parse_expr.h"
+
+#include "catalog/pg_type.h"
+#include "parser/parse_type.h"
+#include "parser/parse_target.h"
+#include "parser/parse_coerce.h"
+#include "utils/syscache.h"
+
+Oid DemoteType(Oid inType);
+Oid PromoteTypeToNext(Oid inType);
+
+
+/* coerce_type()
+ * Convert a function argument to a different type.
+ */
+Node *
+coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId)
+{
+       Node   *result = NULL;
+       Oid             infunc;
+       Datum   val;
+
+#ifdef PARSEDEBUG
+printf("coerce_type: argument types are %d -> %d\n",
+ inputTypeId, targetTypeId);
+#endif
+
+       if (targetTypeId == InvalidOid)
+       {
+#ifdef PARSEDEBUG
+printf("coerce_type: apparent NULL target argument; suppress type conversion\n");
+#endif
+               result = node;
+       }
+       else if (inputTypeId != targetTypeId)
+       {
+               /* one of the known-good transparent conversions? then drop through... */
+               if (IS_BINARY_COMPATIBLE(inputTypeId, targetTypeId))
+               {
+#ifdef PARSEDEBUG
+printf("coerce_type: argument type %s is known to be convertible to type %s\n",
+ typeidTypeName(inputTypeId), typeidTypeName(targetTypeId));
+#endif
+                       result = node;
+               }
+
+               /* if not unknown input type, try for explicit conversion using functions... */
+               else if (inputTypeId != UNKNOWNOID)
+               {
+                       /* We already know there is a function which will do this, so let's use it */
+                       FuncCall *n = makeNode(FuncCall);
+                       n->funcname = typeidTypeName(targetTypeId);
+                       n->args = lcons(node, NIL);
+
+#ifdef PARSEDEBUG
+printf("coerce_type: construct function %s(%s)\n",
+ typeidTypeName(targetTypeId), typeidTypeName(inputTypeId));
+#endif
+
+                       result = transformExpr(pstate, (Node *) n, EXPR_COLUMN_FIRST);
+               }
+               else
+               {
+#ifdef PARSEDEBUG
+printf("coerce_type: node is UNKNOWN type\n");
+#endif
+            if (nodeTag(node) == T_Const)
+            {
+                               Const *con = (Const *) node;
+
+                               val = (Datum) textout((struct varlena *)
+                                                                          con->constvalue);
+                               infunc = typeidInfunc(targetTypeId);
+                               con = makeNode(Const);
+                               con->consttype = targetTypeId;
+                               con->constlen = typeLen(typeidType(targetTypeId));
+
+                               /* use "-1" for varchar() type */
+                               con->constvalue = (Datum) fmgr(infunc,
+                                                                                          val,
+                                                                                          typeidTypElem(targetTypeId),
+                                                                                          -1);
+                               con->constisnull = false;
+                               con->constbyval = true;
+                               con->constisset = false;
+                               result = (Node *) con;
+                       }
+                       else
+                       {
+#ifdef PARSEDEBUG
+printf("coerce_type: should never get here!\n");
+#endif
+                               result = node;
+                       }
+               }
+       }
+       else
+       {
+#ifdef PARSEDEBUG
+printf("coerce_type: argument type IDs %d match\n", inputTypeId);
+#endif
+
+               result = node;
+       }
+
+       return result;
+} /* coerce_type() */
+
+
+/* can_coerce_type()
+ * Can input_typeids be coerced to func_typeids?
+ *
+ * There are a few types which are known apriori to be convertible.
+ * We will check for those cases first, and then look for possible
+ *  conversion functions.
+ *
+ * Notes:
+ * This uses the same mechanism as the CAST() SQL construct in gram.y.
+ * We should also check the function return type on candidate conversion
+ *  routines just to be safe but we do not do that yet...
+ * We need to have a zero-filled OID array here, otherwise the cache lookup fails.
+ * - thomas 1998-03-31
+ */
+bool
+can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids)
+{
+       HeapTuple       ftup;
+       int                     i;
+       Type            tp;
+       Oid                     oid_array[8];
+
+       /* run through argument list... */
+       for (i = 0; i < nargs; i++)
+       {
+#ifdef PARSEDEBUG
+printf("can_coerce_type: argument #%d types are %d -> %d\n",
+ i, input_typeids[i], func_typeids[i]);
+#endif
+               if (input_typeids[i] != func_typeids[i])
+               {
+                       /* one of the known-good transparent conversions? then drop through... */
+                       if (IS_BINARY_COMPATIBLE(input_typeids[i], func_typeids[i]))
+                       {
+#ifdef PARSEDEBUG
+printf("can_coerce_type: argument #%d type %s is known to be convertible to type %s\n",
+ i, typeidTypeName(input_typeids[i]), typeidTypeName(func_typeids[i]));
+#endif
+                       }
+
+                       /* don't know what to do for the output type? then quit... */
+                       else if (func_typeids[i] == InvalidOid)
+                       {
+#ifdef PARSEDEBUG
+printf("can_coerce_type: output OID func_typeids[%d] is zero\n", i);
+#endif
+                               return false;
+                       }
+
+                       /* don't know what to do for the input type? then quit... */
+                       else if (input_typeids[i] == InvalidOid)
+                       {
+#ifdef PARSEDEBUG
+printf("can_coerce_type: input OID input_typeids[%d] is zero\n", i);
+#endif
+                               return false;
+                       }
+
+                       /* if not unknown input type, try for explicit conversion using functions... */
+                       else if (input_typeids[i] != UNKNOWNOID)
+                       {
+                               MemSet(&oid_array[0], 0, 8 * sizeof(Oid));
+                               oid_array[0] = input_typeids[i];
+
+                               /* look for a single-argument function named with the target type name */
+                               ftup = SearchSysCacheTuple(PRONAME,
+                                                                                  PointerGetDatum(typeidTypeName(func_typeids[i])),
+                                                                                  Int32GetDatum(1),
+                                                                                  PointerGetDatum(oid_array),
+                                                                                  0);
+
+                               /* should also check the function return type just to be safe... */
+                               if (HeapTupleIsValid(ftup))
+                               {
+#ifdef PARSEDEBUG
+printf("can_coerce_type: found function %s(%s) to convert argument #%d\n",
+ typeidTypeName(func_typeids[i]), typeidTypeName(input_typeids[i]), i);
+#endif
+                               }
+                               else
+                               {
+#ifdef PARSEDEBUG
+printf("can_coerce_type: did not find function %s(%s) to convert argument #%d\n",
+ typeidTypeName(func_typeids[i]), typeidTypeName(input_typeids[i]), i);
+#endif
+                                       return false;
+                               }
+                       }
+                       else
+                       {
+#ifdef PARSEDEBUG
+printf("can_coerce_type: argument #%d type is %d (UNKNOWN)\n",
+ i, input_typeids[i]);
+#endif
+                       }
+
+                       tp = typeidType(input_typeids[i]);
+                       if (typeTypeFlag(tp) == 'c')
+                       {
+#ifdef PARSEDEBUG
+printf("can_coerce_type: typeTypeFlag for %s is 'c'\n",
+ typeidTypeName(input_typeids[i]));
+#endif
+                               return false;
+                       }
+
+#ifdef PARSEDEBUG
+printf("can_coerce_type: conversion from %s to %s is possible\n",
+ typeidTypeName(input_typeids[i]), typeidTypeName(func_typeids[i]));
+#endif
+               }
+               else
+               {
+#ifdef PARSEDEBUG
+printf("can_coerce_type: argument #%d type IDs %d match\n",
+ i, input_typeids[i]);
+#endif
+               }
+       }
+
+       return true;
+} /* can_coerce_type() */
+
+
+/* TypeCategory()
+ * Assign a category to the specified OID.
+ */
+CATEGORY
+TypeCategory(Oid inType)
+{
+       CATEGORY result;
+
+       switch (inType)
+       {
+               case (BOOLOID):
+                       result = BOOLEAN_TYPE;
+                       break;
+
+               case (CHAROID):
+               case (BPCHAROID):
+               case (VARCHAROID):
+               case (TEXTOID):
+                       result = STRING_TYPE;
+                       break;
+
+               case (INT2OID):
+               case (INT4OID):
+               case (FLOAT4OID):
+               case (FLOAT8OID):
+               case (CASHOID):
+                       result = NUMERIC_TYPE;
+                       break;
+
+               case (ABSTIMEOID):
+               case (TIMESTAMPOID):
+               case (DATETIMEOID):
+                       result = DATETIME_TYPE;
+                       break;
+
+               case (RELTIMEOID):
+               case (TIMESPANOID):
+                       result = TIMESPAN_TYPE;
+                       break;
+
+               case (POINTOID):
+               case (LSEGOID):
+               case (LINEOID):
+               case (BOXOID):
+               case (PATHOID):
+               case (CIRCLEOID):
+               case (POLYGONOID):
+                       result = GEOMETRIC_TYPE;
+                       break;
+
+               default:
+                       result = USER_TYPE;
+                       break;
+       }
+       return (result);
+} /* TypeCategory() */
+
+
+/* IsPreferredType()
+ * Assign a category to the specified OID.
+ */
+bool
+IsPreferredType(CATEGORY category, Oid type)
+{
+       return (type == PreferredType(category, type));
+} /* IsPreferredType() */
+
+
+/* PreferredType()
+ * Assign a category to the specified OID.
+ */
+Oid
+PreferredType(CATEGORY category, Oid type)
+{
+       Oid             result;
+
+       switch (category)
+       {
+               case (BOOLEAN_TYPE):
+                       result = BOOLOID;
+                       break;
+
+               case (STRING_TYPE):
+                       result = TEXTOID;
+                       break;
+
+               case (NUMERIC_TYPE):
+                       result = FLOAT8OID;
+                       break;
+
+               case (DATETIME_TYPE):
+                       result = DATETIMEOID;
+                       break;
+
+               case (TIMESPAN_TYPE):
+                       result = TIMESPANOID;
+                       break;
+
+               case (GEOMETRIC_TYPE):
+               case (USER_TYPE):
+                       result = type;
+                       break;
+
+               default:
+                       result = UNKNOWNOID;
+                       break;
+       }
+#ifdef PARSEDEBUG
+printf("PreferredType- (%d) preferred type is %s\n", category, typeidTypeName(result));
+#endif
+       return (result);
+} /* PreferredType() */
+
+
+#if FALSE
+Oid
+PromoteTypeToNext(Oid inType)
+{
+       Oid result;
+
+       switch (inType)
+       {
+               case (CHAROID):
+               case (BPCHAROID):
+                       result = VARCHAROID;
+                       break;
+
+               case (VARCHAROID):
+                       result = TEXTOID;
+                       break;
+
+               case (INT2OID):
+               case (CASHOID):
+                       result = INT4OID;
+                       break;
+
+               case (INT4OID):
+               case (FLOAT4OID):
+                       result = FLOAT8OID;
+                       break;
+
+               case (DATEOID):
+               case (ABSTIMEOID):
+               case (TIMESTAMPOID):
+                       result = DATETIMEOID;
+                       break;
+
+               case (TIMEOID):
+               case (RELTIMEOID):
+                       result = TIMESPANOID;
+                       break;
+
+               case (BOOLOID):
+               case (TEXTOID):
+               case (FLOAT8OID):
+               case (DATETIMEOID):
+               case (TIMESPANOID):
+               default:
+                       result = inType;
+                       break;
+       }
+       return (result);
+} /* PromoteTypeToNext() */
+
+
+Oid
+DemoteType(Oid inType)
+{
+       Oid result;
+
+       switch (inType)
+       {
+               case (FLOAT4OID):
+               case (FLOAT8OID):
+                       result = INT4OID;
+                       break;
+
+               default:
+                       result = inType;
+                       break;
+       }
+       return (result);
+} /* DemoteType() */
+
+
+Oid
+PromoteLesserType(Oid inType1, Oid inType2, Oid *newType1, Oid *newType2)
+{
+       Oid result;
+
+       if (inType1 == inType2)
+       {
+               result = PromoteTypeToNext(inType1);
+               inType1 = result;
+               *arg2 = result;
+               return (result);
+       }
+
+       kind1 = ClassifyType(inType1);
+       kind2 = ClassifyType(*arg2);
+       if (kind1 != kind2)
+       {
+               *newType1 = inType1;
+               *newType2 = inType2;
+               result = InvalidOid;
+       }
+
+       isBuiltIn1 = IS_BUILTIN_TYPE(inType1);
+       isBuiltIn2 = IS_BUILTIN_TYPE(*arg2);
+
+       if (isBuiltIn1 && isBuiltIn2)
+       {
+               switch (*arg1)
+               {
+                       case (CHAROID):
+                       switch (*arg2)
+                       {
+               case (BPCHAROID):
+               case (VARCHAROID):
+               case (TEXTOID):
+
+               case (INT2OID):
+               case (INT4OID):
+               case (FLOAT4OID):
+               case (FLOAT8OID):
+               case (CASHOID):
+
+               case (POINTOID):
+               case (LSEGOID):
+               case (LINEOID):
+               case (BOXOID):
+               case (PATHOID):
+               case (CIRCLEOID):
+               case (POLYGONOID):
+
+               case (InvalidOid):
+               case (UNKNOWNOID):
+               case (BOOLOID):
+               default:
+                       *arg1 = InvalidOid;
+                       *arg2 = InvalidOid;
+                       result = InvalidOid;
+       }
+       }
+       else if (isBuiltIn1 && !isBuiltIn2)
+       {
+               if ((promotedType = PromoteBuiltInType(*arg1)) != *arg1)
+               {
+                       *arg1 = promotedType;
+                       return (promotedType);
+               }
+               else if (CanCoerceType(*arg1, *arg2))
+               {
+                       *arg1 = *arg2;
+                       return (*arg2);
+               }
+       }
+       else if (!isBuiltIn1 && isBuiltIn2)
+       {
+               if ((promotedType = PromoteBuiltInType(*arg2)) != *arg2)
+               {
+                       *arg2 = promotedType;
+                       return (promotedType);
+               }
+               else if (CanCoerceType(*arg2, *arg1))
+               {
+                       *arg2 = *arg1;
+                       return (*arg1);
+               }
+       }
+
+
+       if (*arg2 == InvalidOid)
+               return InvalidOid;
+
+       switch (*arg1)
+       {
+               case (CHAROID):
+                       switch (*arg2)
+                       {
+               case (BPCHAROID):
+               case (VARCHAROID):
+               case (TEXTOID):
+
+               case (INT2OID):
+               case (INT4OID):
+               case (FLOAT4OID):
+               case (FLOAT8OID):
+               case (CASHOID):
+
+               case (POINTOID):
+               case (LSEGOID):
+               case (LINEOID):
+               case (BOXOID):
+               case (PATHOID):
+               case (CIRCLEOID):
+               case (POLYGONOID):
+
+               case (InvalidOid):
+               case (UNKNOWNOID):
+               case (BOOLOID):
+               default:
+                       *arg1 = InvalidOid;
+                       *arg2 = InvalidOid;
+                       result = InvalidOid;
+       }
+}
+#endif
index 39b9fd0..af8424e 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.27 1998/04/26 04:06:45 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.28 1998/05/09 23:29:53 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -301,12 +301,14 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
                                result = (Node *) expr;
                                break;
                        }
-/* These nodes do _not_ come from the original parse tree.
- * They result from parser transformation in this phase.
+
+/* These nodes do _not_ come from the original parse tree,
+ *  but result from parser transformation in this phase.
  * At least one construct (BETWEEN/AND) puts the same nodes
- *  into two branches of the parse tree. Hence, some nodes
- *  are transformed twice. These nodes come from transforming
- *  a function call. Let's try just passing them through...
+ *  into two branches of the parse tree; hence, some nodes
+ *  are transformed twice.
+ * These cases below come from transforming function calls.
+ * Let's try just passing them through...
  * - thomas 1998-03-14
  */
                case T_Expr:
@@ -506,6 +508,10 @@ parser_typecast(Value *expr, TypeName *typename, int16 atttypmod)
        return (Node *) adt;
 }
 
+
+/* parser_typecast2()
+ * Convert (only) constants to specified type.
+ */
 Node *
 parser_typecast2(Node *expr, Oid exprType, Type tp, int16 atttypmod)
 {
index 7809e53..a4e34d6 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.16 1998/04/27 04:06:05 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.17 1998/05/09 23:29:53 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,6 +37,7 @@
 #include "parser/parse_relation.h"
 #include "parser/parse_target.h"
 #include "parser/parse_type.h"
+#include "parser/parse_coerce.h"
 #include "storage/bufmgr.h"
 #include "storage/lmgr.h"
 #include "utils/acl.h"
@@ -50,7 +51,7 @@ ParseComplexProjection(ParseState *pstate,
                                           Node *first_arg,
                                           bool *attisset);
 static Oid **argtype_inherit(int nargs, Oid *oid_array);
-static bool can_coerce(int nargs, Oid *input_typeids, Oid *func_typeids);
+
 static int     find_inheritors(Oid relid, Oid **supervec);
 static CandidateList func_get_candidates(char *funcname, int nargs);
 static bool
@@ -61,14 +62,15 @@ func_get_detail(char *funcname,
                                Oid *rettype,   /* return value */
                                bool *retset,   /* return value */
                                Oid **true_typeids);
-static Oid *
+Oid *
 func_select_candidate(int nargs,
                                          Oid *input_typeids,
                                          CandidateList candidates);
 static Oid     funcid_get_rettype(Oid funcid);
 static Oid **gen_cross_product(InhPaths *arginh, int nargs);
 static void
-make_arguments(int nargs,
+make_arguments(ParseState *pstate,
+                          int nargs,
                           List *fargs,
                           Oid *input_typeids,
                           Oid *function_typeids);
@@ -255,6 +257,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
                        /*
                         * Parsing aggregates.
                         */
+                       Type            tp;
                        Oid                     basetype;
 
                        /*
@@ -271,15 +274,27 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
                                                                        0, 0))
                                return (Node *) ParseAgg(pstate, funcname, basetype,
                                                                                 fargs, precedence);
+
+                       /*
+                    * See if this is a single argument function with the function name
+                    *  also a type name and the input argument and type name binary compatible...
+                        */
+                       if ((HeapTupleIsValid(tp = SearchSysCacheTuple(TYPNAME,
+                                                                                                                  PointerGetDatum(funcname),
+                                                                                                                  0, 0, 0)))
+                        && IS_BINARY_COMPATIBLE(typeTypeId(tp), basetype))
+                       {
+                               return((Node *)lfirst(fargs));
+                       }
                }
        }
 
 
        /*
-        * If we dropped through to here it's really a function (or a set,
-        * which * is implemented as a function.) * extract arg type info and
-        * transform relation name arguments into * varnodes of the
-        * appropriate form.
+        * If we dropped through to here it's really a function (or a set,
+        *  which is implemented as a function).
+        * Extract arg type info and transform relation name arguments
+        *  into varnodes of the appropriate form.
         */
        MemSet(&oid_array[0], 0, 8 * sizeof(Oid));
 
@@ -326,9 +341,9 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
                         * constants, we don't know what to do with, because we can't
                         * cast them    - jolly
                         */
-                       if (exprType(pair) == UNKNOWNOID &&
-                               !IsA(pair, Const))
-                               elog(ERROR, "ParseFuncOrColumn: no function named '%s' that takes in an unknown type as argument #%d", funcname, nargs);
+                       if (exprType(pair) == UNKNOWNOID && !IsA(pair, Const))
+                               elog(ERROR, "ParseFuncOrColumn: no function named '%s'"
+                                " that takes in an unknown type as argument #%d", funcname, nargs);
                        else
                                toid = exprType(pair);
                }
@@ -383,7 +398,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
        funcnode->func_planlist = NIL;
 
        /* perform the necessary typecasting */
-       make_arguments(nargs, fargs, oid_array, true_oid_array);
+       make_arguments(pstate, nargs, fargs, oid_array, true_oid_array);
 
        /*
         * for functions returning base types, we want to project out the
@@ -482,7 +497,7 @@ funcid_get_rettype(Oid funcid)
                                                                         0, 0, 0);
 
        if (!HeapTupleIsValid(func_tuple))
-               elog(ERROR, "function  %d does not exist", funcid);
+               elog(ERROR, "function %d does not exist", funcid);
 
        funcrettype = (Oid)
                ((Form_pg_proc) GETSTRUCT(func_tuple))->prorettype;
@@ -490,7 +505,8 @@ funcid_get_rettype(Oid funcid)
        return (funcrettype);
 }
 
-/*
+
+/* func_get_candidates()
  * get a list of all argument type vectors for which a function named
  * funcname taking nargs arguments exists
  */
@@ -567,44 +583,9 @@ func_get_candidates(char *funcname, int nargs)
        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] == BPCHAROID && func_typeids[i] == TEXTOID) ||
-                               (input_typeids[i] == BPCHAROID && func_typeids[i] == VARCHAROID) ||
-                               (input_typeids[i] == VARCHAROID && func_typeids[i] == TEXTOID) ||
-                               (input_typeids[i] == VARCHAROID && func_typeids[i] == BPCHAROID) ||
-                       (input_typeids[i] == CASHOID && func_typeids[i] == INT4OID) ||
-                        (input_typeids[i] == INT4OID && func_typeids[i] == CASHOID))
-                               ;                               /* these are OK */
-                       else if (input_typeids[i] != UNKNOWNOID || func_typeids[i] == 0)
-                               return false;
-
-                       tp = typeidType(input_typeids[i]);
-                       if (typeTypeFlag(tp) == 'c')
-                               return false;
-               }
-       }
-
-       return true;
-}
 
-/*
- * given a list of possible typeid arrays to a function and an array of
+/* match_argtypes()
+ * 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
@@ -627,7 +608,7 @@ match_argtypes(int nargs,
                 current_candidate = current_candidate->next)
        {
                current_typeids = current_candidate->args;
-               if (can_coerce(nargs, input_typeids, current_typeids))
+               if (can_coerce_type(nargs, input_typeids, current_typeids))
                {
                        matching_candidate = (CandidateList)
                                palloc(sizeof(struct _CandidateList));
@@ -639,31 +620,637 @@ match_argtypes(int nargs,
        }
 
        return ncandidates;
-}
+} /* match_argtypes() */
 
-/*
- * given the input argtype array and more than one candidate
+
+/* func_select_candidate()
+ * 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
+ * otherwise returns NULL.
+ *
+ * If all input Oids are UNKNOWNOID, then try matching with TEXTOID.
+ * Otherwise, could return first function arguments on list of candidates.
+ * But for now, return NULL and make the user give a better hint.
+ * - thomas 1998-03-17
  */
-static Oid *
+Oid *
 func_select_candidate(int nargs,
                                          Oid *input_typeids,
                                          CandidateList candidates)
 {
-       /* XXX no conflict resolution implemeneted yet */
+       CandidateList   current_candidate;
+       CandidateList   last_candidate;
+       Oid                        *current_typeids;
+       int                             i;
+
+       int                             ncandidates;
+       int                             nbestMatch,
+                                       nmatch,
+                                       nident;
+
+       CATEGORY                slot_category,
+                                       current_category;
+       Oid                             slot_type,
+                                       current_type;
+
+/*
+ * Run through all candidates and keep those with the most matches
+ *  on explicit types. Keep all candidates if none match.
+ */
+       ncandidates = 0;
+       nbestMatch = 0;
+       last_candidate = NULL;
+       for (current_candidate = candidates;
+                current_candidate != NULL;
+                current_candidate = current_candidate->next)
+       {
+               current_typeids = current_candidate->args;
+               nmatch = 0;
+               nident = 0;
+               for (i = 0; i < nargs; i++)
+               {
+                       if ((input_typeids[i] != UNKNOWNOID)
+                        && (current_typeids[i] == input_typeids[i]))
+                       {
+                               nmatch++;
+                       }
+                       else if (IS_BINARY_COMPATIBLE(current_typeids[i], input_typeids[i]))
+                       {
+                               nident++;
+                       }
+               }
+
+               if ((nmatch + nident) == nargs)
+                       return (current_candidate->args);
+
+#ifdef PARSEDEBUG
+printf("func_select_candidate- candidate has %d matches\n", nmatch);
+#endif
+               if ((nmatch > nbestMatch) || (last_candidate == NULL))
+               {
+                       nbestMatch = nmatch;
+                       candidates = current_candidate;
+                       last_candidate = current_candidate;
+                       ncandidates = 1;
+#ifdef PARSEDEBUG
+printf("func_select_candidate- choose candidate as best match\n");
+#endif
+               }
+               else if (nmatch == nbestMatch)
+               {
+                       last_candidate->next = current_candidate;
+                       last_candidate = current_candidate;
+                       ncandidates++;
+#ifdef PARSEDEBUG
+printf("func_select_candidate- choose candidate as possible match\n");
+#endif
+               }
+               else
+               {
+                       last_candidate->next = NULL;
+#ifdef PARSEDEBUG
+printf("func_select_candidate- reject candidate as possible match\n");
+#endif
+               }
+       }
+
+/*
+ * Still too many candidates?
+ * Try assigning types for the unknown columns.
+ */
+       if (ncandidates > 1)
+       {
+               for (i = 0; i < nargs; i++)
+               {
+                       if (input_typeids[i] == UNKNOWNOID)
+                       {
+                               slot_category = INVALID_TYPE;
+                               slot_type = InvalidOid;
+                               for (current_candidate = candidates;
+                                        current_candidate != NULL;
+                                        current_candidate = current_candidate->next)
+                               {
+                                       current_typeids = current_candidate->args;
+                                       current_type = current_typeids[i];
+                                       current_category = TypeCategory(current_typeids[i]);
+
+                                       if (slot_category == InvalidOid)
+                                       {
+                                               slot_category = current_category;
+                                               slot_type = current_type;
+#ifdef PARSEDEBUG
+printf("func_select_candidate- assign column #%d first candidate slot type %s\n",
+ i, typeidTypeName(current_type));
+#endif
+                                       }
+                                       else if ((current_category != slot_category)
+                                        && IS_BUILTIN_TYPE(current_type))
+                                       {
+#ifdef PARSEDEBUG
+printf("func_select_candidate- multiple possible types for column #%d; unable to choose candidate\n", i);
+#endif
+                                               return NULL;
+                                       }
+                                       else if (current_type != slot_type)
+                                       {
+                                               if (IsPreferredType(slot_category, current_type))
+                                               {
+                                                       slot_type = current_type;
+                                                       candidates = current_candidate;
+#ifdef PARSEDEBUG
+printf("func_select_candidate- column #%d found preferred candidate type %s\n",
+ i, typeidTypeName(slot_type));
+#endif
+                                               }
+                                               else
+                                               {
+#ifdef PARSEDEBUG
+printf("func_select_candidate- column #%d found possible candidate type %s\n",
+ i, typeidTypeName(current_type));
+#endif
+                                               }
+                                       }
+                               }
+
+                               if (slot_type != InvalidOid)
+                               {
+                                       input_typeids[i] = slot_type;
+#ifdef PARSEDEBUG
+printf("func_select_candidate- assign column #%d slot type %s\n",
+ i, typeidTypeName(input_typeids[i]));
+#endif
+                               }
+                       }
+                       else
+                       {
+#ifdef PARSEDEBUG
+printf("func_select_candidate- column #%d input type is %s\n",
+ i, typeidTypeName(input_typeids[i]));
+#endif
+                       }
+               }
+
+               ncandidates = 0;
+               for (current_candidate = candidates;
+                        current_candidate != NULL;
+                        current_candidate = current_candidate->next)
+               {
+                       ncandidates++;
+               }
+       }
+
+       if (ncandidates == 1)
+               return (candidates->args);
+
        return (NULL);
-}
+} /* func_select_candidate() */
+
+
+Oid *
+oper_select_candidate(int nargs,
+                                         Oid *input_typeids,
+                                         CandidateList candidates);
+
+#if FALSE
+/* oper_select_candidate()
+ */
+Oid *
+oper_select_candidate(int nargs,
+                                         Oid *input_typeids,
+                                         CandidateList candidates)
+{
+       CandidateList   current_candidate;
+       Oid                        *current_typeids;
+       int                             unknownOids, textOids;
+       int                             i;
+
+       int                             ncandidates;
+       int                             nbestMatch;
+       Oid                             bestTypeId;
+
+       unknownOids = TRUE;
+       for (i = 0; i < nargs; i++)
+       {
+               unknownOids &= (input_typeids[i] == UNKNOWNOID);
+#ifdef PARSEDEBUG
+printf("oper_select_candidate: argument #%d type is %s\n",
+ i, typeidTypeName(input_typeids[i]));
+#endif
+       }
+
+       for (current_candidate = candidates;
+                current_candidate != NULL;
+                current_candidate = current_candidate->next)
+       {
+               current_typeids = current_candidate->args;
+               if (unknownOids)
+               {
+                       textOids = TRUE;
+                       for (i = 0; i < nargs; i++)
+                       {
+                               textOids &= (current_typeids[i] == TEXTOID);
+#ifdef PARSEDEBUG
+printf("oper_select_candidate: candidate argument #%d type is %s\n",
+ i, typeidTypeName(current_typeids[i]));
+#endif
+                       }
+                       if (textOids)
+                               return(current_candidate->args);
+               }
+       }
+
+#ifdef PARSEDEBUG
+printf("oper_select_candidate: no all-text operators found\n");
+#endif
+
+       /* OK, there are multiple types here; let's see if we can choose... */
+               nbestMatch = 0;
+               bestTypeId = InvalidOid;
+
+               for (current_candidate = candidates;
+                        current_candidate != NULL;
+                        current_candidate = current_candidate->next)
+               {
+                       current_typeids = current_candidate->args;
+                       if (IS_HIGHEST_TYPE(input_typeids[0])
+                        && (input_typeids[0] == current_typeids[0])
+                        && IS_HIGHEST_TYPE(current_typeids[1])
+                        && can_coerce_type(1, &input_typeids[1], &current_typeids[1]))
+                       {
+#ifdef PARSEDEBUG
+printf("oper_select_candidate: (1) choose (%s,%s) -> (%s,%s)...\n",
+ typeidTypeName(input_typeids[0]), typeidTypeName(input_typeids[1]),
+ typeidTypeName(current_typeids[0]), typeidTypeName(current_typeids[1]));
+#endif
+                               return (current_candidate->args);
+                       }
+                       else if (IS_HIGHEST_TYPE(input_typeids[1])
+                        && (input_typeids[1] == current_typeids[1])
+                        && IS_HIGHEST_TYPE(current_typeids[0])
+                        && can_coerce_type(1, &input_typeids[0], &current_typeids[0]))
+                       {
+#ifdef PARSEDEBUG
+printf("oper_select_candidate: (2) choose (%s,%s) -> (%s,%s)...\n",
+ typeidTypeName(input_typeids[0]), typeidTypeName(input_typeids[1]),
+ typeidTypeName(current_typeids[0]), typeidTypeName(current_typeids[1]));
+#endif
+                               return (current_candidate->args);
+                       }
+                       else
+                       {
+#ifdef PARSEDEBUG
+printf("oper_select_candidate: (3) skip (%s,%s) -> (%s,%s)...\n",
+ typeidTypeName(input_typeids[0]), typeidTypeName(input_typeids[1]),
+ typeidTypeName(current_typeids[0]), typeidTypeName(current_typeids[1]));
+#endif
+                       }
+               }
 
+               for (current_candidate = candidates;
+                        current_candidate != NULL;
+                        current_candidate = current_candidate->next)
+               {
+                       current_typeids = current_candidate->args;
+                       if ((input_typeids[0] == current_typeids[0])
+                        && can_coerce_type(1, &input_typeids[1], &current_typeids[1]))
+                       {
+#ifdef PARSEDEBUG
+printf("oper_select_candidate: (4) choose (%s,%s) -> (%s,%s)...\n",
+ typeidTypeName(input_typeids[0]), typeidTypeName(input_typeids[1]),
+ typeidTypeName(current_typeids[0]), typeidTypeName(current_typeids[1]));
+#endif
+                               return (current_candidate->args);
+                       }
+                       else if ((input_typeids[1] == current_typeids[1])
+                        && can_coerce_type(1, &input_typeids[0], &current_typeids[0]))
+                       {
+#ifdef PARSEDEBUG
+printf("oper_select_candidate: (5) choose (%s,%s) -> (%s,%s)...\n",
+ typeidTypeName(input_typeids[0]), typeidTypeName(input_typeids[1]),
+ typeidTypeName(current_typeids[0]), typeidTypeName(current_typeids[1]));
+#endif
+                               return (current_candidate->args);
+                       }
+                       else
+                       {
+#ifdef PARSEDEBUG
+printf("oper_select_candidate: (3) skip (%s,%s) -> (%s,%s)...\n",
+ typeidTypeName(input_typeids[0]), typeidTypeName(input_typeids[1]),
+ typeidTypeName(current_typeids[0]), typeidTypeName(current_typeids[1]));
+#endif
+                       }
+               }
+
+       return (NULL);
+#if FALSE
+       return (candidates->args);
+#endif
+} /* oper_select_candidate() */
+#endif
+
+
+/* oper_select_candidate()
+ * 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.
+ *
+ * If all input Oids are UNKNOWNOID, then try matching with TEXTOID.
+ * Otherwise, could return first function arguments on list of candidates.
+ * But for now, return NULL and make the user give a better hint.
+ * - thomas 1998-03-17
+ */
+Oid *
+oper_select_candidate(int nargs,
+                                         Oid *input_typeids,
+                                         CandidateList candidates)
+{
+       CandidateList   current_candidate;
+       CandidateList   last_candidate;
+       Oid                        *current_typeids;
+       int                             unknownOids;
+       int                             i;
+
+       int                             ncandidates;
+       int                             nbestMatch,
+                                       nmatch;
+
+       CATEGORY                slot_category,
+                                       current_category;
+       Oid                             slot_type,
+                                       current_type;
+
+/*
+ * Run through all candidates and keep those with the most matches
+ *  on explicit types. Keep all candidates if none match.
+ */
+       ncandidates = 0;
+       nbestMatch = 0;
+       last_candidate = NULL;
+       for (current_candidate = candidates;
+                current_candidate != NULL;
+                current_candidate = current_candidate->next)
+       {
+               current_typeids = current_candidate->args;
+               nmatch = 0;
+               for (i = 0; i < nargs; i++)
+               {
+                       if ((input_typeids[i] != UNKNOWNOID)
+                        && (current_typeids[i] == input_typeids[i]))
+                       {
+                               nmatch++;
+                       }
+               }
+
+#ifdef PARSEDEBUG
+printf("oper_select_candidate- candidate has %d matches\n", nmatch);
+#endif
+               if ((nmatch > nbestMatch) || (last_candidate == NULL))
+               {
+                       nbestMatch = nmatch;
+                       candidates = current_candidate;
+                       last_candidate = current_candidate;
+                       ncandidates = 1;
+#ifdef PARSEDEBUG
+printf("oper_select_candidate- choose candidate as best match\n");
+#endif
+               }
+               else if (nmatch == nbestMatch)
+               {
+                       last_candidate->next = current_candidate;
+                       last_candidate = current_candidate;
+                       ncandidates++;
+#ifdef PARSEDEBUG
+printf("oper_select_candidate- choose candidate as possible match\n");
+#endif
+               }
+               else
+               {
+                       last_candidate->next = NULL;
+#ifdef PARSEDEBUG
+printf("oper_select_candidate- reject candidate as possible match\n");
+#endif
+               }
+       }
+
+       if (ncandidates <= 1)
+               return ((ncandidates == 1)? candidates->args: NULL);
+
+/*
+ * Now look for candidates which allow coersion and are preferred types.
+ * Keep all candidates if none match.
+ */
+       ncandidates = 0;
+       nbestMatch = 0;
+       last_candidate = NULL;
+       for (current_candidate = candidates;
+                current_candidate != NULL;
+                current_candidate = current_candidate->next)
+       {
+               current_typeids = current_candidate->args;
+               nmatch = 0;
+               for (i = 0; i < nargs; i++)
+               {
+                       current_category = TypeCategory(current_typeids[i]);
+                       if (input_typeids[i] != UNKNOWNOID)
+                       {
+                               if (current_typeids[i] == input_typeids[i])
+                               {
+                                       nmatch++;
+                               }
+                               else if (IsPreferredType(current_category, current_typeids[i])
+                                && can_coerce_type(1, &input_typeids[i], &current_typeids[i]))
+                               {
+                                       nmatch++;
+                               }
+                       }
+               }
+
+#ifdef PARSEDEBUG
+printf("oper_select_candidate- candidate has %d matches\n", nmatch);
+#endif
+               if ((nmatch > nbestMatch) || (last_candidate == NULL))
+               {
+                       nbestMatch = nmatch;
+                       candidates = current_candidate;
+                       last_candidate = current_candidate;
+                       ncandidates = 1;
+#ifdef PARSEDEBUG
+printf("oper_select_candidate- choose candidate as best match\n");
+#endif
+               }
+               else if (nmatch == nbestMatch)
+               {
+                       last_candidate->next = current_candidate;
+                       last_candidate = current_candidate;
+                       ncandidates++;
+#ifdef PARSEDEBUG
+printf("oper_select_candidate- choose candidate as possible match\n");
+#endif
+               }
+               else
+               {
+                       last_candidate->next = NULL;
+#ifdef PARSEDEBUG
+printf("oper_select_candidate- reject candidate as possible match\n");
+#endif
+               }
+       }
+
+       if (ncandidates <= 1)
+               return ((ncandidates == 1)? candidates->args: NULL);
+
+/*
+ * Still too many candidates?
+ * Try assigning types for the unknown columns.
+ */
+       if (ncandidates > 1)
+       {
+               unknownOids = FALSE;
+               current_type = UNKNOWNOID;
+               for (i = 0; i < nargs; i++)
+               {
+                       if (input_typeids[i] != UNKNOWNOID)
+                       {
+                               current_type = input_typeids[i];
+                       }
+                       else
+                       {
+                               unknownOids = TRUE;
+                       }
+               }
+
+               if (unknownOids && (current_type != UNKNOWNOID))
+               {
+                       for (current_candidate = candidates;
+                                current_candidate != NULL;
+                                current_candidate = current_candidate->next)
+                       {
+                               nmatch = 0;
+                               for (i = 0; i < nargs; i++)
+                               {
+                                       current_typeids = current_candidate->args;
+                                       if ((current_type == current_typeids[i])
+                                        || IS_BINARY_COMPATIBLE(current_type, current_typeids[i]))
+                                               nmatch++;
+                               }
+                               if (nmatch == nargs)
+                                       return (candidates->args);
+                       }
+               }
+
+               for (i = 0; i < nargs; i++)
+               {
+                       if (input_typeids[i] == UNKNOWNOID)
+                       {
+                               slot_category = INVALID_TYPE;
+                               slot_type = InvalidOid;
+                               for (current_candidate = candidates;
+                                        current_candidate != NULL;
+                                        current_candidate = current_candidate->next)
+                               {
+                                       current_typeids = current_candidate->args;
+                                       current_type = current_typeids[i];
+                                       current_category = TypeCategory(current_typeids[i]);
+                                       if (slot_category == InvalidOid)
+                                       {
+                                               slot_category = current_category;
+                                               slot_type = current_type;
+#ifdef PARSEDEBUG
+printf("oper_select_candidate- assign column #%d first candidate slot type %s\n",
+ i, typeidTypeName(current_type));
+#endif
+                                       }
+                                       else if (current_category != slot_category)
+                                       {
+#ifdef PARSEDEBUG
+printf("oper_select_candidate- multiple possible types for column #%d; unable to choose candidate\n", i);
+#endif
+                                               return NULL;
+                                       }
+                                       else if (current_type != slot_type)
+                                       {
+                                               if (IsPreferredType(slot_category, current_type))
+                                               {
+                                                       slot_type = current_type;
+                                                       candidates = current_candidate;
+#ifdef PARSEDEBUG
+printf("oper_select_candidate- column #%d found preferred candidate type %s\n",
+ i, typeidTypeName(slot_type));
+#endif
+                                               }
+                                               else
+                                               {
+#ifdef PARSEDEBUG
+printf("oper_select_candidate- column #%d found possible candidate type %s\n",
+ i, typeidTypeName(current_type));
+#endif
+                                               }
+                                       }
+                               }
+
+                               if (slot_type != InvalidOid)
+                               {
+                                       input_typeids[i] = slot_type;
+#ifdef PARSEDEBUG
+printf("oper_select_candidate- assign column #%d slot type %s\n",
+ i, typeidTypeName(input_typeids[i]));
+#endif
+                               }
+                       }
+                       else
+                       {
+#ifdef PARSEDEBUG
+printf("oper_select_candidate- column #%d input type is %s\n",
+ i, typeidTypeName(input_typeids[i]));
+#endif
+                       }
+               }
+
+               ncandidates = 0;
+               for (current_candidate = candidates;
+                        current_candidate != NULL;
+                        current_candidate = current_candidate->next)
+               {
+                       ncandidates++;
+               }
+       }
+
+       if (ncandidates == 1)
+               return (candidates->args);
+
+       return (NULL);
+} /* oper_select_candidate() */
+
+
+/* func_get_detail()
+ * Find the named function in the system catalogs.
+ *
+ * Attempt to find the named function in the system catalogs with
+ *  arguments exactly as specified, so that the normal case
+ *  (exact match) is as quick as possible.
+ *
+ * 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
+ *   c) if the answer is more than one, attempt to resolve the conflict
+ *   d) if the answer is zero, try the next array from vector #1
+ */
 static 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 *funcid,            /* return value */
+                               Oid *rettype,           /* return value */
+                               bool *retset,           /* return value */
+                               Oid **true_typeids)     /* return value */
 {
        Oid               **input_typeid_vector;
        Oid                *current_input_typeids;
@@ -672,11 +1259,7 @@ func_get_detail(char *funcname,
        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
-        */
+       /* attempt to find with arguments exactly as specified... */
        ftup = SearchSysCacheTuple(PRONAME,
                                                           PointerGetDatum(funcname),
                                                           Int32GetDatum(nargs),
@@ -684,24 +1267,15 @@ func_get_detail(char *funcname,
                                                           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
-        */
+       /* didn't find an exact match, so now try to match up candidates... */
        if (!HeapTupleIsValid(ftup))
        {
                function_typeids = func_get_candidates(funcname, nargs);
 
+               /* found something, so let's look through them... */
                if (function_typeids != NULL)
                {
-                       int                     ncandidates = 0;
+                       int                     ncandidates;
 
                        input_typeid_vector = argtype_inherit(nargs, oid_array);
                        current_input_typeids = oid_array;
@@ -711,36 +1285,41 @@ func_get_detail(char *funcname,
                                ncandidates = match_argtypes(nargs, current_input_typeids,
                                                                                         function_typeids,
                                                                                         &current_function_typeids);
+
+                               /* one match only? then run with it... */
                                if (ncandidates == 1)
                                {
                                        *true_typeids = current_function_typeids->args;
                                        ftup = SearchSysCacheTuple(PRONAME,
                                                                                           PointerGetDatum(funcname),
                                                                                           Int32GetDatum(nargs),
-                                                                                 PointerGetDatum(*true_typeids),
+                                                                                          PointerGetDatum(*true_typeids),
                                                                                           0);
                                        Assert(HeapTupleIsValid(ftup));
                                }
+
+                               /* multiple candidates? then better decide or throw an error... */
                                else if (ncandidates > 1)
                                {
-                                       *true_typeids =
-                                               func_select_candidate(nargs,
-                                                                                         current_input_typeids,
-                                                                                         current_function_typeids);
+                                       *true_typeids = func_select_candidate(nargs,
+                                                                                                                 current_input_typeids,
+                                                                                                                 current_function_typeids);
+
+                                       /* couldn't decide, so quit */
                                        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(NULL, funcname, nargs, oid_array);
+                                               func_error(NULL, funcname, nargs, oid_array,
+                                                       "There is more than one function that satisfies the given argument types"
+                                                       "\n\tYou will have to retype your query using explicit typecasts");
                                        }
+
+                                       /* found something, so use the first one... */
                                        else
                                        {
                                                ftup = SearchSysCacheTuple(PRONAME,
-                                                                                          PointerGetDatum(funcname),
+                                                                                                  PointerGetDatum(funcname),
                                                                                                   Int32GetDatum(nargs),
-                                                                                 PointerGetDatum(*true_typeids),
+                                                                                                  PointerGetDatum(*true_typeids),
                                                                                                   0);
                                                Assert(HeapTupleIsValid(ftup));
                                        }
@@ -752,6 +1331,22 @@ func_get_detail(char *funcname,
                }
        }
 
+#if FALSE
+       /* Last-ditch attempt
+     * See if this is a single argument function with the function name
+     *  also a type name and the input argument and type name binary compatible...
+        */
+       if (!HeapTupleIsValid(ftup) && (nargs == 1))
+       {
+               Type    ttup;
+
+               if ((HeapTupleIsValid(ttup = SearchSysCacheTuple(TYPNAME, PointerGetDatum(funcname), 0, 0, 0)))
+                && IS_BINARY_COMPATIBLE(typeTypeId(ttup), oid_array[0]))
+               {
+               }
+       }
+#endif
+
        if (!HeapTupleIsValid(ftup))
        {
                Type            tp;
@@ -760,10 +1355,11 @@ func_get_detail(char *funcname,
                {
                        tp = typeidType(oid_array[0]);
                        if (typeTypeFlag(tp) == 'c')
-                               elog(ERROR, "no such attribute or function \"%s\"",
-                                        funcname);
+                               elog(ERROR, "no such attribute or function '%s'", funcname);
                }
-               func_error(NULL, funcname, nargs, oid_array);
+#if FALSE
+               func_error(NULL, funcname, nargs, oid_array, NULL);
+#endif
        }
        else
        {
@@ -774,10 +1370,10 @@ func_get_detail(char *funcname,
 
                return (true);
        }
-/* shouldn't reach here */
-       return (false);
 
-}
+       /* shouldn't reach here */
+       return (false);
+}      /* func_get_detail() */
 
 /*
  *     argtype_inherit() -- Construct an argtype vector reflecting the
@@ -1012,26 +1608,27 @@ gen_cross_product(InhPaths *arginh, int nargs)
 }
 
 
-/*
- ** make_arguments --
- **   Given the number and types of arguments to a function, and the
- **   actual arguments and argument types, do the necessary typecasting.
+/* make_arguments()
+ * Given the number and types of arguments to a function, and the
+ *  actual arguments and argument types, do the necessary typecasting.
+ *
+ * There are two ways an input typeid can differ from a function typeid:
+ *  1) the input type inherits the function type, so no typecasting required
+ *  2) the input type can be typecast into the function type
+ * Right now, we only typecast unknowns, and that is all we check for.
+ *
+ * func_get_detail() now can find coersions for function arguments which
+ *  will make this function executable. So, we need to recover these
+ *  results here too.
+ * - thomas 1998-03-25
  */
 static void
-make_arguments(int nargs,
+make_arguments(ParseState *pstate,
+                          int nargs,
                           List *fargs,
                           Oid *input_typeids,
                           Oid *function_typeids)
 {
-
-       /*
-        * there are two ways an input typeid can differ from a function
-        * typeid : either the input type inherits the function type, so no
-        * typecasting is necessary, or the input type can be typecast into
-        * the function type. right now, we only typecast unknowns, and that
-        * is all we check for.
-        */
-
        List       *current_fargs;
        int                     i;
 
@@ -1039,7 +1636,7 @@ make_arguments(int nargs,
                 i < nargs;
                 i++, current_fargs = lnext(current_fargs))
        {
-
+               /* unspecified type for string constant? then use heuristics for conversion... */
                if (input_typeids[i] == UNKNOWNOID && function_typeids[i] != InvalidOid)
                {
                        lfirst(current_fargs) =
@@ -1048,6 +1645,15 @@ make_arguments(int nargs,
                                                                 typeidType(function_typeids[i]),
                                                                 -1);
                }
+
+               /* types don't match? then force coersion using a function call... */
+               else if (input_typeids[i] != function_typeids[i])
+               {
+                       lfirst(current_fargs) = coerce_type(pstate,
+                                                                                               lfirst(current_fargs),
+                                                                                               input_typeids[i],
+                                                                                               function_typeids[i]);
+               }
        }
 }
 
@@ -1070,7 +1676,8 @@ setup_tlist(char *attname, Oid relid)
 
        attno = get_attnum(relid, attname);
        if (attno < 0)
-               elog(ERROR, "cannot reference attribute '%s' of tuple params/return values for functions", attname);
+               elog(ERROR, "cannot reference attribute '%s'"
+                " of tuple params/return values for functions", attname);
 
        typeid = get_atttype(relid, attno);
        type_mod = get_atttypmod(relid, attno);
@@ -1173,8 +1780,7 @@ ParseComplexProjection(ParseState *pstate,
                                        }
                                        else
                                        {
-                                               elog(ERROR,
-                                                        "Function '%s' has bad returntype %d",
+                                               elog(ERROR, "Function '%s' has bad returntype %d",
                                                         funcname, argtype);
                                        }
                                }
@@ -1285,7 +1891,7 @@ ParseComplexProjection(ParseState *pstate,
  * argument types
  */
 void
-func_error(char *caller, char *funcname, int nargs, Oid *argtypes)
+func_error(char *caller, char *funcname, int nargs, Oid *argtypes, char *msg)
 {
        char            p[(NAMEDATALEN + 2) * MAXFMGRARGS],
                           *ptr;
@@ -1312,10 +1918,12 @@ func_error(char *caller, char *funcname, int nargs, Oid *argtypes)
 
        if (caller == NULL)
        {
-               elog(ERROR, "function %s(%s) does not exist", funcname, p);
+               elog(ERROR, "function '%s(%s)' does not exist%s%s",
+                funcname, p, ((msg != NULL)? "\n\t": ""), ((msg != NULL)? msg: ""));
        }
        else
        {
-               elog(ERROR, "%s: function %s(%s) does not exist", caller, funcname, p);
+               elog(ERROR, "%s: function '%s(%s)' does not exist%s%s",
+                caller, funcname, p, ((msg != NULL)? "\n\t": ""), ((msg != NULL)? msg: ""));
        }
 }
index 3a36fcd..140ca3d 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.14 1998/02/26 04:33:32 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.15 1998/05/09 23:29:53 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -39,7 +39,7 @@ make_operand(char *opname,
 /*
  * make_parsestate() --
  *       allocate and initialize a new ParseState.
- *     the CALLERS is responsible for freeing the ParseState* returned
+ *     the CALLER is responsible for freeing the ParseState* returned
  *
  */
 
@@ -57,6 +57,15 @@ make_parsestate(ParseState *parentParseState)
        return (pstate);
 }
 
+
+extern
+Node *
+coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId);
+
+
+/* make_operand()
+ * Ensure argument type match by forcing conversion of constants.
+ */
 static Node *
 make_operand(char *opname,
                         Node *tree,
@@ -65,35 +74,33 @@ make_operand(char *opname,
 {
        Node       *result;
        Type            true_type;
+#if FALSE
        Datum           val;
        Oid                     infunc;
+#endif
 
+#ifdef PARSEDEBUG
+printf("make_operand: constructing operand for '%s' %s->%s\n",
+ opname, typeidTypeName(orig_typeId), typeidTypeName(true_typeId));
+#endif
        if (tree != NULL)
        {
                result = tree;
                true_type = typeidType(true_typeId);
                disallow_setop(opname, true_type, result);
+
+               /* must coerce? */
                if (true_typeId != orig_typeId)
-               {                                               /* must coerce */
-                       Const      *con = (Const *) result;
-
-                       Assert(nodeTag(result) == T_Const);
-                       val = (Datum) textout((struct varlena *)
-                                                                 con->constvalue);
-                       infunc = typeidInfunc(true_typeId);
-                       con = makeNode(Const);
-                       con->consttype = true_typeId;
-                       con->constlen = typeLen(true_type);
-                       con->constvalue = (Datum) fmgr(infunc,
-                                                                                  val,
-                                                                                  typeidTypElem(true_typeId),
-                                                                                  -1 /* for varchar() type */ );
-                       con->constisnull = false;
-                       con->constbyval = true;
-                       con->constisset = false;
-                       result = (Node *) con;
+               {
+#ifdef PARSEDEBUG
+printf("make_operand: try to convert node from %s to %s\n",
+ typeidTypeName(orig_typeId), typeidTypeName(true_typeId));
+#endif
+                       result = coerce_type(NULL, tree, orig_typeId, true_typeId);
                }
        }
+
+       /* otherwise, this is a NULL value */
        else
        {
                Const      *con = makeNode(Const);
@@ -108,7 +115,7 @@ make_operand(char *opname,
        }
 
        return result;
-}
+} /* make_operand() */
 
 
 static void
@@ -119,13 +126,49 @@ disallow_setop(char *op, Type optype, Node *operand)
 
        if (nodeTag(operand) == T_Iter)
        {
-               elog(NOTICE, "An operand to the '%s' operator returns a set of %s,",
-                        op, typeTypeName(optype));
-               elog(ERROR, "but '%s' takes single values, not sets.",
-                        op);
+               elog(ERROR, "An operand to the '%s' operator returns a set of %s,"
+                        "\n\tbut '%s' takes single values, not sets.",
+                        op, typeTypeName(optype), op);
        }
 }
 
+
+/* CoerceType()
+ * Try to force type of node.
+ */
+Oid CoerceType(Oid typeId, Node *node);
+
+Oid
+CoerceType(Oid typeId, Node *node)
+{
+       switch (nodeTag(node))
+       {
+               case T_Const:
+                       {
+                               Const      *con = (Const *) node;
+                       
+#ifdef PARSEDEBUG
+printf( "Convert node %d to text\n", nodeTag(node));
+#endif
+
+                               typeId = TEXTOID;
+                               con->consttype = typeId;
+                       }
+                       break;
+
+               default:
+                       break;
+       }
+       return typeId;
+} /* CoerceType() */
+
+
+/* make_op()
+ * Operator construction.
+ *
+ * Transform operator expression ensuring type compatibility.
+ * This is where some type conversion happens.
+ */
 Expr *
 make_op(char *opname, Node *ltree, Node *rtree)
 {
@@ -138,10 +181,9 @@ make_op(char *opname, Node *ltree, Node *rtree)
                           *right;
        Expr       *result;
 
+       /* right operator? */
        if (rtree == NULL)
        {
-
-               /* right operator */
                ltypeId = (ltree == NULL) ? UNKNOWNOID : exprType(ltree);
                temp = right_oper(opname, ltypeId);
                opform = (OperatorTupleForm) GETSTRUCT(temp);
@@ -149,25 +191,29 @@ make_op(char *opname, Node *ltree, Node *rtree)
                right = NULL;
 
        }
+
+       /* left operator? */
        else if (ltree == NULL)
        {
-
-               /* left operator */
                rtypeId = (rtree == NULL) ? UNKNOWNOID : exprType(rtree);
                temp = left_oper(opname, rtypeId);
+#ifdef PARSEDEBUG
+printf("make_op: returned from left_oper() with structure at %p\n", (void *)temp);
+#endif
                opform = (OperatorTupleForm) GETSTRUCT(temp);
+#ifdef PARSEDEBUG
+printf("make_op: calling make_operand()\n");
+#endif
                right = make_operand(opname, rtree, rtypeId, opform->oprright);
                left = NULL;
 
        }
+
+       /* otherwise, binary operator */
        else
        {
-               char       *outstr;
-               Oid                     infunc,
-                                       outfunc;
-               Type            newtype;
 
-#define CONVERTABLE_TYPE(t) (  (t) == INT2OID || \
+#define CONVERTIBLE_TYPE(t) (  (t) == INT2OID || \
                                                                (t) == INT4OID || \
                                                                (t) == OIDOID || \
                                                                (t) == FLOAT4OID || \
@@ -178,12 +224,32 @@ make_op(char *opname, Node *ltree, Node *rtree)
                ltypeId = (ltree == NULL) ? UNKNOWNOID : exprType(ltree);
                rtypeId = (rtree == NULL) ? UNKNOWNOID : exprType(rtree);
 
+#if FALSE
+               /* Both operands of unknown type?
+                * Then they are strings and we should force at least one to text
+                * - thomas 1998-03-16
+                */
+               ltypeId = exprType(ltree);
+               rtypeId = exprType(rtree);
+
+               if ((ltypeId == UNKNOWNOID)
+                && (rtypeId == UNKNOWNOID))
+               {
+#ifdef PARSEDEBUG
+printf( "Convert left-hand constant to text for node %d\n", nodeTag(ltree));
+#endif
+
+                       ltypeId = CoerceType(TEXTOID, ltree);
+               }
+#endif
+
+#if FALSE
                /*
                 * convert constant when using a const of a numeric type and a
                 * non-const of another numeric type
                 */
-               if (CONVERTABLE_TYPE(ltypeId) && nodeTag(ltree) != T_Const &&
-                       CONVERTABLE_TYPE(rtypeId) && nodeTag(rtree) == T_Const &&
+               if (CONVERTIBLE_TYPE(ltypeId) && nodeTag(ltree) != T_Const &&
+                       CONVERTIBLE_TYPE(rtypeId) && nodeTag(rtree) == T_Const &&
                        !((Const *) rtree)->constiscast)
                {
                        outfunc = typeidOutfunc(rtypeId);
@@ -197,8 +263,8 @@ make_op(char *opname, Node *ltree, Node *rtree)
                        ((Const *) rtree)->constbyval = typeByVal(newtype);
                }
 
-               if (CONVERTABLE_TYPE(rtypeId) && nodeTag(rtree) != T_Const &&
-                       CONVERTABLE_TYPE(ltypeId) && nodeTag(ltree) == T_Const &&
+               if (CONVERTIBLE_TYPE(rtypeId) && nodeTag(rtree) != T_Const &&
+                       CONVERTIBLE_TYPE(ltypeId) && nodeTag(ltree) == T_Const &&
                        !((Const *) ltree)->constiscast)
                {
                        outfunc = typeidOutfunc(ltypeId);
@@ -211,6 +277,7 @@ make_op(char *opname, Node *ltree, Node *rtree)
                        ((Const *) ltree)->constlen = typeLen(newtype);
                        ((Const *) ltree)->constbyval = typeByVal(newtype);
                }
+#endif
 
                temp = oper(opname, ltypeId, rtypeId, false);
                opform = (OperatorTupleForm) GETSTRUCT(temp);
@@ -219,8 +286,8 @@ make_op(char *opname, Node *ltree, Node *rtree)
        }
 
        newop = makeOper(oprid(temp),           /* opno */
-                                        InvalidOid,/* opid */
-                                        opform->oprresult, /* operator result type */
+                                        InvalidOid,            /* opid */
+                                        opform->oprresult,     /* operator result type */
                                         0,
                                         NULL);
 
@@ -239,6 +306,7 @@ make_op(char *opname, Node *ltree, Node *rtree)
        return result;
 }
 
+
 Var *
 make_var(ParseState *pstate, Oid relid, char *refname,
                 char *attrname)
@@ -356,6 +424,9 @@ make_array_ref(Node *expr,
        return aref;
 }
 
+
+/* make_array_set()
+ */
 ArrayRef   *
 make_array_set(Expr *target_expr,
                           List *upperIndexpr,
@@ -406,10 +477,12 @@ make_array_set(Expr *target_expr,
        aref->refexpr = (Node *) target_expr;
        aref->refassgnexpr = (Node *) expr;
 
-       if (lowerIndexpr == NIL)        /* accessing a single array element */
+       /* accessing a single array element? */
+       if (lowerIndexpr == NIL)
                reftype = aref->refelemtype;
+
+       /* otherwise, request to set a part of the array, by another array */
        else
-/* request to set a part of the array, by another array */
                reftype = typearray;
 
        aref->refelemtype = reftype;
index e2eb6b9..2e43b13 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.10 1998/04/27 04:06:09 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.11 1998/05/09 23:29:53 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "fmgr.h"
 #include "parser/parse_oper.h"
 #include "parser/parse_type.h"
+#include "parser/parse_coerce.h"
 #include "storage/bufmgr.h"
 #include "utils/syscache.h"
 
+extern
+Oid *
+func_select_candidate(int nargs, Oid *input_typeids, CandidateList candidates);
+
+extern
+Oid *
+oper_select_candidate(int nargs, Oid *input_typeids, CandidateList candidates);
+
 static int
 binary_oper_get_candidates(char *opname,
                                                   Oid leftTypeId,
@@ -63,7 +72,8 @@ oprid(Operator op)
        return (op->t_oid);
 }
 
-/*
+
+/* binary_oper_get_candidates()
  *     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
@@ -97,7 +107,145 @@ binary_oper_get_candidates(char *opname,
                                                   F_CHAREQ,
                                                   CharGetDatum('b'));
 
+#if FALSE
+       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;
+#endif
+
+       nkeys = 2;
+
+       pg_operator_desc = heap_openr(OperatorRelationName);
+       pg_operator_scan = heap_beginscan(pg_operator_desc,
+                                                                         0,
+                                                                         true,
+                                                                         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;
+} /* binary_oper_get_candidates() */
+
+
+#if FALSE
+/* BinaryOperCandidates()
+ *     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
+BinaryOperCandidates(char *opname,
+                                        Oid lTypeId,
+                                        Oid rTypeId,
+                                        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];
+
+       /* Can we promote the lesser type and find a match? */
+       lCandidateTypeId = lTypeId;
+       rCandidateTypeId = rTypeId;
+       higherTypeId = PromoteLowerType(&lCandidateTypeId, &rCandidateTypeId);
+       if (lTypeId != higherTypeId)
+               lowerTypeId = lTypeId;
+       else
+               lowerTypeId = rTypeId;
+
+       while (lCandidateTypeId != rCandidateTypeId)
+               if ((lCandidateTypeId == InvalidOid) || (rCandidateTypeId == InvalidOid))
+                       break;
+
+               tup = SearchSysCacheTuple(OPRNAME,
+                                                                 PointerGetDatum(op),
+                                                                 ObjectIdGetDatum(lCandidateTypeId),
+                                                                 ObjectIdGetDatum(rCandidateTypeId),
+                                                                 Int8GetDatum('b'));
+               if (HeapTupleIsValid(tup))
+                       return ((Operator) tup);
+
+               PromoteLowerType(&lCandidateTypeId, &rCandidateTypeId);
+       }
 
+       /* Can we promote the lesser type directly to the other? */
+       if (can_coerce_type(lowerTypeId, higherTypeId))
+       {
+               tup = SearchSysCacheTuple(OPRNAME,
+                                                                 PointerGetDatum(op),
+                                                                 ObjectIdGetDatum(higherTypeId),
+                                                                 ObjectIdGetDatum(higherTypeId),
+                                                                 Int8GetDatum('b'));
+               if (HeapTupleIsValid(tup))
+                       return ((Operator) tup);
+       }
+
+
+       *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 FALSE
        if (leftTypeId == UNKNOWNOID)
        {
                if (rightTypeId == UNKNOWNOID)
@@ -124,8 +272,12 @@ binary_oper_get_candidates(char *opname,
                                                           ObjectIdGetDatum(leftTypeId));
        }
        else
+       {
                /* currently only "unknown" can be coerced */
                return 0;
+#endif
+
+       nkeys = 2;
 
        pg_operator_desc = heap_openr(OperatorRelationName);
        pg_operator_scan = heap_beginscan(pg_operator_desc,
@@ -156,7 +308,9 @@ binary_oper_get_candidates(char *opname,
        heap_close(pg_operator_desc);
 
        return ncandidates;
-}
+} /* BinaryOperCandidates() */
+#endif
+
 
 /*
  * equivalentOpersAfterPromotion -
@@ -164,7 +318,7 @@ binary_oper_get_candidates(char *opname,
  *       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
+ *       all the candidates operate on the same data types after
  *       promotion (int2, int4, float4 -> float8).
  */
 static bool
@@ -237,9 +391,33 @@ equivalentOpersAfterPromotion(CandidateList candidates)
 }
 
 
-/*
- *     given a choice of argument type pairs for a binary operator,
- *     try to choose a default pair
+/* binary_oper_select_candidate()
+ * Given a choice of argument type pairs for a binary operator,
+ *  try to choose a default pair.
+ *
+ * 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 was commented out since then - ay]
+ *
+ * 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
  */
 static CandidateList
 binary_oper_select_candidate(Oid arg1,
@@ -249,37 +427,11 @@ binary_oper_select_candidate(Oid arg1,
        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 both are "unknown", there is no way to select a candidate
         */
-
        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;
 
@@ -296,90 +448,102 @@ binary_oper_select_candidate(Oid arg1,
        return (NULL);
 }
 
-/* Given operator, types of arg1, and arg2, return oper struct */
-/* arg1, arg2 --typeids */
+/* oper()
+ * Given operator, types of arg1, and arg2, return oper struct.
+ * Inputs:
+ * arg1, arg2: Type IDs
+ */
 Operator
 oper(char *op, Oid arg1, Oid arg2, bool noWarnings)
 {
-       HeapTuple       tup;
-       CandidateList candidates;
-       int                     ncandidates;
-
-       if (!arg2)
+       HeapTuple               tup;
+       CandidateList   candidates;
+       int                             ncandidates;
+       Oid                        *targetOids;
+       Oid                             inputOids[2];
+
+       /* Unspecified type for one of the arguments? then use the other */
+       if (arg2 == InvalidOid)
                arg2 = arg1;
-       if (!arg1)
+       if (arg1 == InvalidOid)
                arg1 = arg2;
 
-       if (!(tup = SearchSysCacheTuple(OPRNAME,
-                                                                       PointerGetDatum(op),
-                                                                       ObjectIdGetDatum(arg1),
-                                                                       ObjectIdGetDatum(arg2),
-                                                                       Int8GetDatum('b'))))
+       tup = SearchSysCacheTuple(OPRNAME,
+                                                         PointerGetDatum(op),
+                                                         ObjectIdGetDatum(arg1),
+                                                         ObjectIdGetDatum(arg2),
+                                                         Int8GetDatum('b'));
+
+       /* Did not find anything? then look more carefully... */
+       if (!HeapTupleIsValid(tup))
        {
                ncandidates = binary_oper_get_candidates(op, arg1, arg2, &candidates);
+
+               /* No operators found? Then throw error or return null... */
                if (ncandidates == 0)
                {
-
-                       /*
-                        * no operators of the desired types found
-                        */
                        if (!noWarnings)
                                op_error(op, arg1, arg2);
                        return (NULL);
                }
+
+               /* Or found exactly one? Then proceed... */
                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]),
+                                                                         ObjectIdGetDatum(candidates->args[0]),
+                                                                         ObjectIdGetDatum(candidates->args[1]),
                                                                          Int8GetDatum('b'));
                        Assert(HeapTupleIsValid(tup));
                }
+
+               /* Otherwise, multiple operators of the desired types found... */
                else
                {
-
-                       /*
-                        * multiple operators of the desired types found
-                        */
+#if FALSE
                        candidates = binary_oper_select_candidate(arg1, arg2, candidates);
-                       if (candidates != NULL)
+#endif
+                       inputOids[0] = arg1;
+                       inputOids[1] = arg2;
+                       targetOids = oper_select_candidate(2, inputOids, candidates);
+#if FALSE
+                       targetOids = func_select_candidate(2, inputOids, candidates);
+#endif
+                       if (targetOids != NULL)
                        {
-                               /* we chose one of them */
+#if PARSEDEBUG
+printf("oper: found candidate\n");
+#endif
                                tup = SearchSysCacheTuple(OPRNAME,
                                                                                  PointerGetDatum(op),
-                                                                  ObjectIdGetDatum(candidates->args[0]),
-                                                                  ObjectIdGetDatum(candidates->args[1]),
+                                                                                 ObjectIdGetDatum(targetOids[0]),
+                                                                                 ObjectIdGetDatum(targetOids[1]),
                                                                                  Int8GetDatum('b'));
-                               Assert(HeapTupleIsValid(tup));
                        }
                        else
                        {
-                               Type            tp1,
-                                                       tp2;
+                               tup = NULL;
+                       }
 
-                               /* we chose none of them */
-                               tp1 = typeidType(arg1);
-                               tp2 = typeidType(arg2);
+                       /* Could not choose one, for whatever reason... */
+                       if (!HeapTupleIsValid(tup))
+                       {
                                if (!noWarnings)
                                {
-                                       elog(NOTICE, "there is more than one operator %s for types", op);
-                                       elog(NOTICE, "%s and %s. You will have to retype this query",
-                                                typeTypeName(tp1), typeTypeName(tp2));
-                                       elog(ERROR, "using an explicit cast");
+                                       elog(ERROR, "There is more than one operator '%s' for types '%s' and '%s'"
+                                               "\n\tYou will have to retype this query using an explicit cast",
+                                               op, typeTypeName(typeidType(arg1)), typeTypeName(typeidType(arg2)));
                                }
                                return (NULL);
                        }
                }
        }
        return ((Operator) tup);
-}
+} /* oper() */
 
-/*
+
+/* unary_oper_get_candidates()
  *     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
@@ -409,6 +573,7 @@ unary_oper_get_candidates(char *op,
        fmgr_info(F_CHAREQ, (FmgrInfo *) &opKey[1].sk_func);
        opKey[1].sk_argument = CharGetDatum(rightleft);
 
+#if FALSE
        /* currently, only "unknown" can be coerced */
 
        /*
@@ -419,7 +584,11 @@ unary_oper_get_candidates(char *op,
        {
                return 0;
        }
+#endif
 
+#ifdef PARSEDEBUG
+printf("unary_oper_get_candidates: start scan for '%s'\n", op);
+#endif
        pg_operator_desc = heap_openr(OperatorRelationName);
        pg_operator_scan = heap_beginscan(pg_operator_desc,
                                                                          0,
@@ -442,6 +611,10 @@ unary_oper_get_candidates(char *op,
                                current_candidate->args[0] = oper->oprright;
                        current_candidate->next = *candidates;
                        *candidates = current_candidate;
+#ifdef PARSEDEBUG
+printf("unary_oper_get_candidates: found candidate '%s' for type %s\n",
+ op, typeidTypeName(current_candidate->args[0]));
+#endif
                        ncandidates++;
                        ReleaseBuffer(buffer);
                }
@@ -450,32 +623,35 @@ unary_oper_get_candidates(char *op,
        heap_endscan(pg_operator_scan);
        heap_close(pg_operator_desc);
 
+#ifdef PARSEDEBUG
+printf("unary_oper_get_candidates: found %d candidates\n", ncandidates);
+#endif
        return ncandidates;
-}
+} /* unary_oper_get_candidates() */
+
 
 /* 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'))))
+       HeapTuple               tup;
+       CandidateList   candidates;
+       int                             ncandidates;
+       Oid                        *targetOid;
+
+       tup = SearchSysCacheTuple(OPRNAME,
+                                                         PointerGetDatum(op),
+                                                         ObjectIdGetDatum(arg),
+                                                         ObjectIdGetDatum(InvalidOid),
+                                                         Int8GetDatum('r'));
+
+       if (!HeapTupleIsValid(tup))
        {
                ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'r');
                if (ncandidates == 0)
                {
-                       elog(ERROR,
-                                "Can't find right op: %s for type %d", op, arg);
+                       elog(ERROR, "Can't find right op '%s' for type %d", op, arg);
                        return (NULL);
                }
                else if (ncandidates == 1)
@@ -489,38 +665,59 @@ right_oper(char *op, Oid arg)
                }
                else
                {
-                       elog(NOTICE, "there is more than one right operator %s", op);
-                       elog(NOTICE, "you will have to retype this query");
-                       elog(ERROR, "using an explicit cast");
-                       return (NULL);
+#if FALSE
+                       elog(ERROR, "There is more than one right operator %s"
+                               "\n\tYou will have to retype this query using an explicit cast", op);
+#endif
+                       targetOid = func_select_candidate(1, &arg, candidates);
+
+                       if (targetOid != NULL)
+                       {
+                               tup = SearchSysCacheTuple(OPRNAME,
+                                                                                 PointerGetDatum(op),
+                                                                                 ObjectIdGetDatum(InvalidOid),
+                                                                                 ObjectIdGetDatum(*targetOid),
+                                                                                 Int8GetDatum('r'));
+                       }
+                       else
+                       {
+                               tup = NULL;
+                       }
+
+                       if (!HeapTupleIsValid(tup))
+                       {
+                               elog(ERROR, "Unable to convert right operator '%s' from type %s to %s",
+                                        op, typeidTypeName(arg), typeidTypeName(*targetOid));
+                               return (NULL);
+                       }
                }
        }
        return ((Operator) tup);
-}
+} /* right_oper() */
+
 
 /* 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'))))
+       HeapTuple               tup;
+       CandidateList   candidates;
+       int                             ncandidates;
+       Oid                        *targetOid;
+
+       tup = SearchSysCacheTuple(OPRNAME,
+                                                         PointerGetDatum(op),
+                                                         ObjectIdGetDatum(InvalidOid),
+                                                         ObjectIdGetDatum(arg),
+                                                         Int8GetDatum('l'));
+
+       if (!HeapTupleIsValid(tup))
        {
                ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'l');
                if (ncandidates == 0)
                {
-                       elog(ERROR,
-                                "Can't find left op: %s for type %d", op, arg);
+                       elog(ERROR, "Can't find left op '%s' for type %d", op, arg);
                        return (NULL);
                }
                else if (ncandidates == 1)
@@ -528,22 +725,44 @@ left_oper(char *op, Oid arg)
                        tup = SearchSysCacheTuple(OPRNAME,
                                                                          PointerGetDatum(op),
                                                                          ObjectIdGetDatum(InvalidOid),
-                                                                  ObjectIdGetDatum(candidates->args[0]),
+                                                                         ObjectIdGetDatum(candidates->args[0]),
                                                                          Int8GetDatum('l'));
                        Assert(HeapTupleIsValid(tup));
+#ifdef PARSEDEBUG
+printf("left_oper: searched cache for single left oper candidate '%s %s'\n",
+ op, typeidTypeName((Oid) candidates->args[0]));
+#endif
                }
                else
                {
-                       elog(NOTICE, "there is more than one left operator %s", op);
-                       elog(NOTICE, "you will have to retype this query");
-                       elog(ERROR, "using an explicit cast");
-                       return (NULL);
+#if FALSE
+                       elog(ERROR, "There is more than one left operator %s"
+                               "\n\tYou will have to retype this query using an explicit cast", op);
+#endif
+                       targetOid = func_select_candidate(1, &arg, candidates);
+                       tup = SearchSysCacheTuple(OPRNAME,
+                                                                         PointerGetDatum(op),
+                                                                         ObjectIdGetDatum(InvalidOid),
+                                                                         ObjectIdGetDatum(*targetOid),
+                                                                         Int8GetDatum('l'));
+
+                       if (!HeapTupleIsValid(tup))
+                       {
+                               elog(ERROR, "Unable to convert left operator '%s' from type %s to %s",
+                                        op, typeidTypeName(arg), typeidTypeName(*targetOid));
+                               return (NULL);
+                       }
+#ifdef PARSEDEBUG
+printf("left_oper: searched cache for best left oper candidate '%s %s'\n",
+ op, typeidTypeName(*targetOid));
+#endif
                }
        }
        return ((Operator) tup);
-}
+} /* left_oper() */
 
-/*
+
+/* op_error()
  * Give a somewhat useful error message when the operator for two types
  * is not found.
  */
@@ -559,7 +778,8 @@ op_error(char *op, Oid arg1, Oid arg2)
        }
        else
        {
-               elog(ERROR, "left hand side of operator %s has an unknown type, probably a bad attribute name", op);
+               elog(ERROR, "Left hand side of operator '%s' has an unknown type"
+                       "\n\tProbably a bad attribute name", op);
        }
 
        if (typeidIsValid(arg2))
@@ -568,17 +788,10 @@ op_error(char *op, Oid arg1, Oid arg2)
        }
        else
        {
-               elog(ERROR, "right hand side of operator %s has an unknown type, probably a bad attribute name", op);
+               elog(ERROR, "Right hand side of operator %s has an unknown type"
+                       "\n\tProbably a bad attribute name", op);
        }
 
-#if FALSE
-       elog(NOTICE, "there is no operator %s for types %s and %s",
-                op, typeTypeName(tp1), typeTypeName(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(ERROR, "%s for %s and %s using CREATE OPERATOR",
-                op, typeTypeName(tp1), typeTypeName(tp2));
-#endif
        elog(ERROR, "There is no operator '%s' for types '%s' and '%s'"
                 "\n\tYou will either have to retype this query using an explicit cast,"
         "\n\tor you will have to define the operator using CREATE OPERATOR",
index b9ab916..e3d5654 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.11 1998/02/26 04:33:35 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.12 1998/05/09 23:29:54 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "parser/parse_target.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+extern
+bool can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids);
+
+extern
+Node *coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId);
 
 static List *expandAllTables(ParseState *pstate);
 static char *figureColname(Node *expr, Node *resval);
@@ -34,6 +41,16 @@ make_targetlist_expr(ParseState *pstate,
                                         char *colname,
                                         Node *expr,
                                         List *arrayRef);
+Node *
+size_target_expr(ParseState *pstate,
+                                Node *expr,
+                                Oid attrtype,
+                                int16 attrtypmod);
+Node *
+coerce_target_expr(ParseState *pstate,
+                                  Node *expr,
+                                  Oid type_id,
+                                  Oid attrtype);
 
 /*
  * transformTargetList -
@@ -110,8 +127,7 @@ transformTargetList(ParseState *pstate, List *targetlist)
                                                Relation        rd;
                                                Value      *constval;
 
-                                               if (exprType(expr) != UNKNOWNOID ||
-                                                       !IsA(expr, Const))
+                                               if (exprType(expr) != UNKNOWNOID || !IsA(expr, Const))
                                                        elog(ERROR, "yyparse: string constant expected");
 
                                                val = (char *) textout((struct varlena *)
@@ -123,15 +139,15 @@ transformTargetList(ParseState *pstate, List *targetlist)
 
                                                        aind->uidx = transformExpr(pstate, aind->uidx, EXPR_COLUMN_FIRST);
                                                        if (!IsA(aind->uidx, Const))
-                                                               elog(ERROR,
-                                                                        "Array Index for Append should be a constant");
+                                                               elog(ERROR, "Array Index for Append should be a constant");
+
                                                        uindx[i] = ((Const *) aind->uidx)->constvalue;
                                                        if (aind->lidx != NULL)
                                                        {
                                                                aind->lidx = transformExpr(pstate, aind->lidx, EXPR_COLUMN_FIRST);
                                                                if (!IsA(aind->lidx, Const))
-                                                                       elog(ERROR,
-                                                                                "Array Index for Append should be a constant");
+                                                                       elog(ERROR, "Array Index for Append should be a constant");
+
                                                                lindx[i] = ((Const *) aind->lidx)->constvalue;
                                                        }
                                                        else
@@ -140,6 +156,7 @@ transformTargetList(ParseState *pstate, List *targetlist)
                                                        }
                                                        if (lindx[i] > uindx[i])
                                                                elog(ERROR, "yyparse: lower index cannot be greater than upper index");
+
                                                        sprintf(str, "[%d:%d]", lindx[i], uindx[i]);
                                                        str += strlen(str);
                                                        i++;
@@ -151,11 +168,12 @@ transformTargetList(ParseState *pstate, List *targetlist)
                                                ndims = attnumAttNelems(rd, resdomno);
                                                if (i != ndims)
                                                        elog(ERROR, "yyparse: array dimensions do not match");
+
                                                constval = makeNode(Value);
                                                constval->type = T_String;
                                                constval->val.str = save_str;
                                                tent = make_targetlist_expr(pstate, res->name,
-                                                                                  (Node *) make_const(constval),
+                                                                                                       (Node *) make_const(constval),
                                                                                                        NULL);
                                                pfree(save_str);
                                        }
@@ -300,8 +318,7 @@ transformTargetList(ParseState *pstate, List *targetlist)
                                }
                        default:
                                /* internal error */
-                               elog(ERROR,
-                                        "internal error: do not know how to transform targetlist");
+                               elog(ERROR, "internal error: do not know how to transform targetlist");
                                break;
                }
 
@@ -321,11 +338,125 @@ transformTargetList(ParseState *pstate, List *targetlist)
 }
 
 
-/*
- * make_targetlist_expr -
- *       make a TargetEntry from an expression
+Node *
+coerce_target_expr(ParseState *pstate,
+                                  Node *expr,
+                                  Oid type_id,
+                                  Oid attrtype)
+{
+       if (can_coerce_type(1, &type_id, &attrtype))
+       {
+#ifdef PARSEDEBUG
+printf("parse_target: coerce type from %s to %s\n",
+ typeidTypeName(type_id), typeidTypeName(attrtype));
+#endif
+               expr = coerce_type(pstate, expr, type_id, attrtype);
+       }
+
+#ifndef DISABLE_STRING_HACKS
+       /* string hacks to get transparent conversions w/o explicit conversions */
+       else if ((attrtype == BPCHAROID) || (attrtype == VARCHAROID))
+       {
+               Oid text_id = TEXTOID;
+#ifdef PARSEDEBUG
+printf("parse_target: try coercing from %s to %s via text\n",
+ typeidTypeName(type_id), typeidTypeName(attrtype));
+#endif
+               if (type_id == TEXTOID)
+               {
+               }
+               else if (can_coerce_type(1, &type_id, &text_id))
+               {
+                       expr = coerce_type(pstate, expr, type_id, text_id);
+               }
+               else
+               {
+                       expr = NULL;
+               }
+       }
+#endif
+
+       else
+       {
+               expr = NULL;
+       }
+
+       return expr;
+} /* coerce_target_expr() */
+
+
+/* size_target_expr()
+ * Apparently going to a fixed-length string?
+ * Then explicitly size for storage...
+ */
+Node *
+size_target_expr(ParseState *pstate,
+                                Node *expr,
+                                Oid attrtype,
+                                int16 attrtypmod)
+{
+       int                     i;
+       HeapTuple       ftup;
+       char       *funcname;
+       Oid                     oid_array[8];
+
+       FuncCall   *func;
+       A_Const    *cons;
+
+#ifdef PARSEDEBUG
+printf("parse_target: ensure target fits storage\n");
+#endif
+       funcname = typeidTypeName(attrtype);
+       oid_array[0] = attrtype;
+       oid_array[1] = INT4OID;
+       for (i = 2; i < 8; i++) oid_array[i] = InvalidOid;
+
+#ifdef PARSEDEBUG
+printf("parse_target: look for conversion function %s(%s,%s)\n",
+ funcname, typeidTypeName(attrtype), typeidTypeName(INT4OID));
+#endif
+
+       /* attempt to find with arguments exactly as specified... */
+       ftup = SearchSysCacheTuple(PRONAME,
+                                                          PointerGetDatum(funcname),
+                                                          Int32GetDatum(2),
+                                                          PointerGetDatum(oid_array),
+                                                          0);
+
+       if (HeapTupleIsValid(ftup))
+       {
+#ifdef PARSEDEBUG
+printf("parse_target: found conversion function for sizing\n");
+#endif
+               func = makeNode(FuncCall);
+               func->funcname = funcname;
+
+               cons = makeNode(A_Const);
+               cons->val.type = T_Integer;
+               cons->val.val.ival = attrtypmod;
+               func->args = lappend( lcons(expr,NIL), cons);
+
+               expr = transformExpr(pstate, (Node *) func, EXPR_COLUMN_FIRST);
+       }
+#ifdef PARSEDEBUG
+       else
+       {
+printf("parse_target: no conversion function for sizing\n");
+       }
+#endif
+
+       return expr;
+} /* size_target_expr() */
+
+
+/* make_targetlist_expr()
+ * Make a TargetEntry from an expression
  *
  * arrayRef is a list of transformed A_Indices
+ *
+ * For type mismatches between expressions and targets, use the same
+ *  techniques as for function and operator type coersion.
+ * - thomas 1998-05-08
  */
 static TargetEntry *
 make_targetlist_expr(ParseState *pstate,
@@ -355,7 +486,6 @@ make_targetlist_expr(ParseState *pstate,
        /* Processes target columns that will be receiving results */
        if (pstate->p_is_insert || pstate->p_is_update)
        {
-
                /*
                 * insert or update query -- insert, update work only on one
                 * relation, so multiple occurence of same resdomno is bogus
@@ -368,91 +498,47 @@ make_targetlist_expr(ParseState *pstate,
                if ((arrayRef != NIL) && (lfirst(arrayRef) == NIL))
                        attrtype = GetArrayElementType(attrtype);
                attrtypmod = rd->rd_att->attrs[resdomno - 1]->atttypmod;
-#if 0
-               if (Input_is_string && Typecast_ok)
-               {
-                       Datum           val;
 
-                       if (type_id == typeTypeId(type("unknown")))
-                       {
-                               val = (Datum) textout((struct varlena *)
-                                                                         ((Const) lnext(expr))->constvalue);
-                       }
-                       else
-                       {
-                               val = ((Const) lnext(expr))->constvalue;
-                       }
-                       if (attrisset)
-                       {
-                               lnext(expr) = makeConst(attrtype,
-                                                                               attrlen,
-                                                                               val,
-                                                                               false,
-                                                                               true,
-                                                                               true,   /* is set */
-                                                                               false);
-                       }
-                       else
-                       {
-                               lnext(expr) =
-                                       makeConst(attrtype,
-                                                         attrlen,
-                                                         (Datum) fmgr(typeidInfunc(attrtype),
-                                                                          val, typeidTypElem(attrtype), -1),
-                                                         false,
-                                                         true /* Maybe correct-- 80% chance */ ,
-                                                         false,        /* is not a set */
-                                                         false);
-                       }
-               }
-               else if ((Typecast_ok) && (attrtype != type_id))
-               {
-                       lnext(expr) =
-                               parser_typecast2(expr, typeidType(attrtype));
-               }
-               else if (attrtype != type_id)
+               /* Check for InvalidOid since that seems to indicate a NULL constant... */
+               if (type_id != InvalidOid)
                {
-                       if ((attrtype == INT2OID) && (type_id == INT4OID))
-                               lfirst(expr) = lispInteger(INT2OID);    /* handle CASHOID too */
-                       else if ((attrtype == FLOAT4OID) && (type_id == FLOAT8OID))
-                               lfirst(expr) = lispInteger(FLOAT4OID);
-                       else
-                               elog(ERROR, "unequal type in tlist : %s \n", colname);
-               }
-
-               Input_is_string = false;
-               Input_is_integer = false;
-               Typecast_ok = true;
-#endif
-
-               if (attrtype != type_id)
-               {
-                       if (IsA(expr, Const))
+                       /* Mismatch on types? then try to coerce to target...  */
+                       if (attrtype != type_id)
                        {
-                               /* try to cast the constant */
+                               Oid typelem;
+
                                if (arrayRef && !(((A_Indices *) lfirst(arrayRef))->lidx))
                                {
-                                       /* updating a single item */
-                                       Oid                     typelem = typeidTypElem(attrtype);
-
-                                       expr = (Node *) parser_typecast2(expr,
-                                                                                                        type_id,
-                                                                                                        typeidType(typelem),
-                                                                                                        attrtypmod);
+                                       typelem = typeidTypElem(attrtype);
                                }
                                else
-                                       expr = (Node *) parser_typecast2(expr,
-                                                                                                        type_id,
-                                                                                                        typeidType(attrtype),
-                                                                                                        attrtypmod);
+                               {
+                                       typelem = attrtype;
+                               }
+
+                               expr = coerce_target_expr(pstate, expr, type_id, typelem);
+
+                               if (!HeapTupleIsValid(expr))
+                               {
+                                       elog(ERROR, "parser: attribute '%s' is of type '%s'"
+                                                " but expression is of type '%s'"
+                                                "\n\tYou will need to rewrite or cast the expression",
+                                                colname,
+                                                typeidTypeName(attrtype),
+                                                typeidTypeName(type_id));
+                               }
                        }
-                       else
+
+#ifdef PARSEDEBUG
+printf("parse_target: attrtypmod is %d\n", (int4) attrtypmod);
+#endif
+
+                       /* Apparently going to a fixed-length string?
+                        * Then explicitly size for storage...
+                        */
+                       if (attrtypmod > 0)
                        {
-                               /* currently, we can't handle casting of expressions */
-                               elog(ERROR, "parser: attribute '%s' is of type '%s' but expression is of type '%s'",
-                                        colname,
-                                        typeidTypeName(attrtype),
-                                        typeidTypeName(type_id));
+                               expr = size_target_expr(pstate, expr, attrtype, attrtypmod);
                        }
                }
 
@@ -467,8 +553,8 @@ make_targetlist_expr(ParseState *pstate,
                        att->relname = pstrdup(RelationGetRelationName(rd)->data);
                        att->attrs = lcons(makeString(colname), NIL);
                        target_expr = (Expr *) ParseNestedFuncOrColumn(pstate, att,
-                                                                                                  &pstate->p_last_resno,
-                                                                                                         EXPR_COLUMN_FIRST);
+                                                                                                                  &pstate->p_last_resno,
+                                                                                                                  EXPR_COLUMN_FIRST);
                        while (ar != NIL)
                        {
                                A_Indices  *ind = lfirst(ar);
@@ -514,7 +600,8 @@ make_targetlist_expr(ParseState *pstate,
        tent->expr = expr;
 
        return tent;
-}
+} /* make_targetlist_expr() */
+
 
 /*
  * makeTargetNames -
@@ -564,7 +651,7 @@ makeTargetNames(ParseState *pstate, List *cols)
                        attnameAttNum(pstate->p_target_relation, name);
                        foreach(nxt, lnext(tl))
                                if (!strcmp(name, ((Ident *) lfirst(nxt))->name))
-                               elog(ERROR, "Attribute '%s' should be specified only once", name);
+                                       elog(ERROR, "Attribute '%s' should be specified only once", name);
                }
        }
 
index 9019c62..eeceeab 100644 (file)
@@ -1,13 +1,13 @@
 /*-------------------------------------------------------------------------
  *
- * parse_type.h
+ * parse_type.c
  *             handle type operations for parser
  *
  * Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.8 1998/02/27 19:44:51 scrappy Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.9 1998/05/09 23:29:54 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 #include "fmgr.h"
 
+#include "nodes/nodes.h"
+#include "nodes/parsenodes.h"
+#include "nodes/primnodes.h"
+#include "parser/parse_node.h"
+
 #include "catalog/pg_type.h"
 #include "parser/parse_target.h"
 #include "parser/parse_type.h"
 #include "utils/syscache.h"
 
+
 /* check to see if a type id is valid,
  * returns true if it is. By using this call before calling
  * typeidType or typeidTypeName, more meaningful error messages
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
new file mode 100644 (file)
index 0000000..0d7aa70
--- /dev/null
@@ -0,0 +1,96 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_coerce.h
+ *
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: parse_coerce.h,v 1.1 1998/05/09 23:31:34 thomas Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PARSE_COERCE_H
+#define PARSE_COERCE_H
+
+typedef enum CATEGORY {
+       INVALID_TYPE,
+       UNKNOWN_TYPE,
+       BOOLEAN_TYPE,
+       STRING_TYPE,
+       NUMERIC_TYPE,
+       DATETIME_TYPE,
+       TIMESPAN_TYPE,
+       GEOMETRIC_TYPE,
+       USER_TYPE,
+       MIXED_TYPE
+} CATEGORY;
+
+
+#define IS_BUILTIN_TYPE(t) \
+                 (((t) == BOOLOID) \
+               || ((t) == BPCHAROID) \
+               || ((t) == VARCHAROID) \
+               || ((t) == TEXTOID) \
+               || ((t) == CASHOID) \
+               || ((t) == INT4OID) \
+               || ((t) == DATETIMEOID) \
+               || ((t) == FLOAT8OID) \
+               || ((t) == ABSTIMEOID) \
+               || ((t) == TIMESTAMPOID) \
+               || ((t) == RELTIMEOID))
+
+
+/* IS_BINARY_COMPATIBLE()
+ * Check for types with the same underlying binary representation.
+ * This allows us to cheat and directly exchange values without
+ *  going through the trouble of calling a conversion function.
+ */
+#define IS_BINARY_COMPATIBLE(a,b) \
+                 (((a) == BPCHAROID && (b) == TEXTOID) \
+               || ((a) == BPCHAROID && (b) == VARCHAROID) \
+               || ((a) == VARCHAROID && (b) == TEXTOID) \
+               || ((a) == VARCHAROID && (b) == BPCHAROID) \
+               || ((a) == TEXTOID && (b) == BPCHAROID) \
+               || ((a) == TEXTOID && (b) == VARCHAROID) \
+               || ((a) == CASHOID && (b) == INT4OID) \
+               || ((a) == INT4OID && (b) == CASHOID) \
+               || ((a) == DATETIMEOID && (b) == FLOAT8OID) \
+               || ((a) == FLOAT8OID && (b) == DATETIMEOID) \
+               || ((a) == ABSTIMEOID && (b) == TIMESTAMPOID) \
+               || ((a) == TIMESTAMPOID && (b) == ABSTIMEOID) \
+               || ((a) == ABSTIMEOID && (b) == INT4OID) \
+               || ((a) == INT4OID && (b) == ABSTIMEOID) \
+               || ((a) == RELTIMEOID && (b) == INT4OID) \
+               || ((a) == INT4OID && (b) == RELTIMEOID))
+
+/* IS_HIGHER_TYPE()
+ * These types are the most general in each of the type categories.
+ */
+#define IS_HIGHER_TYPE(t) \
+                 (((t) == TEXTOID) \
+               || ((t) == FLOAT8OID) \
+               || ((t) == TIMESPANOID) \
+               || ((t) == DATETIMEOID) \
+               || ((t) == POLYGONOID))
+
+/* IS_HIGHEST_TYPE()
+ * These types are the most general in each of the type categories.
+ * Since timespan and datetime overload so many functions, let's
+ *  give datetime the preference.
+ * Since text is a generic string type let's leave it out too.
+ */
+#define IS_HIGHEST_TYPE(t) \
+                 (((t) == FLOAT8OID) \
+               || ((t) == DATETIMEOID) \
+               || ((t) == TIMESPANOID))
+
+
+extern bool IsPreferredType(CATEGORY category, Oid type);
+extern Oid PreferredType(CATEGORY category, Oid type);
+extern CATEGORY TypeCategory(Oid type);
+
+extern bool can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids);
+extern Node *coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId);
+
+#endif                                                 /* PARSE_COERCE_H */
index 8b59613..fd6f700 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_func.h,v 1.8 1998/02/26 04:42:45 momjian Exp $
+ * $Id: parse_func.h,v 1.9 1998/05/09 23:31:34 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -49,6 +49,6 @@ extern Node *
 ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
                                  int *curr_resno, int precedence);
 
-extern void func_error(char *caller, char *funcname, int nargs, Oid *argtypes);
+extern void func_error(char *caller, char *funcname, int nargs, Oid *argtypes, char *msg);
 
 #endif                                                 /* PARSE_FUNC_H */