From 3ace5fd08247726756d695156e8ccb075d5f76d5 Mon Sep 17 00:00:00 2001 From: "Thomas G. Lockhart" Date: Sat, 9 May 1998 23:31:34 +0000 Subject: [PATCH] Add capabilities for automatic type conversion. --- src/backend/parser/analyze.c | 9 +- src/backend/parser/parse_coerce.c | 560 ++++++++++++++++++++ src/backend/parser/parse_expr.c | 18 +- src/backend/parser/parse_func.c | 846 +++++++++++++++++++++++++----- src/backend/parser/parse_node.c | 157 ++++-- src/backend/parser/parse_oper.c | 469 ++++++++++++----- src/backend/parser/parse_target.c | 275 ++++++---- src/backend/parser/parse_type.c | 10 +- src/include/parser/parse_coerce.h | 96 ++++ src/include/parser/parse_func.h | 4 +- 10 files changed, 2050 insertions(+), 394 deletions(-) create mode 100644 src/backend/parser/parse_coerce.c create mode 100644 src/include/parser/parse_coerce.h diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 7d107e49c3..0038c397b4 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -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 index 0000000000..77c23ac5c3 --- /dev/null +++ b/src/backend/parser/parse_coerce.c @@ -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 +#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 diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 39b9fd09dc..af8424ed0c 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -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) { diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 7809e534bc..a4e34d6791 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -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], ¤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; @@ -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, ¤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)); } @@ -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: "")); } } diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index 3a36fcd576..140ca3df47 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -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; diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index e2eb6b901d..2e43b1380f 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -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 $ * *------------------------------------------------------------------------- */ @@ -23,9 +23,18 @@ #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", diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index b9ab916d88..e3d5654ec4 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -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 $ * *------------------------------------------------------------------------- */ @@ -26,6 +26,13 @@ #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); } } diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index 9019c62122..eeceeab20c 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -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 $ * *------------------------------------------------------------------------- */ @@ -15,11 +15,17 @@ #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 index 0000000000..0d7aa705d0 --- /dev/null +++ b/src/include/parser/parse_coerce.h @@ -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 */ diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h index 8b59613363..fd6f700344 100644 --- a/src/include/parser/parse_func.h +++ b/src/include/parser/parse_func.h @@ -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 */ -- 2.40.0