From 8536c962614a55d33baa283a7901bb167a43978a Mon Sep 17 00:00:00 2001 From: "Thomas G. Lockhart" <lockhart@fourpalms.org> Date: Fri, 29 May 1998 14:00:24 +0000 Subject: [PATCH] Do type conversion to match columns in UNION clauses. Currently force the type to match the _first_ select in the union. Move oper_select_candidate() from parse_func.c to parse_oper.c. Throw error inside of oper_inexact() if no match for binary operators. Check more carefully that types can be coerced even if there is only one candidate operator in oper_inexact(). Fix up error messages for more uniform look. Remove unused code. Fix up comments. --- src/backend/parser/parse_clause.c | 67 ++- src/backend/parser/parse_coerce.c | 4 +- src/backend/parser/parse_expr.c | 6 +- src/backend/parser/parse_func.c | 472 +----------------- src/backend/parser/parse_node.c | 137 +----- src/backend/parser/parse_oper.c | 787 ++++++++++++++++-------------- src/backend/parser/parse_target.c | 15 +- src/backend/parser/parse_type.c | 26 +- 8 files changed, 526 insertions(+), 988 deletions(-) diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 06c7cdee5f2..8fb8e185587 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.16 1998/05/21 03:53:50 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.17 1998/05/29 14:00:19 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -25,12 +25,15 @@ #include "parser/parse_oper.h" #include "parser/parse_relation.h" #include "parser/parse_target.h" +#include "parser/parse_coerce.h" + static TargetEntry * find_targetlist_entry(ParseState *pstate, SortGroupBy *sortgroupby, List *tlist); static void parseFromClause(ParseState *pstate, List *frmList); + /* * makeRangeTable - * make a range table with the specified relation (optional) and the @@ -78,8 +81,7 @@ transformWhereClause(ParseState *pstate, Node *a_expr) if (exprType(qual) != BOOLOID) { - elog(ERROR, - "where clause must return type bool, not %s", + elog(ERROR, "WHERE clause must return type bool, not type %s", typeidTypeName(exprType(qual))); } return qual; @@ -167,7 +169,7 @@ find_targetlist_entry(ParseState *pstate, SortGroupBy *sortgroupby, List *tlist) if (real_rtable_pos == test_rtable_pos) { if (target_result != NULL) - elog(ERROR, "Order/Group By '%s' is ambiguous", sortgroupby->name); + elog(ERROR, "ORDER/GROUP BY '%s' is ambiguous", sortgroupby->name); else target_result = target; } @@ -175,7 +177,7 @@ find_targetlist_entry(ParseState *pstate, SortGroupBy *sortgroupby, List *tlist) else { if (target_result != NULL) - elog(ERROR, "Order/Group By '%s' is ambiguous", sortgroupby->name); + elog(ERROR, "ORDER/GROUP BY '%s' is ambiguous", sortgroupby->name); else target_result = target; } @@ -372,7 +374,7 @@ transformSortClause(ParseState *pstate, break; } if (i == NIL) - elog(ERROR, "The field specified in the UNIQUE ON clause is not in the targetlist"); + elog(ERROR, "All fields in the UNIQUE ON clause must appear in the target list"); foreach(s, sortlist) { @@ -392,23 +394,29 @@ transformSortClause(ParseState *pstate, sortlist = lappend(sortlist, sortcl); } } - } return sortlist; } -/* - * transformUnionClause - - * transform a Union clause - * +/* transformUnionClause() + * Transform a UNION clause. + * Note that the union clause is actually a fully-formed select structure. + * So, it is evaluated as a select, then the resulting target fields + * are matched up to ensure correct types in the results. + * The select clause parsing is done recursively, so the unions are evaluated + * right-to-left. One might want to look at all columns from all clauses before + * trying to coerce, but unless we keep track of the call depth we won't know + * when to do this because of the recursion. + * Let's just try matching in pairs for now (right to left) and see if it works. + * - thomas 1998-05-22 */ List * transformUnionClause(List *unionClause, List *targetlist) { - List *union_list = NIL; + List *union_list = NIL; QueryTreeList *qlist; - int i; + int i; if (unionClause) { @@ -421,13 +429,36 @@ transformUnionClause(List *unionClause, List *targetlist) List *next_target; if (length(targetlist) != length(qlist->qtrees[i]->targetList)) - elog(ERROR,"Each UNION query must have the same number of columns."); + elog(ERROR,"Each UNION clause must have the same number of columns"); foreach(next_target, qlist->qtrees[i]->targetList) { - if (((TargetEntry *)lfirst(prev_target))->resdom->restype != - ((TargetEntry *)lfirst(next_target))->resdom->restype) - elog(ERROR,"Each UNION query must have identical target types."); + Oid itype; + Oid otype; + otype = ((TargetEntry *)lfirst(prev_target))->resdom->restype; + itype = ((TargetEntry *)lfirst(next_target))->resdom->restype; + if (itype != otype) + { + Node *expr; + + expr = ((TargetEntry *)lfirst(next_target))->expr; + expr = coerce_target_expr(NULL, expr, itype, otype); + if (expr == NULL) + { + elog(ERROR,"Unable to transform %s to %s" + "\n\tEach UNION clause must have compatible target types", + typeidTypeName(itype), + typeidTypeName(otype)); + } + ((TargetEntry *)lfirst(next_target))->expr = expr; + ((TargetEntry *)lfirst(next_target))->resdom->restype = otype; + } + /* both are UNKNOWN? then evaluate as text... */ + else if (itype == UNKNOWNOID) + { + ((TargetEntry *)lfirst(next_target))->resdom->restype = TEXTOID; + ((TargetEntry *)lfirst(prev_target))->resdom->restype = TEXTOID; + } prev_target = lnext(prev_target); } union_list = lappend(union_list, qlist->qtrees[i]); @@ -436,4 +467,4 @@ transformUnionClause(List *unionClause, List *targetlist) } else return NIL; -} +} /* transformUnionClause() */ diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 77c23ac5c3a..9fdbb2fcd5b 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.1 1998/05/09 23:29:53 thomas Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.2 1998/05/29 14:00:20 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -319,7 +319,7 @@ IsPreferredType(CATEGORY category, Oid type) /* PreferredType() - * Assign a category to the specified OID. + * Return the preferred type OID for the specified category. */ Oid PreferredType(CATEGORY category, Oid type) diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index af8424ed0c5..007983adf7e 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.28 1998/05/09 23:29:53 thomas Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.29 1998/05/29 14:00:21 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -302,12 +302,12 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) break; } -/* These nodes do _not_ come from the original parse tree, +/* Some 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 cases below come from transforming function calls. + * The three cases below come from transforming function calls. * Let's try just passing them through... * - thomas 1998-03-14 */ diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index a4e34d6791b..66453634aec 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.17 1998/05/09 23:29:53 thomas Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.18 1998/05/29 14:00:21 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -62,10 +62,6 @@ func_get_detail(char *funcname, Oid *rettype, /* return value */ bool *retset, /* return value */ Oid **true_typeids); -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 @@ -166,7 +162,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, { first_arg = lfirst(fargs); if (first_arg == NULL) - elog(ERROR, "function '%s' does not allow NULL input", funcname); + elog(ERROR, "Function '%s' does not allow NULL input", funcname); } /* @@ -234,8 +230,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, heap_close(rd); } else - elog(ERROR, - "Type '%s' is not a relation type", + elog(ERROR, "Type '%s' is not a relation type", typeidTypeName(toid)); argrelid = typeidTypeRelid(toid); @@ -342,8 +337,9 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, * 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); + elog(ERROR, "There is no function '%s'" + " with argument #%d of type UNKNOWN", + funcname, nargs); else toid = exprType(pair); } @@ -385,7 +381,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, } if (!exists) - elog(ERROR, "no such attribute or function '%s'", funcname); + elog(ERROR, "No such attribute or function '%s'", funcname); /* got it */ funcnode = makeNode(Func); @@ -443,7 +439,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, Assert(length(fargs) == 1); seq = (Const *) lfirst(fargs); if (!IsA((Node *) seq, Const)) - elog(ERROR, "%s: only constant sequence names are acceptable", funcname); + elog(ERROR, "Only constant sequence names are acceptable for function '%s'", funcname); seqname = lower((text *) DatumGetPointer(seq->constvalue)); pfree(DatumGetPointer(seq->constvalue)); seq->constvalue = PointerGetDatum(seqname); @@ -458,7 +454,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, pfree(seqrel); if (funcid == F_NEXTVAL && pstate->p_in_where_clause) - elog(ERROR, "nextval of a sequence in WHERE disallowed"); + elog(ERROR, "Sequence function nextval is not allowed in WHERE clauses"); } expr = makeNode(Expr); @@ -497,7 +493,7 @@ funcid_get_rettype(Oid funcid) 0, 0, 0); if (!HeapTupleIsValid(func_tuple)) - elog(ERROR, "function %d does not exist", funcid); + elog(ERROR, "Function OID %d does not exist", funcid); funcrettype = (Oid) ((Form_pg_proc) GETSTRUCT(func_tuple))->prorettype; @@ -807,423 +803,6 @@ printf("func_select_candidate- column #%d input type is %s\n", } /* 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. * @@ -1331,22 +910,6 @@ 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; @@ -1355,11 +918,8 @@ 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, "func_get_detail: No such attribute or function '%s'", funcname); } -#if FALSE - func_error(NULL, funcname, nargs, oid_array, NULL); -#endif } else { @@ -1519,7 +1079,7 @@ find_inheritors(Oid relid, Oid **supervec) /* save the type id, rather than the relation id */ if ((rd = heap_open(qentry->sqe_relid)) == (Relation) NULL) - elog(ERROR, "relid %d does not exist", qentry->sqe_relid); + elog(ERROR, "Relid %d does not exist", qentry->sqe_relid); qentry->sqe_relid = typeTypeId(typenameType(RelationGetRelationName(rd)->data)); heap_close(rd); @@ -1676,7 +1236,7 @@ setup_tlist(char *attname, Oid relid) attno = get_attnum(relid, attname); if (attno < 0) - elog(ERROR, "cannot reference attribute '%s'" + elog(ERROR, "Cannot reference attribute '%s'" " of tuple params/return values for functions", attname); typeid = get_atttype(relid, attno); @@ -1780,7 +1340,7 @@ ParseComplexProjection(ParseState *pstate, } else { - elog(ERROR, "Function '%s' has bad returntype %d", + elog(ERROR, "Function '%s' has bad return type %d", funcname, argtype); } } @@ -1849,7 +1409,7 @@ ParseComplexProjection(ParseState *pstate, } - elog(ERROR, "Function '%s' has bad returntype %d", + elog(ERROR, "Function '%s' has bad return type %d", funcname, argtype); break; } @@ -1918,7 +1478,7 @@ func_error(char *caller, char *funcname, int nargs, Oid *argtypes, char *msg) if (caller == NULL) { - elog(ERROR, "function '%s(%s)' does not exist%s%s", + elog(ERROR, "Function '%s(%s)' does not exist%s%s", funcname, p, ((msg != NULL)? "\n\t": ""), ((msg != NULL)? msg: "")); } else diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index 140ca3df47e..1b0982d3bce 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.15 1998/05/09 23:29:53 thomas Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.16 1998/05/29 14:00:21 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -25,6 +25,7 @@ #include "parser/parse_oper.h" #include "parser/parse_relation.h" #include "parser/parse_type.h" +#include "parser/parse_coerce.h" #include "utils/builtins.h" #include "utils/syscache.h" #include "utils/lsyscache.h" @@ -36,13 +37,10 @@ make_operand(char *opname, Oid orig_typeId, Oid true_typeId); -/* - * make_parsestate() -- - * allocate and initialize a new ParseState. - * the CALLER is responsible for freeing the ParseState* returned - * +/* make_parsestate() + * Allocate and initialize a new ParseState. + * The CALLER is responsible for freeing the ParseState* returned. */ - ParseState * make_parsestate(ParseState *parentParseState) { @@ -58,11 +56,6 @@ make_parsestate(ParseState *parentParseState) } -extern -Node * -coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId); - - /* make_operand() * Ensure argument type match by forcing conversion of constants. */ @@ -74,10 +67,6 @@ 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", @@ -133,36 +122,6 @@ disallow_setop(char *op, Type optype, Node *operand) } -/* 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. * @@ -174,7 +133,7 @@ make_op(char *opname, Node *ltree, Node *rtree) { Oid ltypeId, rtypeId; - Operator temp; + Operator tup; OperatorTupleForm opform; Oper *newop; Node *left, @@ -185,8 +144,8 @@ make_op(char *opname, Node *ltree, Node *rtree) if (rtree == NULL) { ltypeId = (ltree == NULL) ? UNKNOWNOID : exprType(ltree); - temp = right_oper(opname, ltypeId); - opform = (OperatorTupleForm) GETSTRUCT(temp); + tup = right_oper(opname, ltypeId); + opform = (OperatorTupleForm) GETSTRUCT(tup); left = make_operand(opname, ltree, ltypeId, opform->oprleft); right = NULL; @@ -196,11 +155,11 @@ make_op(char *opname, Node *ltree, Node *rtree) else if (ltree == NULL) { rtypeId = (rtree == NULL) ? UNKNOWNOID : exprType(rtree); - temp = left_oper(opname, rtypeId); + tup = left_oper(opname, rtypeId); #ifdef PARSEDEBUG -printf("make_op: returned from left_oper() with structure at %p\n", (void *)temp); +printf("make_op: returned from left_oper() with structure at %p\n", (void *)tup); #endif - opform = (OperatorTupleForm) GETSTRUCT(temp); + opform = (OperatorTupleForm) GETSTRUCT(tup); #ifdef PARSEDEBUG printf("make_op: calling make_operand()\n"); #endif @@ -212,80 +171,28 @@ printf("make_op: calling make_operand()\n"); /* otherwise, binary operator */ else { - -#define CONVERTIBLE_TYPE(t) ( (t) == INT2OID || \ - (t) == INT4OID || \ - (t) == OIDOID || \ - (t) == FLOAT4OID || \ - (t) == FLOAT8OID || \ - (t) == CASHOID) - /* binary operator */ 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 (CONVERTIBLE_TYPE(ltypeId) && nodeTag(ltree) != T_Const && - CONVERTIBLE_TYPE(rtypeId) && nodeTag(rtree) == T_Const && - !((Const *) rtree)->constiscast) + /* check for exact match on this operator... */ + if (HeapTupleIsValid(tup = oper_exact(opname, ltypeId, rtypeId, <ree, &rtree, TRUE))) { - outfunc = typeidOutfunc(rtypeId); - infunc = typeidInfunc(ltypeId); - outstr = (char *) fmgr(outfunc, ((Const *) rtree)->constvalue); - ((Const *) rtree)->constvalue = (Datum) fmgr(infunc, outstr, -1); - pfree(outstr); - ((Const *) rtree)->consttype = rtypeId = ltypeId; - newtype = typeidType(rtypeId); - ((Const *) rtree)->constlen = typeLen(newtype); - ((Const *) rtree)->constbyval = typeByVal(newtype); + ltypeId = exprType(ltree); + rtypeId = exprType(rtree); } - - if (CONVERTIBLE_TYPE(rtypeId) && nodeTag(rtree) != T_Const && - CONVERTIBLE_TYPE(ltypeId) && nodeTag(ltree) == T_Const && - !((Const *) ltree)->constiscast) + /* try to find a match on likely candidates... */ + else if (!HeapTupleIsValid(tup = oper_inexact(opname, ltypeId, rtypeId, <ree, &rtree, FALSE))) { - outfunc = typeidOutfunc(ltypeId); - infunc = typeidInfunc(rtypeId); - outstr = (char *) fmgr(outfunc, ((Const *) ltree)->constvalue); - ((Const *) ltree)->constvalue = (Datum) fmgr(infunc, outstr, -1); - pfree(outstr); - ((Const *) ltree)->consttype = ltypeId = rtypeId; - newtype = typeidType(ltypeId); - ((Const *) ltree)->constlen = typeLen(newtype); - ((Const *) ltree)->constbyval = typeByVal(newtype); + /* Won't return from oper_inexact() without a candidate... */ } -#endif - temp = oper(opname, ltypeId, rtypeId, false); - opform = (OperatorTupleForm) GETSTRUCT(temp); + opform = (OperatorTupleForm) GETSTRUCT(tup); left = make_operand(opname, ltree, ltypeId, opform->oprleft); right = make_operand(opname, rtree, rtypeId, opform->oprright); } - newop = makeOper(oprid(temp), /* opno */ + newop = makeOper(oprid(tup), /* opno */ InvalidOid, /* opid */ opform->oprresult, /* operator result type */ 0, @@ -304,7 +211,7 @@ printf( "Convert left-hand constant to text for node %d\n", nodeTag(ltree)); result->args = lcons(left, lcons(right, NIL)); return result; -} +} /* make_op() */ Var * @@ -538,7 +445,7 @@ make_const(Value *value) default: { if (nodeTag(value) != T_Null) - elog(NOTICE, "unknown type : %d\n", nodeTag(value)); + elog(NOTICE, "make_const: unknown type %d\n", nodeTag(value)); /* null const */ con = makeConst(0, 0, (Datum) NULL, true, false, false, false); diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index 2e43b1380fc..7042e4740fc 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.11 1998/05/09 23:29:53 thomas Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.12 1998/05/29 14:00:22 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -21,37 +21,27 @@ #include "catalog/pg_operator.h" #include "catalog/pg_type.h" #include "fmgr.h" +#include "parser/parse_func.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, Oid rightTypeId, CandidateList *candidates); -static CandidateList -binary_oper_select_candidate(Oid arg1, - Oid arg2, - CandidateList candidates); -static bool equivalentOpersAfterPromotion(CandidateList candidates); -static void op_error(char *op, Oid arg1, Oid arg2); static int unary_oper_get_candidates(char *op, Oid typeId, CandidateList *candidates, char rightleft); - +static void +op_error(char *op, Oid arg1, Oid arg2); Oid any_ordering_op(int restype) @@ -59,7 +49,13 @@ any_ordering_op(int restype) Operator order_op; Oid order_opid; - order_op = oper("<", restype, restype, false); + order_op = oper("<", restype, restype, TRUE); + if (!HeapTupleIsValid(order_op)) + { + elog(ERROR, "Unable to find an ordering operator '%s' for type %s." + "\n\tUse an explicit ordering operator or modify the query.", + "<", typeidTypeName(restype)); + } order_opid = oprid(order_op); return order_opid; @@ -107,44 +103,12 @@ 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, + TRUE, nkeys, opKey); @@ -173,288 +137,400 @@ binary_oper_get_candidates(char *opname, } /* 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. +/* 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. + * + * This routine is new code, replacing binary_oper_select_candidate() + * which dates from v4.2/v1.0.x days. It tries very hard to match up + * operators with types, including allowing type coersions if necessary. + * The important thing is that the code do as much as possible, + * while _never_ doing the wrong thing, where "the wrong thing" would + * be returning an operator when other better choices are available, + * or returning an operator which is a non-intuitive possibility. + * - thomas 1998-05-21 + * + * The comments below came from binary_oper_select_candidate(), and + * illustrate the issues and choices which are possible: + * - thomas 1998-05-20 + * + * 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 int -BinaryOperCandidates(char *opname, - Oid lTypeId, - Oid rTypeId, - CandidateList *candidates) +Oid * +oper_select_candidate(int nargs, + Oid *input_typeids, + 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]; + CandidateList current_candidate; + CandidateList last_candidate; + Oid *current_typeids; + int unknownOids; + int i; - /* 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); + int ncandidates; + int nbestMatch, + nmatch; - PromoteLowerType(&lCandidateTypeId, &rCandidateTypeId); - } + CATEGORY slot_category, + current_category; + Oid slot_type, + current_type; - /* Can we promote the lesser type directly to the other? */ - if (can_coerce_type(lowerTypeId, higherTypeId)) +/* + * 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) { - 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')); + 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++; + } + } -#if FALSE - if (leftTypeId == UNKNOWNOID) - { - if (rightTypeId == UNKNOWNOID) +#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) { - nkeys = 2; + last_candidate->next = current_candidate; + last_candidate = current_candidate; + ncandidates++; +#ifdef PARSEDEBUG +printf("oper_select_candidate- choose candidate as possible match\n"); +#endif } else { - nkeys = 3; - - ScanKeyEntryInitialize(&opKey[2], 0, - Anum_pg_operator_oprright, - F_OIDEQ, - ObjectIdGetDatum(rightTypeId)); + last_candidate->next = NULL; +#ifdef PARSEDEBUG +printf("oper_select_candidate- reject candidate as possible match\n"); +#endif } } - else if (rightTypeId == UNKNOWNOID) - { - nkeys = 3; - ScanKeyEntryInitialize(&opKey[2], 0, - Anum_pg_operator_oprleft, - F_OIDEQ, - ObjectIdGetDatum(leftTypeId)); - } - else + if (ncandidates <= 1) { - /* 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); + if (!can_coerce_type(1, &input_typeids[0], &candidates->args[0]) + || !can_coerce_type(1, &input_typeids[1], &candidates->args[1])) + { + ncandidates = 0; + } + return ((ncandidates == 1)? candidates->args: NULL); + } - do +/* + * Still too many candidates? + * 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) { - tup = heap_getnext(pg_operator_scan, 0, &buffer); - if (HeapTupleIsValid(tup)) + current_typeids = current_candidate->args; + nmatch = 0; + for (i = 0; i < nargs; i++) { - current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList)); - current_candidate->args = (Oid *) palloc(2 * sizeof(Oid)); + 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++; + } + } + } - oper = (OperatorTupleForm) GETSTRUCT(tup); - current_candidate->args[0] = oper->oprleft; - current_candidate->args[1] = oper->oprright; - current_candidate->next = *candidates; - *candidates = current_candidate; +#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++; - ReleaseBuffer(buffer); +#ifdef PARSEDEBUG +printf("oper_select_candidate- choose candidate as possible match\n"); +#endif } - } while (HeapTupleIsValid(tup)); - - heap_endscan(pg_operator_scan); - heap_close(pg_operator_desc); - - return ncandidates; -} /* BinaryOperCandidates() */ + else + { + last_candidate->next = NULL; +#ifdef PARSEDEBUG +printf("oper_select_candidate- reject candidate as possible match\n"); #endif + } + } + if (ncandidates <= 1) + { + if (!can_coerce_type(1, &input_typeids[0], &candidates->args[0]) + || !can_coerce_type(1, &input_typeids[1], &candidates->args[1])) + { + ncandidates = 0; +#ifdef PARSEDEBUG +printf("oper_select_candidate- unable to coerce preferred candidate\n"); +#endif + } + return ((ncandidates == 1)? candidates->args: NULL); + } /* - * equivalentOpersAfterPromotion - - * checks if a list of candidate operators obtained from - * binary_oper_get_candidates() contain equivalent operators. If - * this routine is called, we have more than 1 candidate and need to - * decided whether to pick one of them. This routine returns true if - * all the candidates operate on the same data types after - * promotion (int2, int4, float4 -> float8). + * Still too many candidates? + * Try assigning types for the unknown columns. */ -static bool -equivalentOpersAfterPromotion(CandidateList candidates) -{ - CandidateList result; - CandidateList promotedCandidates = NULL; - Oid leftarg, - rightarg; + unknownOids = FALSE; + current_type = UNKNOWNOID; + for (i = 0; i < nargs; i++) + { + if ((input_typeids[i] != UNKNOWNOID) + && (input_typeids[i] != InvalidOid)) + { + current_type = input_typeids[i]; + } + else + { + unknownOids = TRUE; + } + } - for (result = candidates; result != NULL; result = result->next) + if (unknownOids && (current_type != UNKNOWNOID)) { - CandidateList c; + 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); + } + } - c = (CandidateList) palloc(sizeof(*c)); - c->args = (Oid *) palloc(2 * sizeof(Oid)); - switch (result->args[0]) + for (i = 0; i < nargs; i++) + { + if (input_typeids[i] == UNKNOWNOID) { - case FLOAT4OID: - case INT4OID: - case INT2OID: - case CASHOID: - c->args[0] = FLOAT8OID; - break; - default: - c->args[0] = result->args[0]; - break; + 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 + } } - switch (result->args[1]) + else { - case FLOAT4OID: - case INT4OID: - case INT2OID: - case CASHOID: - c->args[1] = FLOAT8OID; - break; - default: - c->args[1] = result->args[1]; - break; +#ifdef PARSEDEBUG +printf("oper_select_candidate- column #%d input type is %s\n", + i, typeidTypeName(input_typeids[i])); +#endif } - c->next = promotedCandidates; - promotedCandidates = c; } - /* - * if we get called, we have more than 1 candidates so we can do the - * following safely - */ - leftarg = promotedCandidates->args[0]; - rightarg = promotedCandidates->args[1]; - - for (result = promotedCandidates->next; result != NULL; result = result->next) + ncandidates = 0; + for (current_candidate = candidates; + current_candidate != NULL; + current_candidate = current_candidate->next) { - if (result->args[0] != leftarg || result->args[1] != rightarg) - - /* - * this list contains operators that operate on different data - * types even after promotion. Hence we can't decide on which - * one to pick. The user must do explicit type casting. - */ - return FALSE; + if (can_coerce_type(1, &input_typeids[0], ¤t_candidate->args[0]) + && can_coerce_type(1, &input_typeids[1], ¤t_candidate->args[1])) + ncandidates++; } - /* - * all the candidates are equivalent in the following sense: they - * operate on equivalent data types and picking any one of them is as - * good. - */ - return TRUE; -} + return ((ncandidates == 1)? candidates->args: NULL); +} /* oper_select_candidate() */ -/* 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 +/* oper_exact() + * Given operator, and arguments, return oper struct. + * Inputs: + * arg1, arg2: Type IDs */ -static CandidateList -binary_oper_select_candidate(Oid arg1, - Oid arg2, - CandidateList candidates) +Operator +oper_exact(char *op, Oid arg1, Oid arg2, Node **ltree, Node **rtree, bool noWarnings) { - CandidateList result; + HeapTuple tup; + Node *tree; - /* - * If both are "unknown", there is no way to select a candidate - */ - if (arg1 == UNKNOWNOID && arg2 == UNKNOWNOID) - return (NULL); + /* Unspecified type for one of the arguments? then use the other */ + if ((arg1 == UNKNOWNOID) && (arg2 != InvalidOid)) arg1 = arg2; + else if ((arg2 == UNKNOWNOID) && (arg1 != InvalidOid)) arg2 = arg1; - if (!equivalentOpersAfterPromotion(candidates)) - return NULL; + tup = SearchSysCacheTuple(OPRNAME, + PointerGetDatum(op), + ObjectIdGetDatum(arg1), + ObjectIdGetDatum(arg2), + Int8GetDatum('b')); - /* - * if we get here, any one will do but we're more picky and require - * both operands be the same. - */ - for (result = candidates; result != NULL; result = result->next) + /* Did not find anything? then try flipping arguments on a commutative operator... */ + if (!HeapTupleIsValid(tup) && (arg1 != arg2)) { - if (result->args[0] == result->args[1]) - return result; + tup = SearchSysCacheTuple(OPRNAME, + PointerGetDatum(op), + ObjectIdGetDatum(arg2), + ObjectIdGetDatum(arg1), + Int8GetDatum('b')); + + if (HeapTupleIsValid(tup)) + { + OperatorTupleForm opform; + +#if PARSEDEBUG +printf("oper_exact: found possible commutative operator candidate\n"); +#endif + opform = (OperatorTupleForm) GETSTRUCT(tup); + if (opform->oprcom == tup->t_oid) + { +#if PARSEDEBUG +printf("oper_exact: commutative operator found\n"); +#endif + if ((ltree != NULL) && (rtree != NULL)) + { + tree = *ltree; + *ltree = *rtree; + *rtree = tree; + } + } + /* disable for now... - thomas 1998-05-14 */ + else + { + tup = NULL; + } + } + if (!HeapTupleIsValid(tup) && (!noWarnings)) + { + op_error(op, arg1, arg2); + } } - return (NULL); -} + return tup; +} /* oper_exact() */ -/* oper() + +/* oper_inexact() * 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) +oper_inexact(char *op, Oid arg1, Oid arg2, Node **ltree, Node **rtree, bool noWarnings) { HeapTuple tup; CandidateList candidates; @@ -468,77 +544,95 @@ oper(char *op, Oid arg1, Oid arg2, bool noWarnings) if (arg1 == InvalidOid) arg1 = arg2; - tup = SearchSysCacheTuple(OPRNAME, - PointerGetDatum(op), - ObjectIdGetDatum(arg1), - ObjectIdGetDatum(arg2), - Int8GetDatum('b')); + ncandidates = binary_oper_get_candidates(op, arg1, arg2, &candidates); - /* Did not find anything? then look more carefully... */ - if (!HeapTupleIsValid(tup)) + /* No operators found? Then throw error or return null... */ + if (ncandidates == 0) { - ncandidates = binary_oper_get_candidates(op, arg1, arg2, &candidates); + if (!noWarnings) + op_error(op, arg1, arg2); + return (NULL); + } - /* No operators found? Then throw error or return null... */ - if (ncandidates == 0) - { - if (!noWarnings) - op_error(op, arg1, arg2); - return (NULL); - } + /* Or found exactly one? Then proceed... */ + else if (ncandidates == 1) + { + tup = SearchSysCacheTuple(OPRNAME, + PointerGetDatum(op), + ObjectIdGetDatum(candidates->args[0]), + ObjectIdGetDatum(candidates->args[1]), + Int8GetDatum('b')); + Assert(HeapTupleIsValid(tup)); - /* Or found exactly one? Then proceed... */ - else if (ncandidates == 1) +#if PARSEDEBUG +printf("oper_inexact: found single candidate\n"); +#endif + + } + + /* Otherwise, multiple operators of the desired types found... */ + else + { + inputOids[0] = arg1; + inputOids[1] = arg2; + targetOids = oper_select_candidate(2, inputOids, candidates); + if (targetOids != NULL) { +#if PARSEDEBUG +printf("oper_inexact: 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)); - } - /* Otherwise, multiple operators of the desired types found... */ + } else { -#if FALSE - candidates = binary_oper_select_candidate(arg1, arg2, candidates); -#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) - { -#if PARSEDEBUG -printf("oper: found candidate\n"); -#endif - tup = SearchSysCacheTuple(OPRNAME, - PointerGetDatum(op), - ObjectIdGetDatum(targetOids[0]), - ObjectIdGetDatum(targetOids[1]), - Int8GetDatum('b')); - } - else - { - tup = NULL; - } + tup = NULL; + } - /* Could not choose one, for whatever reason... */ - if (!HeapTupleIsValid(tup)) + /* Could not choose one, for whatever reason... */ + if (!HeapTupleIsValid(tup)) + { + if (!noWarnings) { - if (!noWarnings) - { - 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); + elog(ERROR, "There is more than one possible 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_inexact() */ + + +/* oper() + * Given operator, types of arg1, and arg2, return oper struct. + * Inputs: + * arg1, arg2: Type IDs + */ +Operator +oper(char *opname, Oid ltypeId, Oid rtypeId, bool noWarnings) +{ + HeapTuple tup; + + /* check for exact match on this operator... */ + if (HeapTupleIsValid(tup = oper_exact(opname, ltypeId, rtypeId, NULL, NULL, TRUE))) + { + } + /* try to find a match on likely candidates... */ + else if (HeapTupleIsValid(tup = oper_inexact(opname, ltypeId, rtypeId, NULL, NULL, TRUE))) + { + } + else if (!noWarnings) + { + elog(ERROR, "Unable to find binary operator '%s' for types %s and %s", + opname, typeTypeName(typeidType(ltypeId)), typeTypeName(typeidType(rtypeId))); + } + return ((Operator) tup); } /* oper() */ @@ -573,26 +667,13 @@ 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 */ - - /* - * but we should allow types that are internally the same to be - * "coerced" - */ - if (typeId != UNKNOWNOID) - { - 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, - true, + TRUE, 2, opKey); @@ -658,17 +739,13 @@ right_oper(char *op, Oid arg) { tup = SearchSysCacheTuple(OPRNAME, PointerGetDatum(op), - ObjectIdGetDatum(candidates->args[0]), + ObjectIdGetDatum(candidates->args[0]), ObjectIdGetDatum(InvalidOid), Int8GetDatum('r')); Assert(HeapTupleIsValid(tup)); } else { -#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) @@ -735,10 +812,6 @@ printf("left_oper: searched cache for single left oper candidate '%s %s'\n", } else { -#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), @@ -779,7 +852,7 @@ op_error(char *op, Oid arg1, Oid arg2) else { elog(ERROR, "Left hand side of operator '%s' has an unknown type" - "\n\tProbably a bad attribute name", op); + "\n\tProbably a bad attribute name", op); } if (typeidIsValid(arg2)) @@ -789,11 +862,11 @@ op_error(char *op, Oid arg1, Oid arg2) else { elog(ERROR, "Right hand side of operator %s has an unknown type" - "\n\tProbably a bad attribute name", op); + "\n\tProbably a bad attribute name", op); } 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", + "\n\tor you will have to define the operator using CREATE OPERATOR", op, typeTypeName(tp1), typeTypeName(tp2)); } diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index e76fa1829e3..ad996081625 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.13 1998/05/21 03:53:51 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.14 1998/05/29 14:00:23 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -24,15 +24,11 @@ #include "parser/parse_node.h" #include "parser/parse_relation.h" #include "parser/parse_target.h" +#include "parser/parse_coerce.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); @@ -46,11 +42,6 @@ size_target_expr(ParseState *pstate, Node *expr, Oid attrtype, int16 attrtypmod); -Node * -coerce_target_expr(ParseState *pstate, - Node *expr, - Oid type_id, - Oid attrtype); /* @@ -357,7 +348,7 @@ transformTargetList(ParseState *pstate, List *targetlist) } return p_target; -} +} /* transformTargetList() */ Node * diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index eeceeab20c0..5b2529f6847 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.9 1998/05/09 23:29:54 thomas Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.10 1998/05/29 14:00:24 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -264,27 +264,3 @@ typeidInfunc(Oid type_id) infunc = type->typinput; return (infunc); } - - -#ifdef NOT_USED -char -FindDelimiter(char *typename) -{ - char delim; - HeapTuple typeTuple; - TypeTupleForm type; - - - if (!(typeTuple = SearchSysCacheTuple(TYPNAME, - PointerGetDatum(typename), - 0, 0, 0))) - { - elog(ERROR, "type name lookup of %s failed", typename); - } - type = (TypeTupleForm) GETSTRUCT(typeTuple); - - delim = type->typdelim; - return (delim); -} - -#endif -- GitLab