Commit 8536c962 authored by Thomas G. Lockhart's avatar Thomas G. Lockhart

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.
parent 329083a9
...@@ -7,7 +7,7 @@ ...@@ -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 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -436,4 +467,4 @@ transformUnionClause(List *unionClause, List *targetlist)
} }
else else
return NIL; return NIL;
} } /* transformUnionClause() */
...@@ -7,7 +7,7 @@ ...@@ -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) ...@@ -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)
......
...@@ -7,7 +7,7 @@ ...@@ -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) ...@@ -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
*/ */
......
This diff is collapsed.
...@@ -7,7 +7,7 @@ ...@@ -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 @@ ...@@ -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, ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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"); ...@@ -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
#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);
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);
} }
/* try to find a match on likely candidates... */
if (CONVERTIBLE_TYPE(rtypeId) && nodeTag(rtree) != T_Const && else if (!HeapTupleIsValid(tup = oper_inexact(opname, ltypeId, rtypeId, &ltree, &rtree, FALSE)))
CONVERTIBLE_TYPE(ltypeId) && nodeTag(ltree) == T_Const &&
!((Const *) ltree)->constiscast)
{ {
outfunc = typeidOutfunc(ltypeId); /* Won't return from oper_inexact() without a candidate... */
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(tup);
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)); ...@@ -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) ...@@ -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);
......
This diff is collapsed.
...@@ -7,7 +7,7 @@ ...@@ -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 @@ ...@@ -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, ...@@ -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) ...@@ -357,7 +348,7 @@ transformTargetList(ParseState *pstate, List *targetlist)
} }
return p_target; return p_target;
} } /* transformTargetList() */
Node * Node *
......
...@@ -7,7 +7,7 @@ ...@@ -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) ...@@ -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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment