OSDN Git Service

Fix UNION/INTERSECT/EXCEPT so that when two inputs being merged have
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 10 Aug 2006 02:36:29 +0000 (02:36 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 10 Aug 2006 02:36:29 +0000 (02:36 +0000)
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.

src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/prep/prepjointree.c
src/backend/optimizer/prep/prepunion.c
src/backend/optimizer/util/tlist.c
src/backend/parser/analyze.c
src/include/catalog/catversion.h
src/include/nodes/parsenodes.h

index 8903d6b..77ccd64 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * 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)
        COPY_NODE_FIELD(larg);
        COPY_NODE_FIELD(rarg);
        COPY_NODE_FIELD(colTypes);
+       COPY_NODE_FIELD(colTypmods);
 
        return newnode;
 }
index d49e02b..4b749e0 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * 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)
        COMPARE_NODE_FIELD(larg);
        COMPARE_NODE_FIELD(rarg);
        COMPARE_NODE_FIELD(colTypes);
+       COMPARE_NODE_FIELD(colTypmods);
 
        return true;
 }
index 34555e1..39ac8e4 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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
  *       Every node type that can appear in stored rules' parsetrees *must*
@@ -1574,6 +1574,7 @@ _outSetOperationStmt(StringInfo str, SetOperationStmt *node)
        WRITE_NODE_FIELD(larg);
        WRITE_NODE_FIELD(rarg);
        WRITE_NODE_FIELD(colTypes);
+       WRITE_NODE_FIELD(colTypmods);
 }
 
 static void
index 7459acb..80fa88e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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
  *       Path and Plan nodes do not have any readfuncs support, because we
@@ -245,6 +245,7 @@ _readSetOperationStmt(void)
        READ_NODE_FIELD(larg);
        READ_NODE_FIELD(rarg);
        READ_NODE_FIELD(colTypes);
+       READ_NODE_FIELD(colTypmods);
 
        READ_DONE();
 }
index 1d7e20c..fc618f7 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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,
  * Compare tlist's datatypes against the list of set-operation result types.
  * For any items that are different, mark the appropriate element of
  * 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
 compare_tlist_datatypes(List *tlist, List *colTypes,
index 856181d..2fe7847 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * 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)
                Assert(subquery != NULL);
 
                /* 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);
        }
        else if (IsA(setOp, SetOperationStmt))
index 3b9e740..3bf7223 100644 (file)
@@ -22,7 +22,7 @@
  *
  *
  * 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,
  * flag: if >= 0, add a resjunk output column indicating value of flag
  * refnames_tlist: targetlist to take column names from
  * *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 *
 recurse_set_operations(Node *setOp, PlannerInfo *root,
index c75c496..74ebb3f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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)
  *
  * Resjunk columns are ignored if junkOK is true; otherwise presence of
  * a resjunk column will always cause a 'false' result.
+ *
+ * Note: currently no callers care about comparing typmods.
  */
 bool
 tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
index 4f00d98..6921e1d 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * 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,
                                           bool skipValidation,
                                           bool isAddConstraint);
 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 transformConstraintAttrs(List *constraintList);
 static void transformColumnType(ParseState *pstate, ColumnDef *column);
@@ -2312,7 +2313,8 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
        List       *lockingClause;
        Node       *node;
        ListCell   *left_tlist,
-                          *dtlist,
+                          *lct,
+                          *lcm,
                           *l;
        List       *targetvars,
                           *targetnames,
@@ -2395,9 +2397,10 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
        targetnames = NIL;
        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);
                char       *colName;
                TargetEntry *tle;
@@ -2408,7 +2411,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
                expr = (Expr *) makeVar(leftmostRTI,
                                                                lefttle->resno,
                                                                colType,
-                                                               -1,
+                                                               colTypmod,
                                                                0);
                tle = makeTargetEntry(expr,
                                                          (AttrNumber) pstate->p_next_resno++,
@@ -2609,8 +2612,12 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
                SetOperationStmt *op = makeNode(SetOperationStmt);
                List       *lcoltypes;
                List       *rcoltypes;
-               ListCell   *l;
-               ListCell   *r;
+               List       *lcoltypmods;
+               List       *rcoltypmods;
+               ListCell   *lct;
+               ListCell   *rct;
+               ListCell   *lcm;
+               ListCell   *rcm;
                const char *context;
 
                context = (stmt->op == SETOP_UNION ? "UNION" :
@@ -2630,24 +2637,43 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
                 * Verify that the two children have the same number of non-junk
                 * columns, and determine the types of the merged output columns.
                 */
-               lcoltypes = getSetColTypes(pstate, op->larg);
-               rcoltypes = getSetColTypes(pstate, op->rarg);
+               getSetColTypes(pstate, op->larg, &lcoltypes, &lcoltypmods);
+               getSetColTypes(pstate, op->rarg, &rcoltypes, &rcoltypmods);
                if (list_length(lcoltypes) != list_length(rcoltypes))
                        ereport(ERROR,
                                        (errcode(ERRCODE_SYNTAX_ERROR),
                                 errmsg("each %s query must have the same number of columns",
                                                context)));
+               Assert(list_length(lcoltypes) == list_length(lcoltypmods));
+               Assert(list_length(rcoltypes) == list_length(rcoltypmods));
 
                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                     rcoltype = lfirst_oid(r);
+                       Oid                     lcoltype = lfirst_oid(lct);
+                       Oid                     rcoltype = lfirst_oid(rct);
+                       int32           lcoltypmod = lfirst_int(lcm);
+                       int32           rcoltypmod = lfirst_int(rcm);
                        Oid                     rescoltype;
+                       int32           rescoltypmod;
 
+                       /* select common type, same as CASE et al */
                        rescoltype = select_common_type(list_make2_oid(lcoltype, rcoltype),
                                                                                        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->colTypmods = lappend_int(op->colTypmods, rescoltypmod);
+
+                       lcm = lnext(lcm);
+                       rcm = lnext(rcm);
                }
 
                return (Node *) op;
@@ -2656,17 +2682,19 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
 
 /*
  * 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 *
-getSetColTypes(ParseState *pstate, Node *node)
+static void
+getSetColTypes(ParseState *pstate, Node *node,
+                          List **colTypes, List **colTypmods)
 {
+       *colTypes = NIL;
+       *colTypmods = NIL;
        if (IsA(node, RangeTblRef))
        {
                RangeTblRef *rtr = (RangeTblRef *) node;
                RangeTblEntry *rte = rt_fetch(rtr->rtindex, pstate->p_rtable);
                Query      *selectQuery = rte->subquery;
-               List       *result = NIL;
                ListCell   *tl;
 
                Assert(selectQuery != NULL);
@@ -2677,9 +2705,11 @@ getSetColTypes(ParseState *pstate, Node *node)
 
                        if (tle->resjunk)
                                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))
        {
@@ -2687,13 +2717,11 @@ getSetColTypes(ParseState *pstate, Node *node)
 
                /* Result already computed during transformation of node */
                Assert(op->colTypes != NIL);
-               return op->colTypes;
+               *colTypes = op->colTypes;
+               *colTypmods = op->colTypmods;
        }
        else
-       {
                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 */
index dbf0395..b97c3d0 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * 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 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200608051
+#define CATALOG_VERSION_NO     200608091
 
 #endif
index d0fa16f..e2567ff 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * 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
        /* Eventually add fields for CORRESPONDING spec here */
 
        /* 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;