Commit 0ee26100 authored by Tom Lane's avatar Tom Lane

Fix UNION/INTERSECT/EXCEPT so that when two inputs being merged have

same data type and same typmod, we show that typmod as the output
typmod, rather than generic -1.  This responds to several complaints
over the past few years about UNIONs unexpectedly dropping length or
precision info.
parent e860e746
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.345 2006/08/02 01:59:45 joe Exp $ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.346 2006/08/10 02:36:28 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1793,6 +1793,7 @@ _copySetOperationStmt(SetOperationStmt *from) ...@@ -1793,6 +1793,7 @@ _copySetOperationStmt(SetOperationStmt *from)
COPY_NODE_FIELD(larg); COPY_NODE_FIELD(larg);
COPY_NODE_FIELD(rarg); COPY_NODE_FIELD(rarg);
COPY_NODE_FIELD(colTypes); COPY_NODE_FIELD(colTypes);
COPY_NODE_FIELD(colTypmods);
return newnode; return newnode;
} }
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.279 2006/08/02 01:59:45 joe Exp $ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.280 2006/08/10 02:36:28 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -743,6 +743,7 @@ _equalSetOperationStmt(SetOperationStmt *a, SetOperationStmt *b) ...@@ -743,6 +743,7 @@ _equalSetOperationStmt(SetOperationStmt *a, SetOperationStmt *b)
COMPARE_NODE_FIELD(larg); COMPARE_NODE_FIELD(larg);
COMPARE_NODE_FIELD(rarg); COMPARE_NODE_FIELD(rarg);
COMPARE_NODE_FIELD(colTypes); COMPARE_NODE_FIELD(colTypes);
COMPARE_NODE_FIELD(colTypmods);
return true; return true;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.280 2006/08/02 01:59:45 joe Exp $ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.281 2006/08/10 02:36:28 tgl Exp $
* *
* NOTES * NOTES
* Every node type that can appear in stored rules' parsetrees *must* * Every node type that can appear in stored rules' parsetrees *must*
...@@ -1574,6 +1574,7 @@ _outSetOperationStmt(StringInfo str, SetOperationStmt *node) ...@@ -1574,6 +1574,7 @@ _outSetOperationStmt(StringInfo str, SetOperationStmt *node)
WRITE_NODE_FIELD(larg); WRITE_NODE_FIELD(larg);
WRITE_NODE_FIELD(rarg); WRITE_NODE_FIELD(rarg);
WRITE_NODE_FIELD(colTypes); WRITE_NODE_FIELD(colTypes);
WRITE_NODE_FIELD(colTypmods);
} }
static void static void
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.193 2006/08/02 01:59:45 joe Exp $ * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.194 2006/08/10 02:36:28 tgl Exp $
* *
* NOTES * NOTES
* Path and Plan nodes do not have any readfuncs support, because we * Path and Plan nodes do not have any readfuncs support, because we
...@@ -245,6 +245,7 @@ _readSetOperationStmt(void) ...@@ -245,6 +245,7 @@ _readSetOperationStmt(void)
READ_NODE_FIELD(larg); READ_NODE_FIELD(larg);
READ_NODE_FIELD(rarg); READ_NODE_FIELD(rarg);
READ_NODE_FIELD(colTypes); READ_NODE_FIELD(colTypes);
READ_NODE_FIELD(colTypmods);
READ_DONE(); READ_DONE();
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.150 2006/08/02 01:59:45 joe Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.151 2006/08/10 02:36:28 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -813,6 +813,10 @@ recurse_pushdown_safe(Node *setOp, Query *topquery, ...@@ -813,6 +813,10 @@ recurse_pushdown_safe(Node *setOp, Query *topquery,
* Compare tlist's datatypes against the list of set-operation result types. * Compare tlist's datatypes against the list of set-operation result types.
* For any items that are different, mark the appropriate element of * For any items that are different, mark the appropriate element of
* differentTypes[] to show that this column will have type conversions. * differentTypes[] to show that this column will have type conversions.
*
* We don't have to care about typmods here: the only allowed difference
* between set-op input and output typmods is input is a specific typmod
* and output is -1, and that does not require a coercion.
*/ */
static void static void
compare_tlist_datatypes(List *tlist, List *colTypes, compare_tlist_datatypes(List *tlist, List *colTypes,
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.39 2006/07/14 14:52:21 momjian Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.40 2006/08/10 02:36:28 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -716,6 +716,7 @@ is_simple_union_all_recurse(Node *setOp, Query *setOpQuery, List *colTypes) ...@@ -716,6 +716,7 @@ is_simple_union_all_recurse(Node *setOp, Query *setOpQuery, List *colTypes)
Assert(subquery != NULL); Assert(subquery != NULL);
/* Leaf nodes are OK if they match the toplevel column types */ /* Leaf nodes are OK if they match the toplevel column types */
/* We don't have to compare typmods here */
return tlist_same_datatypes(subquery->targetList, colTypes, true); return tlist_same_datatypes(subquery->targetList, colTypes, true);
} }
else if (IsA(setOp, SetOperationStmt)) else if (IsA(setOp, SetOperationStmt))
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.132 2006/04/30 18:30:39 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.133 2006/08/10 02:36:28 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -152,6 +152,10 @@ plan_set_operations(PlannerInfo *root, double tuple_fraction, ...@@ -152,6 +152,10 @@ plan_set_operations(PlannerInfo *root, double tuple_fraction,
* flag: if >= 0, add a resjunk output column indicating value of flag * flag: if >= 0, add a resjunk output column indicating value of flag
* refnames_tlist: targetlist to take column names from * refnames_tlist: targetlist to take column names from
* *sortClauses: receives list of SortClauses for result plan, if any * *sortClauses: receives list of SortClauses for result plan, if any
*
* We don't have to care about typmods here: the only allowed difference
* between set-op input and output typmods is input is a specific typmod
* and output is -1, and that does not require a coercion.
*/ */
static Plan * static Plan *
recurse_set_operations(Node *setOp, PlannerInfo *root, recurse_set_operations(Node *setOp, PlannerInfo *root,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/tlist.c,v 1.72 2006/03/05 15:58:32 momjian Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/util/tlist.c,v 1.73 2006/08/10 02:36:29 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -174,6 +174,8 @@ get_sortgrouplist_exprs(List *sortClauses, List *targetList) ...@@ -174,6 +174,8 @@ get_sortgrouplist_exprs(List *sortClauses, List *targetList)
* *
* Resjunk columns are ignored if junkOK is true; otherwise presence of * Resjunk columns are ignored if junkOK is true; otherwise presence of
* a resjunk column will always cause a 'false' result. * a resjunk column will always cause a 'false' result.
*
* Note: currently no callers care about comparing typmods.
*/ */
bool bool
tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK) tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.343 2006/08/02 14:14:22 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.344 2006/08/10 02:36:29 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -131,7 +131,8 @@ static void transformFKConstraints(ParseState *pstate, ...@@ -131,7 +131,8 @@ static void transformFKConstraints(ParseState *pstate,
bool skipValidation, bool skipValidation,
bool isAddConstraint); bool isAddConstraint);
static void applyColumnNames(List *dst, List *src); static void applyColumnNames(List *dst, List *src);
static List *getSetColTypes(ParseState *pstate, Node *node); static void getSetColTypes(ParseState *pstate, Node *node,
List **colTypes, List **colTypmods);
static void transformLockingClause(Query *qry, LockingClause *lc); static void transformLockingClause(Query *qry, LockingClause *lc);
static void transformConstraintAttrs(List *constraintList); static void transformConstraintAttrs(List *constraintList);
static void transformColumnType(ParseState *pstate, ColumnDef *column); static void transformColumnType(ParseState *pstate, ColumnDef *column);
...@@ -2312,7 +2313,8 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) ...@@ -2312,7 +2313,8 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
List *lockingClause; List *lockingClause;
Node *node; Node *node;
ListCell *left_tlist, ListCell *left_tlist,
*dtlist, *lct,
*lcm,
*l; *l;
List *targetvars, List *targetvars,
*targetnames, *targetnames,
...@@ -2395,9 +2397,10 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) ...@@ -2395,9 +2397,10 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
targetnames = NIL; targetnames = NIL;
left_tlist = list_head(leftmostQuery->targetList); left_tlist = list_head(leftmostQuery->targetList);
foreach(dtlist, sostmt->colTypes) forboth(lct, sostmt->colTypes, lcm, sostmt->colTypmods)
{ {
Oid colType = lfirst_oid(dtlist); Oid colType = lfirst_oid(lct);
int32 colTypmod = lfirst_int(lcm);
TargetEntry *lefttle = (TargetEntry *) lfirst(left_tlist); TargetEntry *lefttle = (TargetEntry *) lfirst(left_tlist);
char *colName; char *colName;
TargetEntry *tle; TargetEntry *tle;
...@@ -2408,7 +2411,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) ...@@ -2408,7 +2411,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
expr = (Expr *) makeVar(leftmostRTI, expr = (Expr *) makeVar(leftmostRTI,
lefttle->resno, lefttle->resno,
colType, colType,
-1, colTypmod,
0); 0);
tle = makeTargetEntry(expr, tle = makeTargetEntry(expr,
(AttrNumber) pstate->p_next_resno++, (AttrNumber) pstate->p_next_resno++,
...@@ -2609,8 +2612,12 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt) ...@@ -2609,8 +2612,12 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
SetOperationStmt *op = makeNode(SetOperationStmt); SetOperationStmt *op = makeNode(SetOperationStmt);
List *lcoltypes; List *lcoltypes;
List *rcoltypes; List *rcoltypes;
ListCell *l; List *lcoltypmods;
ListCell *r; List *rcoltypmods;
ListCell *lct;
ListCell *rct;
ListCell *lcm;
ListCell *rcm;
const char *context; const char *context;
context = (stmt->op == SETOP_UNION ? "UNION" : context = (stmt->op == SETOP_UNION ? "UNION" :
...@@ -2630,24 +2637,43 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt) ...@@ -2630,24 +2637,43 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
* Verify that the two children have the same number of non-junk * Verify that the two children have the same number of non-junk
* columns, and determine the types of the merged output columns. * columns, and determine the types of the merged output columns.
*/ */
lcoltypes = getSetColTypes(pstate, op->larg); getSetColTypes(pstate, op->larg, &lcoltypes, &lcoltypmods);
rcoltypes = getSetColTypes(pstate, op->rarg); getSetColTypes(pstate, op->rarg, &rcoltypes, &rcoltypmods);
if (list_length(lcoltypes) != list_length(rcoltypes)) if (list_length(lcoltypes) != list_length(rcoltypes))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("each %s query must have the same number of columns", errmsg("each %s query must have the same number of columns",
context))); context)));
Assert(list_length(lcoltypes) == list_length(lcoltypmods));
Assert(list_length(rcoltypes) == list_length(rcoltypmods));
op->colTypes = NIL; op->colTypes = NIL;
forboth(l, lcoltypes, r, rcoltypes) op->colTypmods = NIL;
/* don't have a "foreach4", so chase two of the lists by hand */
lcm = list_head(lcoltypmods);
rcm = list_head(rcoltypmods);
forboth(lct, lcoltypes, rct, rcoltypes)
{ {
Oid lcoltype = lfirst_oid(l); Oid lcoltype = lfirst_oid(lct);
Oid rcoltype = lfirst_oid(r); Oid rcoltype = lfirst_oid(rct);
int32 lcoltypmod = lfirst_int(lcm);
int32 rcoltypmod = lfirst_int(rcm);
Oid rescoltype; Oid rescoltype;
int32 rescoltypmod;
/* select common type, same as CASE et al */
rescoltype = select_common_type(list_make2_oid(lcoltype, rcoltype), rescoltype = select_common_type(list_make2_oid(lcoltype, rcoltype),
context); context);
/* if same type and same typmod, use typmod; else default */
if (lcoltype == rcoltype && lcoltypmod == rcoltypmod)
rescoltypmod = lcoltypmod;
else
rescoltypmod = -1;
op->colTypes = lappend_oid(op->colTypes, rescoltype); op->colTypes = lappend_oid(op->colTypes, rescoltype);
op->colTypmods = lappend_int(op->colTypmods, rescoltypmod);
lcm = lnext(lcm);
rcm = lnext(rcm);
} }
return (Node *) op; return (Node *) op;
...@@ -2656,17 +2682,19 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt) ...@@ -2656,17 +2682,19 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
/* /*
* getSetColTypes * getSetColTypes
* Get output column types of an (already transformed) set-op node * Get output column types/typmods of an (already transformed) set-op node
*/ */
static List * static void
getSetColTypes(ParseState *pstate, Node *node) getSetColTypes(ParseState *pstate, Node *node,
List **colTypes, List **colTypmods)
{ {
*colTypes = NIL;
*colTypmods = NIL;
if (IsA(node, RangeTblRef)) if (IsA(node, RangeTblRef))
{ {
RangeTblRef *rtr = (RangeTblRef *) node; RangeTblRef *rtr = (RangeTblRef *) node;
RangeTblEntry *rte = rt_fetch(rtr->rtindex, pstate->p_rtable); RangeTblEntry *rte = rt_fetch(rtr->rtindex, pstate->p_rtable);
Query *selectQuery = rte->subquery; Query *selectQuery = rte->subquery;
List *result = NIL;
ListCell *tl; ListCell *tl;
Assert(selectQuery != NULL); Assert(selectQuery != NULL);
...@@ -2677,9 +2705,11 @@ getSetColTypes(ParseState *pstate, Node *node) ...@@ -2677,9 +2705,11 @@ getSetColTypes(ParseState *pstate, Node *node)
if (tle->resjunk) if (tle->resjunk)
continue; continue;
result = lappend_oid(result, exprType((Node *) tle->expr)); *colTypes = lappend_oid(*colTypes,
exprType((Node *) tle->expr));
*colTypmods = lappend_int(*colTypmods,
exprTypmod((Node *) tle->expr));
} }
return result;
} }
else if (IsA(node, SetOperationStmt)) else if (IsA(node, SetOperationStmt))
{ {
...@@ -2687,13 +2717,11 @@ getSetColTypes(ParseState *pstate, Node *node) ...@@ -2687,13 +2717,11 @@ getSetColTypes(ParseState *pstate, Node *node)
/* Result already computed during transformation of node */ /* Result already computed during transformation of node */
Assert(op->colTypes != NIL); Assert(op->colTypes != NIL);
return op->colTypes; *colTypes = op->colTypes;
*colTypmods = op->colTypmods;
} }
else else
{
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
return NIL; /* keep compiler quiet */
}
} }
/* Attach column names from a ColumnDef list to a TargetEntry list */ /* Attach column names from a ColumnDef list to a TargetEntry list */
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.347 2006/08/06 03:53:44 tgl Exp $ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.348 2006/08/10 02:36:29 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200608051 #define CATALOG_VERSION_NO 200608091
#endif #endif
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.320 2006/08/02 01:59:47 joe Exp $ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.321 2006/08/10 02:36:29 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -769,7 +769,8 @@ typedef struct SetOperationStmt ...@@ -769,7 +769,8 @@ typedef struct SetOperationStmt
/* Eventually add fields for CORRESPONDING spec here */ /* Eventually add fields for CORRESPONDING spec here */
/* Fields derived during parse analysis: */ /* Fields derived during parse analysis: */
List *colTypes; /* list of OIDs of output column types */ List *colTypes; /* OID list of output column type OIDs */
List *colTypmods; /* integer list of output column typmods */
} SetOperationStmt; } SetOperationStmt;
......
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