Previous patch to mark UNION outputs with common typmod (if any) breaks
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 5 Mar 2002 05:10:24 +0000 (05:10 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 5 Mar 2002 05:10:24 +0000 (05:10 +0000)
three-or-more-way UNIONs, as per example from Josh Berkus.  Cause is a
fragile assumption that one tlist's entries will exactly match another.
Restructure code to make that assumption a little less fragile.

src/backend/optimizer/prep/prepunion.c

index 9b7ea633b6817282069a28c792769d821aecbd05..64d8b78f066b5b9e89414bd321c933a14237eb00 100644 (file)
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.70 2002/03/01 06:01:20 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.71 2002/03/05 05:10:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -63,7 +63,9 @@ static List *generate_setop_tlist(List *colTypes, int flag,
                     bool hack_constants,
                     List *input_tlist,
                     List *refnames_tlist);
-static void merge_tlist_typmods(List *tlist, List *planlist);
+static List *generate_append_tlist(List *colTypes, bool flag,
+                    List *input_plans,
+                    List *refnames_tlist);
 static bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK);
 static Node *adjust_inherited_attrs_mutator(Node *node,
                               adjust_inherited_attrs_context *context);
@@ -169,13 +171,11 @@ recurse_set_operations(Node *setOp, Query *parse,
         *
         * XXX you don't really want to know about this: setrefs.c will apply
         * replace_vars_with_subplan_refs() to the Result node's tlist.
-        * This would fail if the input plan's non-resjunk tlist entries
-        * were not all simple Vars equal() to the referencing Vars
-        * generated by generate_setop_tlist().  However, since the input
-        * plan was generated by generate_union_plan() or
-        * generate_nonunion_plan(), the referencing Vars will equal the
-        * tlist entries they reference. Ugly but I don't feel like making
-        * that code more general right now.
+        * This would fail if the Vars generated by generate_setop_tlist()
+        * were not exactly equal() to the corresponding tlist entries of
+        * the subplan.  However, since the subplan was generated by
+        * generate_union_plan() or generate_nonunion_plan(), and hence its
+        * tlist was generated by generate_append_tlist(), this will work.
         */
        if (flag >= 0 ||
            !tlist_same_datatypes(plan->targetlist, colTypes, junkOK))
@@ -226,10 +226,8 @@ generate_union_plan(SetOperationStmt *op, Query *parse,
     * concerned, but we must make it look real anyway for the benefit of
     * the next plan level up.
     */
-   tlist = generate_setop_tlist(op->colTypes, -1, false,
-                                ((Plan *) lfirst(planlist))->targetlist,
-                                refnames_tlist);
-   merge_tlist_typmods(tlist, planlist);
+   tlist = generate_append_tlist(op->colTypes, false,
+                                 planlist, refnames_tlist);
 
    /*
     * Append the child results together.
@@ -285,10 +283,8 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
     * flag column is shown as a variable not a constant, else setrefs.c
     * will get confused.
     */
-   tlist = generate_setop_tlist(op->colTypes, 2, false,
-                                lplan->targetlist,
-                                refnames_tlist);
-   merge_tlist_typmods(tlist, planlist);
+   tlist = generate_append_tlist(op->colTypes, true,
+                                 planlist, refnames_tlist);
 
    /*
     * Append the child results together.
@@ -368,8 +364,7 @@ recurse_union_children(Node *setOp, Query *parse,
  * Generate targetlist for a set-operation plan node
  *
  * colTypes: column datatypes for non-junk columns
- * flag: -1 if no flag column needed, 0 or 1 to create a const flag column,
- *      2 to create a variable flag column
+ * flag: -1 if no flag column needed, 0 or 1 to create a const flag column
  * hack_constants: true to copy up constants (see comments in code)
  * input_tlist: targetlist of this node's input node
  * refnames_tlist: targetlist to take column names from
@@ -450,26 +445,14 @@ generate_setop_tlist(List *colTypes, int flag,
                            -1,
                            pstrdup("flag"),
                            true);
-       if (flag <= 1)
-       {
-           /* flag value is the given constant */
-           expr = (Node *) makeConst(INT4OID,
-                                     sizeof(int4),
-                                     Int32GetDatum(flag),
-                                     false,
-                                     true,
-                                     false,
-                                     false);
-       }
-       else
-       {
-           /* flag value is being copied up from subplan */
-           expr = (Node *) makeVar(0,
-                                   resdom->resno,
-                                   INT4OID,
-                                   -1,
-                                   0);
-       }
+       /* flag value is the given constant */
+       expr = (Node *) makeConst(INT4OID,
+                                 sizeof(int4),
+                                 Int32GetDatum(flag),
+                                 false,
+                                 true,
+                                 false,
+                                 false);
        tlist = lappend(tlist, makeTargetEntry(resdom, expr));
    }
 
