create_unique_plan() should not discard existing output columns of the

subplan it starts with, as they may be needed at upper join levels.
See comments added to code for the non-obvious reason why.  Per bug report
from Robert Creager.
This commit is contained in:
Tom Lane 2003-08-07 19:20:24 +00:00
parent d862045dfc
commit ecbed6e1b9
5 changed files with 94 additions and 37 deletions

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.151 2003/08/04 02:40:00 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.152 2003/08/07 19:20:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -504,52 +504,87 @@ create_unique_plan(Query *root, UniquePath * best_path)
{
Plan *plan;
Plan *subplan;
List *sub_targetlist;
List *uniq_exprs;
int numGroupCols;
AttrNumber *groupColIdx;
int groupColPos;
List *newtlist;
int nextresno;
bool newitems;
List *my_tlist;
List *l;
subplan = create_plan(root, best_path->subpath);
/*
* If the subplan came from an IN subselect (currently always the
* case), we need to instantiate the correct output targetlist for the
* subselect, rather than using the flattened tlist.
* As constructed, the subplan has a "flat" tlist containing just the
* Vars needed here and at upper levels. The values we are supposed
* to unique-ify may be expressions in these variables. We have to
* add any such expressions to the subplan's tlist. We then build
* control information showing which subplan output columns are to be
* examined by the grouping step. (Since we do not remove any existing
* subplan outputs, not all the output columns may be used for grouping.)
*
* Note: the reason we don't remove any subplan outputs is that there
* are scenarios where a Var is needed at higher levels even though it
* is not one of the nominal outputs of an IN clause. Consider
* WHERE x IN (SELECT y FROM t1,t2 WHERE y = z)
* Implied equality deduction will generate an "x = z" clause, which may
* get used instead of "x = y" in the upper join step. Therefore the
* sub-select had better deliver both y and z in its targetlist. It is
* sufficient to unique-ify on y, however.
*
* To find the correct list of values to unique-ify, we look in the
* information saved for IN expressions. If this code is ever used in
* other scenarios, some other way of finding what to unique-ify will
* be needed.
*/
sub_targetlist = NIL;
uniq_exprs = NIL; /* just to keep compiler quiet */
foreach(l, root->in_info_list)
{
InClauseInfo *ininfo = (InClauseInfo *) lfirst(l);
if (bms_equal(ininfo->righthand, best_path->path.parent->relids))
{
sub_targetlist = ininfo->sub_targetlist;
uniq_exprs = ininfo->sub_targetlist;
break;
}
}
if (l == NIL) /* fell out of loop? */
elog(ERROR, "could not find UniquePath in in_info_list");
if (sub_targetlist)
/* set up to record positions of unique columns */
numGroupCols = length(uniq_exprs);
groupColIdx = (AttrNumber *) palloc(numGroupCols * sizeof(AttrNumber));
groupColPos = 0;
/* not sure if tlist might be shared with other nodes, so copy */
newtlist = copyObject(subplan->targetlist);
nextresno = length(newtlist) + 1;
newitems = false;
foreach(l, uniq_exprs)
{
/*
* Transform list of plain Vars into targetlist
*/
List *newtlist = NIL;
int resno = 1;
Node *uniqexpr = lfirst(l);
TargetEntry *tle;
foreach(l, sub_targetlist)
tle = tlistentry_member(uniqexpr, newtlist);
if (!tle)
{
Node *tlexpr = lfirst(l);
TargetEntry *tle;
tle = makeTargetEntry(makeResdom(resno,
exprType(tlexpr),
exprTypmod(tlexpr),
tle = makeTargetEntry(makeResdom(nextresno,
exprType(uniqexpr),
exprTypmod(uniqexpr),
NULL,
false),
(Expr *) tlexpr);
(Expr *) uniqexpr);
newtlist = lappend(newtlist, tle);
resno++;
nextresno++;
newitems = true;
}
groupColIdx[groupColPos++] = tle->resdom->resno;
}
if (newitems)
{
/*
* If the top plan node can't do projections, we need to add a
* Result node to help it along.
@ -563,21 +598,15 @@ create_unique_plan(Query *root, UniquePath * best_path)
subplan->targetlist = newtlist;
}
/* Copy tlist again to make one we can put sorting labels on */
my_tlist = copyObject(subplan->targetlist);
if (best_path->use_hash)
{
int numGroupCols = length(my_tlist);
long numGroups;
AttrNumber *groupColIdx;
int i;
numGroups = (long) Min(best_path->rows, (double) LONG_MAX);
groupColIdx = (AttrNumber *) palloc(numGroupCols * sizeof(AttrNumber));
for (i = 0; i < numGroupCols; i++)
groupColIdx[i] = i + 1;
plan = (Plan *) make_agg(root,
my_tlist,
NIL,
@ -590,9 +619,17 @@ create_unique_plan(Query *root, UniquePath * best_path)
}
else
{
List *sortList;
List *sortList = NIL;
sortList = addAllTargetsToSortList(NULL, NIL, my_tlist, false);
for (groupColPos = 0; groupColPos < numGroupCols; groupColPos++)
{
TargetEntry *tle;
tle = nth(groupColIdx[groupColPos] - 1, my_tlist);
Assert(tle->resdom->resno == groupColIdx[groupColPos]);
sortList = addTargetToSortList(NULL, tle, sortList,
my_tlist, NIL, false);
}
plan = (Plan *) make_sort_from_sortclauses(root, my_tlist,
subplan, sortList);
plan = (Plan *) make_unique(my_tlist, plan, sortList);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.120 2003/08/04 02:40:01 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.121 2003/08/07 19:20:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -59,9 +59,6 @@ static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype,
Var *l_colvar, Var *r_colvar);
static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node,
List *tlist, int clause);
static List *addTargetToSortList(ParseState *pstate, TargetEntry *tle,
List *sortlist, List *targetlist,
List *opname, bool resolveUnknown);
/*
@ -1478,7 +1475,7 @@ addAllTargetsToSortList(ParseState *pstate, List *sortlist,
*
* Returns the updated ORDER BY list.
*/
static List *
List *
addTargetToSortList(ParseState *pstate, TargetEntry *tle,
List *sortlist, List *targetlist,
List *opname, bool resolveUnknown)

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: parse_clause.h,v 1.35 2003/08/04 02:40:14 momjian Exp $
* $Id: parse_clause.h,v 1.36 2003/08/07 19:20:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -35,6 +35,9 @@ extern List *transformDistinctClause(ParseState *pstate, List *distinctlist,
extern List *addAllTargetsToSortList(ParseState *pstate,
List *sortlist, List *targetlist,
bool resolveUnknown);
extern List *addTargetToSortList(ParseState *pstate, TargetEntry *tle,
List *sortlist, List *targetlist,
List *opname, bool resolveUnknown);
extern Index assignSortGroupRef(TargetEntry *tle, List *tlist);
extern bool targetIsInSortList(TargetEntry *tle, List *sortList);

View File

@ -2127,6 +2127,18 @@ on (x1 = xx1) where (xx2 is not null);
4 | 44 | 4 | | 4 | 44
(3 rows)
--
-- regression test: check for bug with propagation of implied equality
-- to outside an IN
--
select count(*) from tenk1 a where unique1 in
(select unique1 from tenk1 b join tenk1 c using (unique1)
where b.unique2 = 42);
count
-------
1
(1 row)
--
-- Clean up
--

View File

@ -330,6 +330,14 @@ on (x1 = xx1) where (y2 is not null);
select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2)
on (x1 = xx1) where (xx2 is not null);
--
-- regression test: check for bug with propagation of implied equality
-- to outside an IN
--
select count(*) from tenk1 a where unique1 in
(select unique1 from tenk1 b join tenk1 c using (unique1)
where b.unique2 = 42);
--
-- Clean up