*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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);
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)
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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:
return (Node *) adt;
}
+
+/* parser_typecast2()
+ * Convert (only) constants to specified type.
+ */
Node *
parser_typecast2(Node *expr, Oid exprType, Type tp, int16 atttypmod)
{
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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"
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
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);
/*
* Parsing aggregates.
*/
+ Type tp;
Oid basetype;
/*
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));
* 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);
}
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
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;
return (funcrettype);
}
-/*
+
+/* func_get_candidates()
* get a list of all argument type vectors for which a function named
* funcname taking nargs arguments exists
*/
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
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));
}
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], ¤t_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], ¤t_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], ¤t_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], ¤t_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], ¤t_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;
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),
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;
ncandidates = match_argtypes(nargs, current_input_typeids,
function_typeids,
¤t_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));
}
}
}
+#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;
{
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
{
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
}
-/*
- ** 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;
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) =
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]);
+ }
}
}
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);
}
else
{
- elog(ERROR,
- "Function '%s' has bad returntype %d",
+ elog(ERROR, "Function '%s' has bad returntype %d",
funcname, argtype);
}
}
* 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;
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: ""));
}
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/*
* 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
*
*/
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,
{
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);
}
return result;
-}
+} /* make_operand() */
static void
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)
{
*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);
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 || \
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);
((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);
((Const *) ltree)->constlen = typeLen(newtype);
((Const *) ltree)->constbyval = typeByVal(newtype);
}
+#endif
temp = oper(opname, ltypeId, rtypeId, false);
opform = (OperatorTupleForm) GETSTRUCT(temp);
}
newop = makeOper(oprid(temp), /* opno */
- InvalidOid,/* opid */
- opform->oprresult, /* operator result type */
+ InvalidOid, /* opid */
+ opform->oprresult, /* operator result type */
0,
NULL);
return result;
}
+
Var *
make_var(ParseState *pstate, Oid relid, char *refname,
char *attrname)
return aref;
}
+
+/* make_array_set()
+ */
ArrayRef *
make_array_set(Expr *target_expr,
List *upperIndexpr,
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;
*
*
* 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,
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
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)
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,
heap_close(pg_operator_desc);
return ncandidates;
-}
+} /* BinaryOperCandidates() */
+#endif
+
/*
* equivalentOpersAfterPromotion -
* 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
}
-/*
- * 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,
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;
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
fmgr_info(F_CHAREQ, (FmgrInfo *) &opKey[1].sk_func);
opKey[1].sk_argument = CharGetDatum(rightleft);
+#if FALSE
/* currently, only "unknown" can be coerced */
/*
{
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,
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);
}
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)
}
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)
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.
*/
}
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))
}
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",
*
*
* 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);
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 -
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 *)
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
}
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++;
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);
}
}
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;
}
}
-/*
- * 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,
/* 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
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);
}
}
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);
tent->expr = expr;
return tent;
-}
+} /* make_targetlist_expr() */
+
/*
* makeTargetNames -
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);
}
}
/*-------------------------------------------------------------------------
*
- * 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
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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 */
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */