Commit 94467182 authored by Tom Lane's avatar Tom Lane

Previous patch to mark UNION outputs with common typmod (if any) breaks

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.
parent 17b6baf1
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -63,7 +63,9 @@ static List *generate_setop_tlist(List *colTypes, int flag,
bool hack_constants, bool hack_constants,
List *input_tlist, List *input_tlist,
List *refnames_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 bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK);
static Node *adjust_inherited_attrs_mutator(Node *node, static Node *adjust_inherited_attrs_mutator(Node *node,
adjust_inherited_attrs_context *context); adjust_inherited_attrs_context *context);
...@@ -169,13 +171,11 @@ recurse_set_operations(Node *setOp, Query *parse, ...@@ -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 * 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. * replace_vars_with_subplan_refs() to the Result node's tlist.
* This would fail if the input plan's non-resjunk tlist entries * This would fail if the Vars generated by generate_setop_tlist()
* were not all simple Vars equal() to the referencing Vars * were not exactly equal() to the corresponding tlist entries of
* generated by generate_setop_tlist(). However, since the input * the subplan. However, since the subplan was generated by
* plan was generated by generate_union_plan() or * generate_union_plan() or generate_nonunion_plan(), and hence its
* generate_nonunion_plan(), the referencing Vars will equal the * tlist was generated by generate_append_tlist(), this will work.
* tlist entries they reference. Ugly but I don't feel like making
* that code more general right now.
*/ */
if (flag >= 0 || if (flag >= 0 ||
!tlist_same_datatypes(plan->targetlist, colTypes, junkOK)) !tlist_same_datatypes(plan->targetlist, colTypes, junkOK))
...@@ -226,10 +226,8 @@ generate_union_plan(SetOperationStmt *op, Query *parse, ...@@ -226,10 +226,8 @@ generate_union_plan(SetOperationStmt *op, Query *parse,
* concerned, but we must make it look real anyway for the benefit of * concerned, but we must make it look real anyway for the benefit of
* the next plan level up. * the next plan level up.
*/ */
tlist = generate_setop_tlist(op->colTypes, -1, false, tlist = generate_append_tlist(op->colTypes, false,
((Plan *) lfirst(planlist))->targetlist, planlist, refnames_tlist);
refnames_tlist);
merge_tlist_typmods(tlist, planlist);
/* /*
* Append the child results together. * Append the child results together.
...@@ -285,10 +283,8 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse, ...@@ -285,10 +283,8 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
* flag column is shown as a variable not a constant, else setrefs.c * flag column is shown as a variable not a constant, else setrefs.c
* will get confused. * will get confused.
*/ */
tlist = generate_setop_tlist(op->colTypes, 2, false, tlist = generate_append_tlist(op->colTypes, true,
lplan->targetlist, planlist, refnames_tlist);
refnames_tlist);
merge_tlist_typmods(tlist, planlist);
/* /*
* Append the child results together. * Append the child results together.
...@@ -368,8 +364,7 @@ recurse_union_children(Node *setOp, Query *parse, ...@@ -368,8 +364,7 @@ recurse_union_children(Node *setOp, Query *parse,
* Generate targetlist for a set-operation plan node * Generate targetlist for a set-operation plan node
* *
* colTypes: column datatypes for non-junk columns * colTypes: column datatypes for non-junk columns
* flag: -1 if no flag column needed, 0 or 1 to create a const flag column, * flag: -1 if no flag column needed, 0 or 1 to create a const flag column
* 2 to create a variable flag column
* hack_constants: true to copy up constants (see comments in code) * hack_constants: true to copy up constants (see comments in code)
* input_tlist: targetlist of this node's input node * input_tlist: targetlist of this node's input node
* refnames_tlist: targetlist to take column names from * refnames_tlist: targetlist to take column names from
...@@ -450,8 +445,6 @@ generate_setop_tlist(List *colTypes, int flag, ...@@ -450,8 +445,6 @@ generate_setop_tlist(List *colTypes, int flag,
-1, -1,
pstrdup("flag"), pstrdup("flag"),
true); true);
if (flag <= 1)
{
/* flag value is the given constant */ /* flag value is the given constant */
expr = (Node *) makeConst(INT4OID, expr = (Node *) makeConst(INT4OID,
sizeof(int4), sizeof(int4),
...@@ -460,16 +453,6 @@ generate_setop_tlist(List *colTypes, int flag, ...@@ -460,16 +453,6 @@ generate_setop_tlist(List *colTypes, int flag,
true, true,
false, false,
false); false);
}
else
{
/* flag value is being copied up from subplan */
expr = (Node *) makeVar(0,
resdom->resno,
INT4OID,
-1,
0);
}
tlist = lappend(tlist, makeTargetEntry(resdom, expr)); tlist = lappend(tlist, makeTargetEntry(resdom, expr));
} }
...@@ -477,44 +460,117 @@ generate_setop_tlist(List *colTypes, int flag, ...@@ -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
* *
* If the inputs all agree on type and typmod of a particular column, * colTypes: column datatypes for non-junk columns
* use that typmod; else use -1. We assume the result tlist has been * flag: true to create a flag column copied up from subplans
* initialized with the types and typmods of the first input subplan. * input_plans: list of sub-plans of the Append
* refnames_tlist: targetlist to take column names from
*
* 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 static List *
merge_tlist_typmods(List *tlist, List *planlist) 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; List *planl;
int32 *colTypmods;
foreach(planl, planlist) /*
* 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, input_plans)
{ {
Plan *subplan = (Plan *) lfirst(planl); Plan *subplan = (Plan *) lfirst(planl);
List *subtlist = subplan->targetlist; List *subtlist;
List *restlist;
foreach(restlist, tlist) curColType = colTypes;
colindex = 0;
foreach(subtlist, subplan->targetlist)
{ {
TargetEntry *restle = (TargetEntry *) lfirst(restlist); TargetEntry *subtle = (TargetEntry *) lfirst(subtlist);
TargetEntry *subtle;
if (restle->resdom->resjunk) if (subtle->resdom->resjunk)
continue; continue;
Assert(subtlist != NIL); Assert(curColType != NIL);
subtle = (TargetEntry *) lfirst(subtlist); if (subtle->resdom->restype == (Oid) lfirsti(curColType))
while (subtle->resdom->resjunk) {
/* 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;
}
else
{ {
subtlist = lnext(subtlist); /* types disagree, so force typmod to -1 */
Assert(subtlist != NIL); colTypmods[colindex] = -1;
subtle = (TargetEntry *) lfirst(subtlist);
} }
if (restle->resdom->restype != subtle->resdom->restype || curColType = lnext(curColType);
restle->resdom->restypmod != subtle->resdom->restypmod) colindex++;
restle->resdom->restypmod = -1;
subtlist = lnext(subtlist);
} }
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;
} }
/* /*
......
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