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,16 +394,22 @@ 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)
@ -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);
@ -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); ltypeId = exprType(ltree);
rtypeId = exprType(rtree); 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 /* 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;
int nbestMatch,
nmatch;
CATEGORY slot_category,
current_category;
Oid slot_type,
current_type;
/* /*
* If both are "unknown", there is no way to select a candidate * Run through all candidates and keep those with the most matches
* on explicit types. Keep all candidates if none match.
*/ */
if (arg1 == UNKNOWNOID && arg2 == UNKNOWNOID) ncandidates = 0;
return (NULL); nbestMatch = 0;
last_candidate = NULL;
if (!equivalentOpersAfterPromotion(candidates)) for (current_candidate = candidates;
return NULL; current_candidate != NULL;
current_candidate = current_candidate->next)
/*
* 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)
{ {
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++;
}
} }
return (NULL); #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
}
} }
/* oper() 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);
}
/*
* 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,15 +544,6 @@ oper(char *op, Oid arg1, Oid arg2, bool noWarnings)
if (arg1 == InvalidOid) if (arg1 == InvalidOid)
arg1 = arg2; arg1 = arg2;
tup = SearchSysCacheTuple(OPRNAME,
PointerGetDatum(op),
ObjectIdGetDatum(arg1),
ObjectIdGetDatum(arg2),
Int8GetDatum('b'));
/* Did not find anything? then look more carefully... */
if (!HeapTupleIsValid(tup))
{
ncandidates = binary_oper_get_candidates(op, arg1, arg2, &candidates); ncandidates = binary_oper_get_candidates(op, arg1, arg2, &candidates);
/* No operators found? Then throw error or return null... */ /* No operators found? Then throw error or return null... */
@ -496,30 +563,30 @@ oper(char *op, Oid arg1, Oid arg2, bool noWarnings)
ObjectIdGetDatum(candidates->args[1]), ObjectIdGetDatum(candidates->args[1]),
Int8GetDatum('b')); Int8GetDatum('b'));
Assert(HeapTupleIsValid(tup)); Assert(HeapTupleIsValid(tup));
#if PARSEDEBUG
printf("oper_inexact: found single candidate\n");
#endif
} }
/* Otherwise, multiple operators of the desired types found... */ /* Otherwise, multiple operators of the desired types found... */
else else
{ {
#if FALSE
candidates = binary_oper_select_candidate(arg1, arg2, candidates);
#endif
inputOids[0] = arg1; inputOids[0] = arg1;
inputOids[1] = arg2; inputOids[1] = arg2;
targetOids = oper_select_candidate(2, inputOids, candidates); targetOids = oper_select_candidate(2, inputOids, candidates);
#if FALSE
targetOids = func_select_candidate(2, inputOids, candidates);
#endif
if (targetOids != NULL) if (targetOids != NULL)
{ {
#if PARSEDEBUG #if PARSEDEBUG
printf("oper: found candidate\n"); printf("oper_inexact: found candidate\n");
#endif #endif
tup = SearchSysCacheTuple(OPRNAME, tup = SearchSysCacheTuple(OPRNAME,
PointerGetDatum(op), PointerGetDatum(op),
ObjectIdGetDatum(targetOids[0]), ObjectIdGetDatum(targetOids[0]),
ObjectIdGetDatum(targetOids[1]), ObjectIdGetDatum(targetOids[1]),
Int8GetDatum('b')); Int8GetDatum('b'));
} }
else else
{ {
@ -531,14 +598,41 @@ printf("oper: found candidate\n");
{ {
if (!noWarnings) if (!noWarnings)
{ {
elog(ERROR, "There is more than one operator '%s' for types '%s' and '%s'" 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", "\n\tYou will have to retype this query using an explicit cast",
op, typeTypeName(typeidType(arg1)), typeTypeName(typeidType(arg2))); 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);
@ -665,10 +746,6 @@ right_oper(char *op, Oid arg)
} }
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),

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