@@ -477,44 +460,117 @@ generate_setop_tlist(List *colTypes, int flag,
 }
 
 /*
- * Merge typmods of a list of set-operation subplans.
+ * Generate targetlist for a set-operation Append node
+ *
+ * colTypes: column datatypes for non-junk columns
+ * flag: true to create a flag column copied up from subplans
+ * input_plans: list of sub-plans of the Append
+ * refnames_tlist: targetlist to take column names from
  *
- * If the inputs all agree on type and typmod of a particular column,
- * use that typmod; else use -1.  We assume the result tlist has been
- * initialized with the types and typmods of the first input subplan.
+ * The entries in the Append's targetlist should always be simple Vars;
+ * we just have to make sure they have the right datatypes and typmods.
  */
-static void
-merge_tlist_typmods(List *tlist, List *planlist)
+static List *
+generate_append_tlist(List *colTypes, bool flag,
+                     List *input_plans,
+                     List *refnames_tlist)
 {
+   List       *tlist = NIL;
+   int         resno = 1;
+   List       *curColType;
+   int         colindex;
+   Resdom     *resdom;
+   Node       *expr;
    List       *planl;
+   int32      *colTypmods;
+
+   /*
+    * First extract typmods to use.
+    *
+    * If the inputs all agree on type and typmod of a particular column,
+    * use that typmod; else use -1.
+    */
+   colTypmods = (int32 *) palloc(length(colTypes) * sizeof(int32));
 
-   foreach(planl, planlist)
+   foreach(planl, input_plans)
    {
        Plan   *subplan = (Plan *) lfirst(planl);
-       List   *subtlist = subplan->targetlist;
-       List   *restlist;
+       List   *subtlist;
 
-       foreach(restlist, tlist)
+       curColType = colTypes;
+       colindex = 0;
+       foreach(subtlist, subplan->targetlist)
        {
-           TargetEntry *restle = (TargetEntry *) lfirst(restlist);
-           TargetEntry *subtle;
+           TargetEntry *subtle = (TargetEntry *) lfirst(subtlist);
 
-           if (restle->resdom->resjunk)
+           if (subtle->resdom->resjunk)
                continue;
-           Assert(subtlist != NIL);
-           subtle = (TargetEntry *) lfirst(subtlist);
-           while (subtle->resdom->resjunk)
+           Assert(curColType != NIL);
+           if (subtle->resdom->restype == (Oid) lfirsti(curColType))
            {
-               subtlist = lnext(subtlist);
-               Assert(subtlist != NIL);
-               subtle = (TargetEntry *) lfirst(subtlist);
+               /* If first subplan, copy the typmod; else compare */
+               if (planl == input_plans)
+                   colTypmods[colindex] = subtle->resdom->restypmod;
+               else if (subtle->resdom->restypmod != colTypmods[colindex])
+                   colTypmods[colindex] = -1;
            }
-           if (restle->resdom->restype != subtle->resdom->restype ||
-               restle->resdom->restypmod != subtle->resdom->restypmod)
-               restle->resdom->restypmod = -1;
-           subtlist = lnext(subtlist);
+           else
+           {
+               /* types disagree, so force typmod to -1 */
+               colTypmods[colindex] = -1;
+           }
+           curColType = lnext(curColType);
+           colindex++;
        }
+       Assert(curColType == NIL);
    }
+
+   /*
+    * Now we can build the tlist for the Append.
+    */
+   colindex = 0;
+   foreach(curColType, colTypes)
+   {
+       Oid         colType = (Oid) lfirsti(curColType);
+       int32       colTypmod = colTypmods[colindex++];
+       TargetEntry *reftle = (TargetEntry *) lfirst(refnames_tlist);
+
+       Assert(reftle->resdom->resno == resno);
+       Assert(!reftle->resdom->resjunk);
+       expr = (Node *) makeVar(0,
+                               resno,
+                               colType,
+                               colTypmod,
+                               0);
+       resdom = makeResdom((AttrNumber) resno++,
+                           colType,
+                           colTypmod,
+                           pstrdup(reftle->resdom->resname),
+                           false);
+       tlist = lappend(tlist, makeTargetEntry(resdom, expr));
+       refnames_tlist = lnext(refnames_tlist);
+   }
+
+   if (flag)
+   {
+       /* Add a resjunk flag column */
+       resdom = makeResdom((AttrNumber) resno++,
+                           INT4OID,
+                           -1,
+                           pstrdup("flag"),
+                           true);
+       /* flag value is shown as copied up from subplan */
+       expr = (Node *) makeVar(0,
+                               resdom->resno,
+                               INT4OID,
+                               -1,
+                               0);
+       tlist = lappend(tlist, makeTargetEntry(resdom, expr));
+   }
+
+   pfree(colTypmods);
+
+   return tlist;
 }
 
 /*