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.
This commit is contained in:
Thomas G. Lockhart 1998-05-29 14:00:24 +00:00
parent 329083a97e
commit 8536c96261
8 changed files with 542 additions and 1004 deletions

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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_oper.h"
#include "parser/parse_relation.h" #include "parser/parse_relation.h"
#include "parser/parse_target.h" #include "parser/parse_target.h"
#include "parser/parse_coerce.h"
static TargetEntry * static TargetEntry *
find_targetlist_entry(ParseState *pstate, find_targetlist_entry(ParseState *pstate,
SortGroupBy *sortgroupby, List *tlist); SortGroupBy *sortgroupby, List *tlist);
static void parseFromClause(ParseState *pstate, List *frmList); static void parseFromClause(ParseState *pstate, List *frmList);
/* /*
* makeRangeTable - * makeRangeTable -
* make a range table with the specified relation (optional) and the * 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) if (exprType(qual) != BOOLOID)
{ {
elog(ERROR, elog(ERROR, "WHERE clause must return type bool, not type %s",
"where clause must return type bool, not %s",
typeidTypeName(exprType(qual))); typeidTypeName(exprType(qual)));
} }
return qual; return qual;
@ -167,7 +169,7 @@ find_targetlist_entry(ParseState *pstate, SortGroupBy *sortgroupby, List *tlist)
if (real_rtable_pos == test_rtable_pos) if (real_rtable_pos == test_rtable_pos)
{ {
if (target_result != NULL) 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 else
target_result = target; target_result = target;
} }
@ -175,7 +177,7 @@ find_targetlist_entry(ParseState *pstate, SortGroupBy *sortgroupby, List *tlist)
else else
{ {
if (target_result != NULL) 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 else
target_result = target; target_result = target;
} }
@ -372,7 +374,7 @@ transformSortClause(ParseState *pstate,
break; break;
} }
if (i == NIL) 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) foreach(s, sortlist)
{ {
@ -392,23 +394,29 @@ transformSortClause(ParseState *pstate,
sortlist = lappend(sortlist, sortcl); sortlist = lappend(sortlist, sortcl);
} }
} }
} }
return sortlist; return sortlist;
} }
/* /* transformUnionClause()
* transformUnionClause - * Transform a UNION clause.
* 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 * List *
transformUnionClause(List *unionClause, List *targetlist) transformUnionClause(List *unionClause, List *targetlist)
{ {
List *union_list = NIL; List *union_list = NIL;
QueryTreeList *qlist; QueryTreeList *qlist;
int i; int i;
if (unionClause) if (unionClause)
{ {
@ -421,13 +429,36 @@ transformUnionClause(List *unionClause, List *targetlist)
List *next_target; List *next_target;
if (length(targetlist) != length(qlist->qtrees[i]->targetList)) 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) foreach(next_target, qlist->qtrees[i]->targetList)
{ {
if (((TargetEntry *)lfirst(prev_target))->resdom->restype != Oid itype;
((TargetEntry *)lfirst(next_target))->resdom->restype) Oid otype;
elog(ERROR,"Each UNION query must have identical target types."); 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); prev_target = lnext(prev_target);
} }
union_list = lappend(union_list, qlist->qtrees[i]); union_list = lappend(union_list, qlist->qtrees[i]);
@ -436,4 +467,4 @@ transformUnionClause(List *unionClause, List *targetlist)
} }
else else
return NIL; return NIL;
} } /* transformUnionClause() */

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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() /* PreferredType()
* Assign a category to the specified OID. * Return the preferred type OID for the specified category.
*/ */
Oid Oid
PreferredType(CATEGORY category, Oid type) PreferredType(CATEGORY category, Oid type)

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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; 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. * but result from parser transformation in this phase.
* At least one construct (BETWEEN/AND) puts the same nodes * At least one construct (BETWEEN/AND) puts the same nodes
* into two branches of the parse tree; hence, some nodes * into two branches of the parse tree; hence, some nodes
* are transformed twice. * 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... * Let's try just passing them through...
* - thomas 1998-03-14 * - thomas 1998-03-14
*/ */

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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 */ Oid *rettype, /* return value */
bool *retset, /* return value */ bool *retset, /* return value */
Oid **true_typeids); Oid **true_typeids);
Oid *
func_select_candidate(int nargs,
Oid *input_typeids,
CandidateList candidates);
static Oid funcid_get_rettype(Oid funcid); static Oid funcid_get_rettype(Oid funcid);
static Oid **gen_cross_product(InhPaths *arginh, int nargs); static Oid **gen_cross_product(InhPaths *arginh, int nargs);
static void static void
@ -166,7 +162,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
{ {
first_arg = lfirst(fargs); first_arg = lfirst(fargs);
if (first_arg == NULL) 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); heap_close(rd);
} }
else else
elog(ERROR, elog(ERROR, "Type '%s' is not a relation type",
"Type '%s' is not a relation type",
typeidTypeName(toid)); typeidTypeName(toid));
argrelid = typeidTypeRelid(toid); argrelid = typeidTypeRelid(toid);
@ -342,8 +337,9 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
* cast them - jolly * cast them - jolly
*/ */
if (exprType(pair) == UNKNOWNOID && !IsA(pair, Const)) if (exprType(pair) == UNKNOWNOID && !IsA(pair, Const))
elog(ERROR, "ParseFuncOrColumn: no function named '%s'" elog(ERROR, "There is no function '%s'"
" that takes in an unknown type as argument #%d", funcname, nargs); " with argument #%d of type UNKNOWN",
funcname, nargs);
else else
toid = exprType(pair); toid = exprType(pair);
} }
@ -385,7 +381,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
} }
if (!exists) if (!exists)
elog(ERROR, "no such attribute or function '%s'", funcname); elog(ERROR, "No such attribute or function '%s'", funcname);
/* got it */ /* got it */
funcnode = makeNode(Func); funcnode = makeNode(Func);
@ -443,7 +439,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
Assert(length(fargs) == 1); Assert(length(fargs) == 1);
seq = (Const *) lfirst(fargs); seq = (Const *) lfirst(fargs);
if (!IsA((Node *) seq, Const)) 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)); seqname = lower((text *) DatumGetPointer(seq->constvalue));
pfree(DatumGetPointer(seq->constvalue)); pfree(DatumGetPointer(seq->constvalue));
seq->constvalue = PointerGetDatum(seqname); seq->constvalue = PointerGetDatum(seqname);
@ -458,7 +454,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
pfree(seqrel); pfree(seqrel);
if (funcid == F_NEXTVAL && pstate->p_in_where_clause) 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); expr = makeNode(Expr);
@ -497,7 +493,7 @@ funcid_get_rettype(Oid funcid)
0, 0, 0); 0, 0, 0);
if (!HeapTupleIsValid(func_tuple)) if (!HeapTupleIsValid(func_tuple))
elog(ERROR, "function %d does not exist", funcid); elog(ERROR, "Function OID %d does not exist", funcid);
funcrettype = (Oid) funcrettype = (Oid)
((Form_pg_proc) GETSTRUCT(func_tuple))->prorettype; ((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() */ } /* func_select_candidate() */
Oid *
oper_select_candidate(int nargs,
Oid *input_typeids,
CandidateList candidates);
#if FALSE
/* oper_select_candidate()
*/
Oid *
oper_select_candidate(int nargs,
Oid *input_typeids,
CandidateList candidates)
{
CandidateList current_candidate;
Oid *current_typeids;
int unknownOids, textOids;
int i;
int ncandidates;
int nbestMatch;
Oid bestTypeId;
unknownOids = TRUE;
for (i = 0; i < nargs; i++)
{
unknownOids &= (input_typeids[i] == UNKNOWNOID);
#ifdef PARSEDEBUG
printf("oper_select_candidate: argument #%d type is %s\n",
i, typeidTypeName(input_typeids[i]));
#endif
}
for (current_candidate = candidates;
current_candidate != NULL;
current_candidate = current_candidate->next)
{
current_typeids = current_candidate->args;
if (unknownOids)
{
textOids = TRUE;
for (i = 0; i < nargs; i++)
{
textOids &= (current_typeids[i] == TEXTOID);
#ifdef PARSEDEBUG
printf("oper_select_candidate: candidate argument #%d type is %s\n",
i, typeidTypeName(current_typeids[i]));
#endif
}
if (textOids)
return(current_candidate->args);
}
}
#ifdef PARSEDEBUG
printf("oper_select_candidate: no all-text operators found\n");
#endif
/* OK, there are multiple types here; let's see if we can choose... */
nbestMatch = 0;
bestTypeId = InvalidOid;
for (current_candidate = candidates;
current_candidate != NULL;
current_candidate = current_candidate->next)
{
current_typeids = current_candidate->args;
if (IS_HIGHEST_TYPE(input_typeids[0])
&& (input_typeids[0] == current_typeids[0])
&& IS_HIGHEST_TYPE(current_typeids[1])
&& can_coerce_type(1, &input_typeids[1], &current_typeids[1]))
{
#ifdef PARSEDEBUG
printf("oper_select_candidate: (1) choose (%s,%s) -> (%s,%s)...\n",
typeidTypeName(input_typeids[0]), typeidTypeName(input_typeids[1]),
typeidTypeName(current_typeids[0]), typeidTypeName(current_typeids[1]));
#endif
return (current_candidate->args);
}
else if (IS_HIGHEST_TYPE(input_typeids[1])
&& (input_typeids[1] == current_typeids[1])
&& IS_HIGHEST_TYPE(current_typeids[0])
&& can_coerce_type(1, &input_typeids[0], &current_typeids[0]))
{
#ifdef PARSEDEBUG
printf("oper_select_candidate: (2) choose (%s,%s) -> (%s,%s)...\n",
typeidTypeName(input_typeids[0]), typeidTypeName(input_typeids[1]),
typeidTypeName(current_typeids[0]), typeidTypeName(current_typeids[1]));
#endif
return (current_candidate->args);
}
else
{
#ifdef PARSEDEBUG
printf("oper_select_candidate: (3) skip (%s,%s) -> (%s,%s)...\n",
typeidTypeName(input_typeids[0]), typeidTypeName(input_typeids[1]),
typeidTypeName(current_typeids[0]), typeidTypeName(current_typeids[1]));
#endif
}
}
for (current_candidate = candidates;
current_candidate != NULL;
current_candidate = current_candidate->next)
{
current_typeids = current_candidate->args;
if ((input_typeids[0] == current_typeids[0])
&& can_coerce_type(1, &input_typeids[1], &current_typeids[1]))
{
#ifdef PARSEDEBUG
printf("oper_select_candidate: (4) choose (%s,%s) -> (%s,%s)...\n",
typeidTypeName(input_typeids[0]), typeidTypeName(input_typeids[1]),
typeidTypeName(current_typeids[0]), typeidTypeName(current_typeids[1]));
#endif
return (current_candidate->args);
}
else if ((input_typeids[1] == current_typeids[1])
&& can_coerce_type(1, &input_typeids[0], &current_typeids[0]))
{
#ifdef PARSEDEBUG
printf("oper_select_candidate: (5) choose (%s,%s) -> (%s,%s)...\n",
typeidTypeName(input_typeids[0]), typeidTypeName(input_typeids[1]),
typeidTypeName(current_typeids[0]), typeidTypeName(current_typeids[1]));
#endif
return (current_candidate->args);
}
else
{
#ifdef PARSEDEBUG
printf("oper_select_candidate: (3) skip (%s,%s) -> (%s,%s)...\n",
typeidTypeName(input_typeids[0]), typeidTypeName(input_typeids[1]),
typeidTypeName(current_typeids[0]), typeidTypeName(current_typeids[1]));
#endif
}
}
return (NULL);
#if FALSE
return (candidates->args);
#endif
} /* oper_select_candidate() */
#endif
/* oper_select_candidate()
* Given the input argtype array and more than one candidate
* for the function argtype array, attempt to resolve the conflict.
* returns the selected argtype array if the conflict can be resolved,
* otherwise returns NULL.
*
* If all input Oids are UNKNOWNOID, then try matching with TEXTOID.
* Otherwise, could return first function arguments on list of candidates.
* But for now, return NULL and make the user give a better hint.
* - thomas 1998-03-17
*/
Oid *
oper_select_candidate(int nargs,
Oid *input_typeids,
CandidateList candidates)
{
CandidateList current_candidate;
CandidateList last_candidate;
Oid *current_typeids;
int unknownOids;
int i;
int ncandidates;
int nbestMatch,
nmatch;
CATEGORY slot_category,
current_category;
Oid slot_type,
current_type;
/*
* Run through all candidates and keep those with the most matches
* on explicit types. Keep all candidates if none match.
*/
ncandidates = 0;
nbestMatch = 0;
last_candidate = NULL;
for (current_candidate = candidates;
current_candidate != NULL;
current_candidate = current_candidate->next)
{
current_typeids = current_candidate->args;
nmatch = 0;
for (i = 0; i < nargs; i++)
{
if ((input_typeids[i] != UNKNOWNOID)
&& (current_typeids[i] == input_typeids[i]))
{
nmatch++;
}
}
#ifdef PARSEDEBUG
printf("oper_select_candidate- candidate has %d matches\n", nmatch);
#endif
if ((nmatch > nbestMatch) || (last_candidate == NULL))
{
nbestMatch = nmatch;
candidates = current_candidate;
last_candidate = current_candidate;
ncandidates = 1;
#ifdef PARSEDEBUG
printf("oper_select_candidate- choose candidate as best match\n");
#endif
}
else if (nmatch == nbestMatch)
{
last_candidate->next = current_candidate;
last_candidate = current_candidate;
ncandidates++;
#ifdef PARSEDEBUG
printf("oper_select_candidate- choose candidate as possible match\n");
#endif
}
else
{
last_candidate->next = NULL;
#ifdef PARSEDEBUG
printf("oper_select_candidate- reject candidate as possible match\n");
#endif
}
}
if (ncandidates <= 1)
return ((ncandidates == 1)? candidates->args: NULL);
/*
* Now look for candidates which allow coersion and are preferred types.
* Keep all candidates if none match.
*/
ncandidates = 0;
nbestMatch = 0;
last_candidate = NULL;
for (current_candidate = candidates;
current_candidate != NULL;
current_candidate = current_candidate->next)
{
current_typeids = current_candidate->args;
nmatch = 0;
for (i = 0; i < nargs; i++)
{
current_category = TypeCategory(current_typeids[i]);
if (input_typeids[i] != UNKNOWNOID)
{
if (current_typeids[i] == input_typeids[i])
{
nmatch++;
}
else if (IsPreferredType(current_category, current_typeids[i])
&& can_coerce_type(1, &input_typeids[i], &current_typeids[i]))
{
nmatch++;
}
}
}
#ifdef PARSEDEBUG
printf("oper_select_candidate- candidate has %d matches\n", nmatch);
#endif
if ((nmatch > nbestMatch) || (last_candidate == NULL))
{
nbestMatch = nmatch;
candidates = current_candidate;
last_candidate = current_candidate;
ncandidates = 1;
#ifdef PARSEDEBUG
printf("oper_select_candidate- choose candidate as best match\n");
#endif
}
else if (nmatch == nbestMatch)
{
last_candidate->next = current_candidate;
last_candidate = current_candidate;
ncandidates++;
#ifdef PARSEDEBUG
printf("oper_select_candidate- choose candidate as possible match\n");
#endif
}
else
{
last_candidate->next = NULL;
#ifdef PARSEDEBUG
printf("oper_select_candidate- reject candidate as possible match\n");
#endif
}
}
if (ncandidates <= 1)
return ((ncandidates == 1)? candidates->args: NULL);
/*
* Still too many candidates?
* Try assigning types for the unknown columns.
*/
if (ncandidates > 1)
{
unknownOids = FALSE;
current_type = UNKNOWNOID;
for (i = 0; i < nargs; i++)
{
if (input_typeids[i] != UNKNOWNOID)
{
current_type = input_typeids[i];
}
else
{
unknownOids = TRUE;
}
}
if (unknownOids && (current_type != UNKNOWNOID))
{
for (current_candidate = candidates;
current_candidate != NULL;
current_candidate = current_candidate->next)
{
nmatch = 0;
for (i = 0; i < nargs; i++)
{
current_typeids = current_candidate->args;
if ((current_type == current_typeids[i])
|| IS_BINARY_COMPATIBLE(current_type, current_typeids[i]))
nmatch++;
}
if (nmatch == nargs)
return (candidates->args);
}
}
for (i = 0; i < nargs; i++)
{
if (input_typeids[i] == UNKNOWNOID)
{
slot_category = INVALID_TYPE;
slot_type = InvalidOid;
for (current_candidate = candidates;
current_candidate != NULL;
current_candidate = current_candidate->next)
{
current_typeids = current_candidate->args;
current_type = current_typeids[i];
current_category = TypeCategory(current_typeids[i]);
if (slot_category == InvalidOid)
{
slot_category = current_category;
slot_type = current_type;
#ifdef PARSEDEBUG
printf("oper_select_candidate- assign column #%d first candidate slot type %s\n",
i, typeidTypeName(current_type));
#endif
}
else if (current_category != slot_category)
{
#ifdef PARSEDEBUG
printf("oper_select_candidate- multiple possible types for column #%d; unable to choose candidate\n", i);
#endif
return NULL;
}
else if (current_type != slot_type)
{
if (IsPreferredType(slot_category, current_type))
{
slot_type = current_type;
candidates = current_candidate;
#ifdef PARSEDEBUG
printf("oper_select_candidate- column #%d found preferred candidate type %s\n",
i, typeidTypeName(slot_type));
#endif
}
else
{
#ifdef PARSEDEBUG
printf("oper_select_candidate- column #%d found possible candidate type %s\n",
i, typeidTypeName(current_type));
#endif
}
}
}
if (slot_type != InvalidOid)
{
input_typeids[i] = slot_type;
#ifdef PARSEDEBUG
printf("oper_select_candidate- assign column #%d slot type %s\n",
i, typeidTypeName(input_typeids[i]));
#endif
}
}
else
{
#ifdef PARSEDEBUG
printf("oper_select_candidate- column #%d input type is %s\n",
i, typeidTypeName(input_typeids[i]));
#endif
}
}
ncandidates = 0;
for (current_candidate = candidates;
current_candidate != NULL;
current_candidate = current_candidate->next)
{
ncandidates++;
}
}
if (ncandidates == 1)
return (candidates->args);
return (NULL);
} /* oper_select_candidate() */
/* func_get_detail() /* func_get_detail()
* Find the named function in the system catalogs. * 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)) if (!HeapTupleIsValid(ftup))
{ {
Type tp; Type tp;
@ -1355,11 +918,8 @@ func_get_detail(char *funcname,
{ {
tp = typeidType(oid_array[0]); tp = typeidType(oid_array[0]);
if (typeTypeFlag(tp) == 'c') 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 else
{ {
@ -1519,7 +1079,7 @@ find_inheritors(Oid relid, Oid **supervec)
/* save the type id, rather than the relation id */ /* save the type id, rather than the relation id */
if ((rd = heap_open(qentry->sqe_relid)) == (Relation) NULL) 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)); qentry->sqe_relid = typeTypeId(typenameType(RelationGetRelationName(rd)->data));
heap_close(rd); heap_close(rd);
@ -1676,7 +1236,7 @@ setup_tlist(char *attname, Oid relid)
attno = get_attnum(relid, attname); attno = get_attnum(relid, attname);
if (attno < 0) if (attno < 0)
elog(ERROR, "cannot reference attribute '%s'" elog(ERROR, "Cannot reference attribute '%s'"
" of tuple params/return values for functions", attname); " of tuple params/return values for functions", attname);
typeid = get_atttype(relid, attno); typeid = get_atttype(relid, attno);
@ -1780,7 +1340,7 @@ ParseComplexProjection(ParseState *pstate,
} }
else else
{ {
elog(ERROR, "Function '%s' has bad returntype %d", elog(ERROR, "Function '%s' has bad return type %d",
funcname, argtype); 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); funcname, argtype);
break; break;
} }
@ -1918,7 +1478,7 @@ func_error(char *caller, char *funcname, int nargs, Oid *argtypes, char *msg)
if (caller == NULL) 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: "")); funcname, p, ((msg != NULL)? "\n\t": ""), ((msg != NULL)? msg: ""));
} }
else else

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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_oper.h"
#include "parser/parse_relation.h" #include "parser/parse_relation.h"
#include "parser/parse_type.h" #include "parser/parse_type.h"
#include "parser/parse_coerce.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/syscache.h" #include "utils/syscache.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
@ -36,13 +37,10 @@ make_operand(char *opname,
Oid orig_typeId, Oid orig_typeId,
Oid true_typeId); Oid true_typeId);
/* /* make_parsestate()
* make_parsestate() -- * Allocate and initialize a new ParseState.
* allocate and initialize a new ParseState. * The CALLER is responsible for freeing the ParseState* returned.
* the CALLER is responsible for freeing the ParseState* returned
*
*/ */
ParseState * ParseState *
make_parsestate(ParseState *parentParseState) 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() /* make_operand()
* Ensure argument type match by forcing conversion of constants. * Ensure argument type match by forcing conversion of constants.
*/ */
@ -74,10 +67,6 @@ make_operand(char *opname,
{ {
Node *result; Node *result;
Type true_type; Type true_type;
#if FALSE
Datum val;
Oid infunc;
#endif
#ifdef PARSEDEBUG #ifdef PARSEDEBUG
printf("make_operand: constructing operand for '%s' %s->%s\n", 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() /* make_op()
* Operator construction. * Operator construction.
* *
@ -174,7 +133,7 @@ make_op(char *opname, Node *ltree, Node *rtree)
{ {
Oid ltypeId, Oid ltypeId,
rtypeId; rtypeId;
Operator temp; Operator tup;
OperatorTupleForm opform; OperatorTupleForm opform;
Oper *newop; Oper *newop;
Node *left, Node *left,
@ -185,8 +144,8 @@ make_op(char *opname, Node *ltree, Node *rtree)
if (rtree == NULL) if (rtree == NULL)
{ {
ltypeId = (ltree == NULL) ? UNKNOWNOID : exprType(ltree); ltypeId = (ltree == NULL) ? UNKNOWNOID : exprType(ltree);
temp = right_oper(opname, ltypeId); tup = right_oper(opname, ltypeId);
opform = (OperatorTupleForm) GETSTRUCT(temp); opform = (OperatorTupleForm) GETSTRUCT(tup);
left = make_operand(opname, ltree, ltypeId, opform->oprleft); left = make_operand(opname, ltree, ltypeId, opform->oprleft);
right = NULL; right = NULL;
@ -196,11 +155,11 @@ make_op(char *opname, Node *ltree, Node *rtree)
else if (ltree == NULL) else if (ltree == NULL)
{ {
rtypeId = (rtree == NULL) ? UNKNOWNOID : exprType(rtree); rtypeId = (rtree == NULL) ? UNKNOWNOID : exprType(rtree);
temp = left_oper(opname, rtypeId); tup = left_oper(opname, rtypeId);
#ifdef PARSEDEBUG #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 #endif
opform = (OperatorTupleForm) GETSTRUCT(temp); opform = (OperatorTupleForm) GETSTRUCT(tup);
#ifdef PARSEDEBUG #ifdef PARSEDEBUG
printf("make_op: calling make_operand()\n"); printf("make_op: calling make_operand()\n");
#endif #endif
@ -212,80 +171,28 @@ printf("make_op: calling make_operand()\n");
/* otherwise, binary operator */ /* otherwise, binary operator */
else else
{ {
#define CONVERTIBLE_TYPE(t) ( (t) == INT2OID || \
(t) == INT4OID || \
(t) == OIDOID || \
(t) == FLOAT4OID || \
(t) == FLOAT8OID || \
(t) == CASHOID)
/* binary operator */ /* binary operator */
ltypeId = (ltree == NULL) ? UNKNOWNOID : exprType(ltree); ltypeId = (ltree == NULL) ? UNKNOWNOID : exprType(ltree);
rtypeId = (rtree == NULL) ? UNKNOWNOID : exprType(rtree); rtypeId = (rtree == NULL) ? UNKNOWNOID : exprType(rtree);
#if FALSE /* check for exact match on this operator... */
/* Both operands of unknown type? if (HeapTupleIsValid(tup = oper_exact(opname, ltypeId, rtypeId, &ltree, &rtree, TRUE)))
* 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 ltypeId = exprType(ltree);
printf( "Convert left-hand constant to text for node %d\n", nodeTag(ltree)); rtypeId = exprType(rtree);
#endif
ltypeId = CoerceType(TEXTOID, ltree);
} }
#endif /* try to find a match on likely candidates... */
else if (!HeapTupleIsValid(tup = oper_inexact(opname, ltypeId, rtypeId, &ltree, &rtree, FALSE)))
#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)
{ {
outfunc = typeidOutfunc(rtypeId); /* Won't return from oper_inexact() without a candidate... */
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);
} }
if (CONVERTIBLE_TYPE(rtypeId) && nodeTag(rtree) != T_Const && opform = (OperatorTupleForm) GETSTRUCT(tup);
CONVERTIBLE_TYPE(ltypeId) && nodeTag(ltree) == T_Const &&
!((Const *) ltree)->constiscast)
{
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);
}
#endif
temp = oper(opname, ltypeId, rtypeId, false);
opform = (OperatorTupleForm) GETSTRUCT(temp);
left = make_operand(opname, ltree, ltypeId, opform->oprleft); left = make_operand(opname, ltree, ltypeId, opform->oprleft);
right = make_operand(opname, rtree, rtypeId, opform->oprright); right = make_operand(opname, rtree, rtypeId, opform->oprright);
} }
newop = makeOper(oprid(temp), /* opno */ newop = makeOper(oprid(tup), /* opno */
InvalidOid, /* opid */ InvalidOid, /* opid */
opform->oprresult, /* operator result type */ opform->oprresult, /* operator result type */
0, 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)); result->args = lcons(left, lcons(right, NIL));
return result; return result;
} } /* make_op() */
Var * Var *
@ -538,7 +445,7 @@ make_const(Value *value)
default: default:
{ {
if (nodeTag(value) != T_Null) 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 */ /* null const */
con = makeConst(0, 0, (Datum) NULL, true, false, false, false); con = makeConst(0, 0, (Datum) NULL, true, false, false, false);

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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_operator.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "fmgr.h" #include "fmgr.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h" #include "parser/parse_oper.h"
#include "parser/parse_type.h" #include "parser/parse_type.h"
#include "parser/parse_coerce.h" #include "parser/parse_coerce.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "utils/syscache.h" #include "utils/syscache.h"
extern
Oid *
func_select_candidate(int nargs, Oid *input_typeids, CandidateList candidates);
extern
Oid * Oid *
oper_select_candidate(int nargs, Oid *input_typeids, CandidateList candidates); oper_select_candidate(int nargs, Oid *input_typeids, CandidateList candidates);
static int static int
binary_oper_get_candidates(char *opname, binary_oper_get_candidates(char *opname,
Oid leftTypeId, Oid leftTypeId,
Oid rightTypeId, Oid rightTypeId,
CandidateList *candidates); 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 static int
unary_oper_get_candidates(char *op, unary_oper_get_candidates(char *op,
Oid typeId, Oid typeId,
CandidateList *candidates, CandidateList *candidates,
char rightleft); char rightleft);
static void
op_error(char *op, Oid arg1, Oid arg2);
Oid Oid
any_ordering_op(int restype) any_ordering_op(int restype)
@ -59,7 +49,13 @@ any_ordering_op(int restype)
Operator order_op; Operator order_op;
Oid order_opid; 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); order_opid = oprid(order_op);
return order_opid; return order_opid;
@ -107,44 +103,12 @@ binary_oper_get_candidates(char *opname,
F_CHAREQ, F_CHAREQ,
CharGetDatum('b')); 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; nkeys = 2;
pg_operator_desc = heap_openr(OperatorRelationName); pg_operator_desc = heap_openr(OperatorRelationName);
pg_operator_scan = heap_beginscan(pg_operator_desc, pg_operator_scan = heap_beginscan(pg_operator_desc,
0, 0,
true, TRUE,
nkeys, nkeys,
opKey); opKey);
@ -173,227 +137,24 @@ binary_oper_get_candidates(char *opname,
} /* binary_oper_get_candidates() */ } /* binary_oper_get_candidates() */
#if FALSE /* oper_select_candidate()
/* BinaryOperCandidates() * Given the input argtype array and more than one candidate
* Given opname, leftTypeId and rightTypeId, * for the function argtype array, attempt to resolve the conflict.
* find all possible (arg1, arg2) pairs for which an operator named * returns the selected argtype array if the conflict can be resolved,
* opname exists, such that leftTypeId can be coerced to arg1 and * otherwise returns NULL.
* rightTypeId can be coerced to arg2. *
*/ * This routine is new code, replacing binary_oper_select_candidate()
static int * which dates from v4.2/v1.0.x days. It tries very hard to match up
BinaryOperCandidates(char *opname, * operators with types, including allowing type coersions if necessary.
Oid lTypeId, * The important thing is that the code do as much as possible,
Oid rTypeId, * while _never_ doing the wrong thing, where "the wrong thing" would
CandidateList *candidates) * be returning an operator when other better choices are available,
{ * or returning an operator which is a non-intuitive possibility.
CandidateList current_candidate; * - thomas 1998-05-21
Relation pg_operator_desc; *
HeapScanDesc pg_operator_scan; * The comments below came from binary_oper_select_candidate(), and
HeapTuple tup; * illustrate the issues and choices which are possible:
OperatorTupleForm oper; * - thomas 1998-05-20
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)
{
nkeys = 2;
}
else
{
nkeys = 3;
ScanKeyEntryInitialize(&opKey[2], 0,
Anum_pg_operator_oprright,
F_OIDEQ,
ObjectIdGetDatum(rightTypeId));
}
}
else if (rightTypeId == UNKNOWNOID)
{
nkeys = 3;
ScanKeyEntryInitialize(&opKey[2], 0,
Anum_pg_operator_oprleft,
F_OIDEQ,
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;
} /* BinaryOperCandidates() */
#endif
/*
* 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).
*/
static bool
equivalentOpersAfterPromotion(CandidateList candidates)
{
CandidateList result;
CandidateList promotedCandidates = NULL;
Oid leftarg,
rightarg;
for (result = candidates; result != NULL; result = result->next)
{
CandidateList c;
c = (CandidateList) palloc(sizeof(*c));
c->args = (Oid *) palloc(2 * sizeof(Oid));
switch (result->args[0])
{
case FLOAT4OID:
case INT4OID:
case INT2OID:
case CASHOID:
c->args[0] = FLOAT8OID;
break;
default:
c->args[0] = result->args[0];
break;
}
switch (result->args[1])
{
case FLOAT4OID:
case INT4OID:
case INT2OID:
case CASHOID:
c->args[1] = FLOAT8OID;
break;
default:
c->args[1] = result->args[1];
break;
}
c->next = promotedCandidates;
promotedCandidates = c;
}
/*
* if we get called, we have more than 1 candidates so we can do the
* following safely
*/
leftarg = promotedCandidates->args[0];
rightarg = promotedCandidates->args[1];
for (result = promotedCandidates->next; result != NULL; result = result->next)
{
if (result->args[0] != leftarg || result->args[1] != rightarg)
/*
* this list contains operators that operate on different data
* types even after promotion. Hence we can't decide on which
* one to pick. The user must do explicit type casting.
*/
return FALSE;
}
/*
* all the candidates are equivalent in the following sense: they
* operate on equivalent data types and picking any one of them is as
* good.
*/
return TRUE;
}
/* 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 * current wisdom holds that the default operator should be one in which
* both operands have the same type (there will only be one such * both operands have the same type (there will only be one such
@ -419,42 +180,357 @@ equivalentOpersAfterPromotion(CandidateList candidates)
* some sense. (see equivalentOpersAfterPromotion for details.) * some sense. (see equivalentOpersAfterPromotion for details.)
* - ay 6/95 * - ay 6/95
*/ */
static CandidateList Oid *
binary_oper_select_candidate(Oid arg1, oper_select_candidate(int nargs,
Oid arg2, Oid *input_typeids,
CandidateList candidates) CandidateList candidates)
{ {
CandidateList result; CandidateList current_candidate;
CandidateList last_candidate;
Oid *current_typeids;
int unknownOids;
int i;
/* int ncandidates;
* If both are "unknown", there is no way to select a candidate int nbestMatch,
*/ nmatch;
if (arg1 == UNKNOWNOID && arg2 == UNKNOWNOID)
return (NULL);
if (!equivalentOpersAfterPromotion(candidates)) CATEGORY slot_category,
return NULL; current_category;
Oid slot_type,
current_type;
/* /*
* if we get here, any one will do but we're more picky and require * Run through all candidates and keep those with the most matches
* both operands be the same. * on explicit types. Keep all candidates if none match.
*/ */
for (result = candidates; result != NULL; result = result->next) ncandidates = 0;
nbestMatch = 0;
last_candidate = NULL;
for (current_candidate = candidates;
current_candidate != NULL;
current_candidate = current_candidate->next)
{ {
if (result->args[0] == result->args[1]) current_typeids = current_candidate->args;
return result; 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
}
} }
return (NULL); 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;
}
return ((ncandidates == 1)? candidates->args: NULL);
}
/* oper() /*
* 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)
{
current_typeids = current_candidate->args;
nmatch = 0;
for (i = 0; i < nargs; i++)
{
current_category = TypeCategory(current_typeids[i]);
if (input_typeids[i] != UNKNOWNOID)
{
if (current_typeids[i] == input_typeids[i])
{
nmatch++;
}
else if (IsPreferredType(current_category, current_typeids[i])
&& can_coerce_type(1, &input_typeids[i], &current_typeids[i]))
{
nmatch++;
}
}
}
#ifdef PARSEDEBUG
printf("oper_select_candidate- candidate has %d matches\n", nmatch);
#endif
if ((nmatch > nbestMatch) || (last_candidate == NULL))
{
nbestMatch = nmatch;
candidates = current_candidate;
last_candidate = current_candidate;
ncandidates = 1;
#ifdef PARSEDEBUG
printf("oper_select_candidate- choose candidate as best match\n");
#endif
}
else if (nmatch == nbestMatch)
{
last_candidate->next = current_candidate;
last_candidate = current_candidate;
ncandidates++;
#ifdef PARSEDEBUG
printf("oper_select_candidate- choose candidate as possible match\n");
#endif
}
else
{
last_candidate->next = NULL;
#ifdef PARSEDEBUG
printf("oper_select_candidate- reject candidate as possible match\n");
#endif
}
}
if (ncandidates <= 1)
{
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);
}
/*
* Still too many candidates?
* Try assigning types for the unknown columns.
*/
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;
}
}
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)
{
if (can_coerce_type(1, &input_typeids[0], &current_candidate->args[0])
&& can_coerce_type(1, &input_typeids[1], &current_candidate->args[1]))
ncandidates++;
}
return ((ncandidates == 1)? candidates->args: NULL);
} /* oper_select_candidate() */
/* oper_exact()
* Given operator, and arguments, return oper struct.
* Inputs:
* arg1, arg2: Type IDs
*/
Operator
oper_exact(char *op, Oid arg1, Oid arg2, Node **ltree, Node **rtree, bool noWarnings)
{
HeapTuple tup;
Node *tree;
/* 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;
tup = SearchSysCacheTuple(OPRNAME,
PointerGetDatum(op),
ObjectIdGetDatum(arg1),
ObjectIdGetDatum(arg2),
Int8GetDatum('b'));
/* Did not find anything? then try flipping arguments on a commutative operator... */
if (!HeapTupleIsValid(tup) && (arg1 != arg2))
{
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 tup;
} /* oper_exact() */
/* oper_inexact()
* Given operator, types of arg1, and arg2, return oper struct. * Given operator, types of arg1, and arg2, return oper struct.
* Inputs: * Inputs:
* arg1, arg2: Type IDs * arg1, arg2: Type IDs
*/ */
Operator 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; HeapTuple tup;
CandidateList candidates; CandidateList candidates;
@ -468,77 +544,95 @@ oper(char *op, Oid arg1, Oid arg2, bool noWarnings)
if (arg1 == InvalidOid) if (arg1 == InvalidOid)
arg1 = arg2; arg1 = arg2;
tup = SearchSysCacheTuple(OPRNAME, ncandidates = binary_oper_get_candidates(op, arg1, arg2, &candidates);
PointerGetDatum(op),
ObjectIdGetDatum(arg1),
ObjectIdGetDatum(arg2),
Int8GetDatum('b'));
/* Did not find anything? then look more carefully... */ /* No operators found? Then throw error or return null... */
if (!HeapTupleIsValid(tup)) 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... */ /* Or found exactly one? Then proceed... */
if (ncandidates == 0) else if (ncandidates == 1)
{ {
if (!noWarnings) tup = SearchSysCacheTuple(OPRNAME,
op_error(op, arg1, arg2); PointerGetDatum(op),
return (NULL); ObjectIdGetDatum(candidates->args[0]),
} ObjectIdGetDatum(candidates->args[1]),
Int8GetDatum('b'));
Assert(HeapTupleIsValid(tup));
/* Or found exactly one? Then proceed... */ #if PARSEDEBUG
else if (ncandidates == 1) 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, tup = SearchSysCacheTuple(OPRNAME,
PointerGetDatum(op), PointerGetDatum(op),
ObjectIdGetDatum(candidates->args[0]), ObjectIdGetDatum(targetOids[0]),
ObjectIdGetDatum(candidates->args[1]), ObjectIdGetDatum(targetOids[1]),
Int8GetDatum('b')); Int8GetDatum('b'));
Assert(HeapTupleIsValid(tup));
}
/* Otherwise, multiple operators of the desired types found... */ }
else else
{ {
#if FALSE tup = NULL;
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;
}
/* Could not choose one, for whatever reason... */ /* Could not choose one, for whatever reason... */
if (!HeapTupleIsValid(tup)) if (!HeapTupleIsValid(tup))
{
if (!noWarnings)
{ {
if (!noWarnings) 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",
elog(ERROR, "There is more than one operator '%s' for types '%s' and '%s'" op, typeTypeName(typeidType(arg1)), typeTypeName(typeidType(arg2)));
"\n\tYou will have to retype this query using an explicit cast",
op, typeTypeName(typeidType(arg1)), typeTypeName(typeidType(arg2)));
}
return (NULL);
} }
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); return ((Operator) tup);
} /* oper() */ } /* oper() */
@ -573,26 +667,13 @@ unary_oper_get_candidates(char *op,
fmgr_info(F_CHAREQ, (FmgrInfo *) &opKey[1].sk_func); fmgr_info(F_CHAREQ, (FmgrInfo *) &opKey[1].sk_func);
opKey[1].sk_argument = CharGetDatum(rightleft); 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 #ifdef PARSEDEBUG
printf("unary_oper_get_candidates: start scan for '%s'\n", op); printf("unary_oper_get_candidates: start scan for '%s'\n", op);
#endif #endif
pg_operator_desc = heap_openr(OperatorRelationName); pg_operator_desc = heap_openr(OperatorRelationName);
pg_operator_scan = heap_beginscan(pg_operator_desc, pg_operator_scan = heap_beginscan(pg_operator_desc,
0, 0,
true, TRUE,
2, 2,
opKey); opKey);
@ -658,17 +739,13 @@ right_oper(char *op, Oid arg)
{ {
tup = SearchSysCacheTuple(OPRNAME, tup = SearchSysCacheTuple(OPRNAME,
PointerGetDatum(op), PointerGetDatum(op),
ObjectIdGetDatum(candidates->args[0]), ObjectIdGetDatum(candidates->args[0]),
ObjectIdGetDatum(InvalidOid), ObjectIdGetDatum(InvalidOid),
Int8GetDatum('r')); Int8GetDatum('r'));
Assert(HeapTupleIsValid(tup)); Assert(HeapTupleIsValid(tup));
} }
else 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); targetOid = func_select_candidate(1, &arg, candidates);
if (targetOid != NULL) if (targetOid != NULL)
@ -735,10 +812,6 @@ printf("left_oper: searched cache for single left oper candidate '%s %s'\n",
} }
else 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); targetOid = func_select_candidate(1, &arg, candidates);
tup = SearchSysCacheTuple(OPRNAME, tup = SearchSysCacheTuple(OPRNAME,
PointerGetDatum(op), PointerGetDatum(op),
@ -779,7 +852,7 @@ op_error(char *op, Oid arg1, Oid arg2)
else else
{ {
elog(ERROR, "Left hand side of operator '%s' has an unknown type" 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)) if (typeidIsValid(arg2))
@ -789,11 +862,11 @@ op_error(char *op, Oid arg1, Oid arg2)
else else
{ {
elog(ERROR, "Right hand side of operator %s has an unknown type" 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'" 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\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)); op, typeTypeName(tp1), typeTypeName(tp2));
} }

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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_node.h"
#include "parser/parse_relation.h" #include "parser/parse_relation.h"
#include "parser/parse_target.h" #include "parser/parse_target.h"
#include "parser/parse_coerce.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/syscache.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 List *expandAllTables(ParseState *pstate);
static char *figureColname(Node *expr, Node *resval); static char *figureColname(Node *expr, Node *resval);
@ -46,11 +42,6 @@ size_target_expr(ParseState *pstate,
Node *expr, Node *expr,
Oid attrtype, Oid attrtype,
int16 attrtypmod); 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; return p_target;
} } /* transformTargetList() */
Node * Node *

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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; infunc = type->typinput;
return (infunc); 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