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 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.16 1998/05/21 03:53:50 scrappy Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.17 1998/05/29 14:00:19 thomas Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -25,12 +25,15 @@
#include "parser/parse_oper.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parse_coerce.h"
static TargetEntry *
find_targetlist_entry(ParseState *pstate,
SortGroupBy *sortgroupby, List *tlist);
static void parseFromClause(ParseState *pstate, List *frmList);
/*
* makeRangeTable -
* make a range table with the specified relation (optional) and the
......@@ -78,8 +81,7 @@ transformWhereClause(ParseState *pstate, Node *a_expr)
if (exprType(qual) != BOOLOID)
{
elog(ERROR,
"where clause must return type bool, not %s",
elog(ERROR, "WHERE clause must return type bool, not type %s",
typeidTypeName(exprType(qual)));
}
return qual;
......@@ -167,7 +169,7 @@ find_targetlist_entry(ParseState *pstate, SortGroupBy *sortgroupby, List *tlist)
if (real_rtable_pos == test_rtable_pos)
{
if (target_result != NULL)
elog(ERROR, "Order/Group By '%s' is ambiguous", sortgroupby->name);
elog(ERROR, "ORDER/GROUP BY '%s' is ambiguous", sortgroupby->name);
else
target_result = target;
}
......@@ -175,7 +177,7 @@ find_targetlist_entry(ParseState *pstate, SortGroupBy *sortgroupby, List *tlist)
else
{
if (target_result != NULL)
elog(ERROR, "Order/Group By '%s' is ambiguous", sortgroupby->name);
elog(ERROR, "ORDER/GROUP BY '%s' is ambiguous", sortgroupby->name);
else
target_result = target;
}
......@@ -372,7 +374,7 @@ transformSortClause(ParseState *pstate,
break;
}
if (i == NIL)
elog(ERROR, "The field specified in the UNIQUE ON clause is not in the targetlist");
elog(ERROR, "All fields in the UNIQUE ON clause must appear in the target list");
foreach(s, sortlist)
{
......@@ -392,16 +394,22 @@ transformSortClause(ParseState *pstate,
sortlist = lappend(sortlist, sortcl);
}
}
}
return sortlist;
}
/*
* transformUnionClause -
* transform a Union clause
*
/* transformUnionClause()
* Transform a UNION clause.
* Note that the union clause is actually a fully-formed select structure.
* So, it is evaluated as a select, then the resulting target fields
* are matched up to ensure correct types in the results.
* The select clause parsing is done recursively, so the unions are evaluated
* right-to-left. One might want to look at all columns from all clauses before
* trying to coerce, but unless we keep track of the call depth we won't know
* when to do this because of the recursion.
* Let's just try matching in pairs for now (right to left) and see if it works.
* - thomas 1998-05-22
*/
List *
transformUnionClause(List *unionClause, List *targetlist)
......@@ -421,13 +429,36 @@ transformUnionClause(List *unionClause, List *targetlist)
List *next_target;
if (length(targetlist) != length(qlist->qtrees[i]->targetList))
elog(ERROR,"Each UNION query must have the same number of columns.");
elog(ERROR,"Each UNION clause must have the same number of columns");
foreach(next_target, qlist->qtrees[i]->targetList)
{
if (((TargetEntry *)lfirst(prev_target))->resdom->restype !=
((TargetEntry *)lfirst(next_target))->resdom->restype)
elog(ERROR,"Each UNION query must have identical target types.");
Oid itype;
Oid otype;
otype = ((TargetEntry *)lfirst(prev_target))->resdom->restype;
itype = ((TargetEntry *)lfirst(next_target))->resdom->restype;
if (itype != otype)
{
Node *expr;
expr = ((TargetEntry *)lfirst(next_target))->expr;
expr = coerce_target_expr(NULL, expr, itype, otype);
if (expr == NULL)
{
elog(ERROR,"Unable to transform %s to %s"
"\n\tEach UNION clause must have compatible target types",
typeidTypeName(itype),
typeidTypeName(otype));
}
((TargetEntry *)lfirst(next_target))->expr = expr;
((TargetEntry *)lfirst(next_target))->resdom->restype = otype;
}
/* both are UNKNOWN? then evaluate as text... */
else if (itype == UNKNOWNOID)
{
((TargetEntry *)lfirst(next_target))->resdom->restype = TEXTOID;
((TargetEntry *)lfirst(prev_target))->resdom->restype = TEXTOID;
}
prev_target = lnext(prev_target);
}
union_list = lappend(union_list, qlist->qtrees[i]);
......@@ -436,4 +467,4 @@ transformUnionClause(List *unionClause, List *targetlist)
}
else
return NIL;
}
} /* transformUnionClause() */
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.1 1998/05/09 23:29:53 thomas Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.2 1998/05/29 14:00:20 thomas Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -319,7 +319,7 @@ IsPreferredType(CATEGORY category, Oid type)
/* PreferredType()
* Assign a category to the specified OID.
* Return the preferred type OID for the specified category.
*/
Oid
PreferredType(CATEGORY category, Oid type)
......
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.28 1998/05/09 23:29:53 thomas Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.29 1998/05/29 14:00:21 thomas Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -302,12 +302,12 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
break;
}
/* These nodes do _not_ come from the original parse tree,
/* Some nodes do _not_ come from the original parse tree,
* but result from parser transformation in this phase.
* At least one construct (BETWEEN/AND) puts the same nodes
* into two branches of the parse tree; hence, some nodes
* are transformed twice.
* These cases below come from transforming function calls.
* The three cases below come from transforming function calls.
* Let's try just passing them through...
* - thomas 1998-03-14
*/
......
This diff is collapsed.
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.15 1998/05/09 23:29:53 thomas Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.16 1998/05/29 14:00:21 thomas Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -25,6 +25,7 @@
#include "parser/parse_oper.h"
#include "parser/parse_relation.h"
#include "parser/parse_type.h"
#include "parser/parse_coerce.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
#include "utils/lsyscache.h"
......@@ -36,13 +37,10 @@ make_operand(char *opname,
Oid orig_typeId,
Oid true_typeId);
/*
* make_parsestate() --
* allocate and initialize a new ParseState.
* the CALLER is responsible for freeing the ParseState* returned
*
/* make_parsestate()
* Allocate and initialize a new ParseState.
* The CALLER is responsible for freeing the ParseState* returned.
*/
ParseState *
make_parsestate(ParseState *parentParseState)
{
......@@ -58,11 +56,6 @@ make_parsestate(ParseState *parentParseState)
}
extern
Node *
coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId);
/* make_operand()
* Ensure argument type match by forcing conversion of constants.
*/
......@@ -74,10 +67,6 @@ make_operand(char *opname,
{
Node *result;
Type true_type;
#if FALSE
Datum val;
Oid infunc;
#endif
#ifdef PARSEDEBUG
printf("make_operand: constructing operand for '%s' %s->%s\n",
......@@ -133,36 +122,6 @@ disallow_setop(char *op, Type optype, Node *operand)
}
/* CoerceType()
* Try to force type of node.
*/
Oid CoerceType(Oid typeId, Node *node);
Oid
CoerceType(Oid typeId, Node *node)
{
switch (nodeTag(node))
{
case T_Const:
{
Const *con = (Const *) node;
#ifdef PARSEDEBUG
printf( "Convert node %d to text\n", nodeTag(node));
#endif
typeId = TEXTOID;
con->consttype = typeId;
}
break;
default:
break;
}
return typeId;
} /* CoerceType() */
/* make_op()
* Operator construction.
*
......@@ -174,7 +133,7 @@ make_op(char *opname, Node *ltree, Node *rtree)
{
Oid ltypeId,
rtypeId;
Operator temp;
Operator tup;
OperatorTupleForm opform;
Oper *newop;
Node *left,
......@@ -185,8 +144,8 @@ make_op(char *opname, Node *ltree, Node *rtree)
if (rtree == NULL)
{
ltypeId = (ltree == NULL) ? UNKNOWNOID : exprType(ltree);
temp = right_oper(opname, ltypeId);
opform = (OperatorTupleForm) GETSTRUCT(temp);
tup = right_oper(opname, ltypeId);
opform = (OperatorTupleForm) GETSTRUCT(tup);
left = make_operand(opname, ltree, ltypeId, opform->oprleft);
right = NULL;
......@@ -196,11 +155,11 @@ make_op(char *opname, Node *ltree, Node *rtree)
else if (ltree == NULL)
{
rtypeId = (rtree == NULL) ? UNKNOWNOID : exprType(rtree);
temp = left_oper(opname, rtypeId);
tup = left_oper(opname, rtypeId);
#ifdef PARSEDEBUG
printf("make_op: returned from left_oper() with structure at %p\n", (void *)temp);
printf("make_op: returned from left_oper() with structure at %p\n", (void *)tup);
#endif
opform = (OperatorTupleForm) GETSTRUCT(temp);
opform = (OperatorTupleForm) GETSTRUCT(tup);
#ifdef PARSEDEBUG
printf("make_op: calling make_operand()\n");
#endif
......@@ -212,80 +171,28 @@ printf("make_op: calling make_operand()\n");
/* otherwise, binary operator */
else
{
#define CONVERTIBLE_TYPE(t) ( (t) == INT2OID || \
(t) == INT4OID || \
(t) == OIDOID || \
(t) == FLOAT4OID || \
(t) == FLOAT8OID || \
(t) == CASHOID)
/* binary operator */
ltypeId = (ltree == NULL) ? UNKNOWNOID : exprType(ltree);
rtypeId = (rtree == NULL) ? UNKNOWNOID : exprType(rtree);
#if FALSE
/* Both operands of unknown type?
* Then they are strings and we should force at least one to text
* - thomas 1998-03-16
*/
/* check for exact match on this operator... */
if (HeapTupleIsValid(tup = oper_exact(opname, ltypeId, rtypeId, &ltree, &rtree, TRUE)))
{
ltypeId = exprType(ltree);
rtypeId = exprType(rtree);
if ((ltypeId == UNKNOWNOID)
&& (rtypeId == UNKNOWNOID))
{
#ifdef PARSEDEBUG
printf( "Convert left-hand constant to text for node %d\n", nodeTag(ltree));
#endif
ltypeId = CoerceType(TEXTOID, ltree);
}
#endif
#if FALSE
/*
* convert constant when using a const of a numeric type and a
* non-const of another numeric type
*/
if (CONVERTIBLE_TYPE(ltypeId) && nodeTag(ltree) != T_Const &&
CONVERTIBLE_TYPE(rtypeId) && nodeTag(rtree) == T_Const &&
!((Const *) rtree)->constiscast)
{
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);
}
if (CONVERTIBLE_TYPE(rtypeId) && nodeTag(rtree) != T_Const &&
CONVERTIBLE_TYPE(ltypeId) && nodeTag(ltree) == T_Const &&
!((Const *) ltree)->constiscast)
/* try to find a match on likely candidates... */
else if (!HeapTupleIsValid(tup = oper_inexact(opname, ltypeId, rtypeId, &ltree, &rtree, FALSE)))
{
outfunc = typeidOutfunc(ltypeId);
infunc = typeidInfunc(rtypeId);
outstr = (char *) fmgr(outfunc, ((Const *) ltree)->constvalue);
((Const *) ltree)->constvalue = (Datum) fmgr(infunc, outstr, -1);
pfree(outstr);
((Const *) ltree)->consttype = ltypeId = rtypeId;
newtype = typeidType(ltypeId);
((Const *) ltree)->constlen = typeLen(newtype);
((Const *) ltree)->constbyval = typeByVal(newtype);
/* Won't return from oper_inexact() without a candidate... */
}
#endif
temp = oper(opname, ltypeId, rtypeId, false);
opform = (OperatorTupleForm) GETSTRUCT(temp);
opform = (OperatorTupleForm) GETSTRUCT(tup);
left = make_operand(opname, ltree, ltypeId, opform->oprleft);
right = make_operand(opname, rtree, rtypeId, opform->oprright);
}
newop = makeOper(oprid(temp), /* opno */
newop = makeOper(oprid(tup), /* opno */
InvalidOid, /* opid */
opform->oprresult, /* operator result type */
0,
......@@ -304,7 +211,7 @@ printf( "Convert left-hand constant to text for node %d\n", nodeTag(ltree));
result->args = lcons(left, lcons(right, NIL));
return result;
}
} /* make_op() */
Var *
......@@ -538,7 +445,7 @@ make_const(Value *value)
default:
{
if (nodeTag(value) != T_Null)
elog(NOTICE, "unknown type : %d\n", nodeTag(value));
elog(NOTICE, "make_const: unknown type %d\n", nodeTag(value));
/* null const */
con = makeConst(0, 0, (Datum) NULL, true, false, false, false);
......
This diff is collapsed.
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.13 1998/05/21 03:53:51 scrappy Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.14 1998/05/29 14:00:23 thomas Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -24,15 +24,11 @@
#include "parser/parse_node.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parse_coerce.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
extern
bool can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids);
extern
Node *coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId);
static List *expandAllTables(ParseState *pstate);
static char *figureColname(Node *expr, Node *resval);
......@@ -46,11 +42,6 @@ size_target_expr(ParseState *pstate,
Node *expr,
Oid attrtype,
int16 attrtypmod);
Node *
coerce_target_expr(ParseState *pstate,
Node *expr,
Oid type_id,
Oid attrtype);
/*
......@@ -357,7 +348,7 @@ transformTargetList(ParseState *pstate, List *targetlist)
}
return p_target;
}
} /* transformTargetList() */
Node *
......
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.9 1998/05/09 23:29:54 thomas Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.10 1998/05/29 14:00:24 thomas Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -264,27 +264,3 @@ typeidInfunc(Oid type_id)
infunc = type->typinput;
return (infunc);
}
#ifdef NOT_USED
char
FindDelimiter(char *typename)
{
char delim;
HeapTuple typeTuple;
TypeTupleForm type;
if (!(typeTuple = SearchSysCacheTuple(TYPNAME,
PointerGetDatum(typename),
0, 0, 0)))
{
elog(ERROR, "type name lookup of %s failed", typename);
}
type = (TypeTupleForm) GETSTRUCT(typeTuple);
delim = type->typdelim;
return (delim);
}
#endif
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