mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
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.
This commit is contained in:
@ -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,
|
|||||||
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,
|
|||||||
*
|
*
|
||||||
* 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,
|
|||||||
* 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,
|
|||||||
* 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,
|
|||||||
* 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,26 +445,14 @@ generate_setop_tlist(List *colTypes, int flag,
|
|||||||
-1,
|
-1,
|
||||||
pstrdup("flag"),
|
pstrdup("flag"),
|
||||||
true);
|
true);
|
||||||
if (flag <= 1)
|
/* flag value is the given constant */
|
||||||
{
|
expr = (Node *) makeConst(INT4OID,
|
||||||
/* flag value is the given constant */
|
sizeof(int4),
|
||||||
expr = (Node *) makeConst(INT4OID,
|
Int32GetDatum(flag),
|
||||||
sizeof(int4),
|
false,
|
||||||
Int32GetDatum(flag),
|
true,
|
||||||
false,
|
false,
|
||||||
true,
|
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,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
subtlist = lnext(subtlist);
|
/* If first subplan, copy the typmod; else compare */
|
||||||
Assert(subtlist != NIL);
|
if (planl == input_plans)
|
||||||
subtle = (TargetEntry *) lfirst(subtlist);
|
colTypmods[colindex] = subtle->resdom->restypmod;
|
||||||
|
else if (subtle->resdom->restypmod != colTypmods[colindex])
|
||||||
|
colTypmods[colindex] = -1;
|
||||||
}
|
}
|
||||||
if (restle->resdom->restype != subtle->resdom->restype ||
|
else
|
||||||
restle->resdom->restypmod != subtle->resdom->restypmod)
|
{
|
||||||
restle->resdom->restypmod = -1;
|
/* types disagree, so force typmod to -1 */
|
||||||
subtlist = lnext(subtlist);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Reference in New Issue
Block a user