]> granicus.if.org Git - postgresql/commitdiff
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;
 }
 
 /*