Skip to content

Commit a1d1339

Browse files
committed
Repair some bugs in new union/intersect/except code.
Thanks to Kevin O'Gorman for finding these...
1 parent 26adbc7 commit a1d1339

File tree

2 files changed

+95
-27
lines changed

2 files changed

+95
-27
lines changed

src/backend/optimizer/plan/planner.c

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.94 2000/11/05 00:15:53 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.95 2000/11/09 02:46:16 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -48,6 +48,8 @@ static List *make_subplanTargetList(Query *parse, List *tlist,
4848
static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup,
4949
List *groupClause, AttrNumber *grpColIdx,
5050
bool is_presorted, Plan *subplan);
51+
static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
52+
5153

5254
/*****************************************************************************
5355
*
@@ -635,17 +637,22 @@ union_planner(Query *parse,
635637
if (parse->setOperations)
636638
{
637639
/*
638-
* Construct the plan for set operations. The result will
639-
* not need any work except perhaps a top-level sort.
640+
* Construct the plan for set operations. The result will not
641+
* need any work except perhaps a top-level sort and/or LIMIT.
640642
*/
641643
result_plan = plan_set_operations(parse);
642644

643645
/*
644646
* We should not need to call preprocess_targetlist, since we must
645-
* be in a SELECT query node.
647+
* be in a SELECT query node. Instead, use the targetlist
648+
* returned by plan_set_operations (since this tells whether it
649+
* returned any resjunk columns!), and transfer any sort key
650+
* information from the original tlist.
646651
*/
647652
Assert(parse->commandType == CMD_SELECT);
648653

654+
tlist = postprocess_setop_tlist(result_plan->targetlist, tlist);
655+
649656
/*
650657
* We leave current_pathkeys NIL indicating we do not know sort
651658
* order. This is correct when the top set operation is UNION ALL,
@@ -1255,3 +1262,41 @@ make_sortplan(List *tlist, Plan *plannode, List *sortcls)
12551262

12561263
return (Plan *) make_sort(sort_tlist, plannode, keyno);
12571264
}
1265+
1266+
/*
1267+
* postprocess_setop_tlist
1268+
* Fix up targetlist returned by plan_set_operations().
1269+
*
1270+
* We need to transpose sort key info from the orig_tlist into new_tlist.
1271+
* NOTE: this would not be good enough if we supported resjunk sort keys
1272+
* for results of set operations --- then, we'd need to project a whole
1273+
* new tlist to evaluate the resjunk columns. For now, just elog if we
1274+
* find any resjunk columns in orig_tlist.
1275+
*/
1276+
static List *
1277+
postprocess_setop_tlist(List *new_tlist, List *orig_tlist)
1278+
{
1279+
List *l;
1280+
1281+
foreach(l, new_tlist)
1282+
{
1283+
TargetEntry *new_tle = (TargetEntry *) lfirst(l);
1284+
TargetEntry *orig_tle;
1285+
1286+
/* ignore resjunk columns in setop result */
1287+
if (new_tle->resdom->resjunk)
1288+
continue;
1289+
1290+
Assert(orig_tlist != NIL);
1291+
orig_tle = (TargetEntry *) lfirst(orig_tlist);
1292+
orig_tlist = lnext(orig_tlist);
1293+
if (orig_tle->resdom->resjunk)
1294+
elog(ERROR, "postprocess_setop_tlist: resjunk output columns not implemented");
1295+
Assert(new_tle->resdom->resno == orig_tle->resdom->resno);
1296+
Assert(new_tle->resdom->restype == orig_tle->resdom->restype);
1297+
new_tle->resdom->ressortgroupref = orig_tle->resdom->ressortgroupref;
1298+
}
1299+
if (orig_tlist != NIL)
1300+
elog(ERROR, "postprocess_setop_tlist: resjunk output columns not implemented");
1301+
return new_tlist;
1302+
}

src/backend/optimizer/prep/prepunion.c

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*
1010
*
1111
* IDENTIFICATION
12-
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.54 2000/10/05 19:11:30 tgl Exp $
12+
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.55 2000/11/09 02:46:17 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -39,8 +39,8 @@ typedef struct
3939
} fix_parsetree_attnums_context;
4040

4141
static Plan *recurse_set_operations(Node *setOp, Query *parse,
42-
List *colTypes, int flag,
43-
List *refnames_tlist);
42+
List *colTypes, bool junkOK,
43+
int flag, List *refnames_tlist);
4444
static Plan *generate_union_plan(SetOperationStmt *op, Query *parse,
4545
List *refnames_tlist);
4646
static Plan *generate_nonunion_plan(SetOperationStmt *op, Query *parse,
@@ -49,9 +49,10 @@ static List *recurse_union_children(Node *setOp, Query *parse,
4949
SetOperationStmt *top_union,
5050
List *refnames_tlist);
5151
static List *generate_setop_tlist(List *colTypes, int flag,
52+
bool hack_constants,
5253
List *input_tlist,
5354
List *refnames_tlist);
54-
static bool tlist_same_datatypes(List *tlist, List *colTypes);
55+
static bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK);
5556
static void fix_parsetree_attnums(Index rt_index, Oid old_relid,
5657
Oid new_relid, Query *parsetree);
5758
static bool fix_parsetree_attnums_walker(Node *node,
@@ -95,10 +96,11 @@ plan_set_operations(Query *parse)
9596
/*
9697
* Recurse on setOperations tree to generate plans for set ops.
9798
* The final output plan should have just the column types shown
98-
* as the output from the top-level node.
99+
* as the output from the top-level node, plus possibly a resjunk
100+
* working column (we can rely on upper-level nodes to deal with that).
99101
*/
100102
return recurse_set_operations((Node *) topop, parse,
101-
topop->colTypes, -1,
103+
topop->colTypes, true, -1,
102104
leftmostQuery->targetList);
103105
}
104106

@@ -107,13 +109,14 @@ plan_set_operations(Query *parse)
107109
* Recursively handle one step in a tree of set operations
108110
*
109111
* colTypes: integer list of type OIDs of expected output columns
112+
* junkOK: if true, child resjunk columns may be left in the result
110113
* flag: if >= 0, add a resjunk output column indicating value of flag
111114
* refnames_tlist: targetlist to take column names from
112115
*/
113116
static Plan *
114117
recurse_set_operations(Node *setOp, Query *parse,
115-
List *colTypes, int flag,
116-
List *refnames_tlist)
118+
List *colTypes, bool junkOK,
119+
int flag, List *refnames_tlist)
117120
{
118121
if (IsA(setOp, RangeTblRef))
119122
{
@@ -133,7 +136,7 @@ recurse_set_operations(Node *setOp, Query *parse,
133136
* Add a SubqueryScan with the caller-requested targetlist
134137
*/
135138
plan = (Plan *)
136-
make_subqueryscan(generate_setop_tlist(colTypes, flag,
139+
make_subqueryscan(generate_setop_tlist(colTypes, flag, true,
137140
subplan->targetlist,
138141
refnames_tlist),
139142
NIL,
@@ -165,10 +168,11 @@ recurse_set_operations(Node *setOp, Query *parse,
165168
* the referencing Vars will equal the tlist entries they reference.
166169
* Ugly but I don't feel like making that code more general right now.
167170
*/
168-
if (flag >= 0 || ! tlist_same_datatypes(plan->targetlist, colTypes))
171+
if (flag >= 0 ||
172+
! tlist_same_datatypes(plan->targetlist, colTypes, junkOK))
169173
{
170174
plan = (Plan *)
171-
make_result(generate_setop_tlist(colTypes, flag,
175+
make_result(generate_setop_tlist(colTypes, flag, false,
172176
plan->targetlist,
173177
refnames_tlist),
174178
NULL,
@@ -215,7 +219,7 @@ generate_union_plan(SetOperationStmt *op, Query *parse,
215219
make_append(planlist,
216220
0,
217221
NIL,
218-
generate_setop_tlist(op->colTypes, -1,
222+
generate_setop_tlist(op->colTypes, -1, false,
219223
((Plan *) lfirst(planlist))->targetlist,
220224
refnames_tlist));
221225
/*
@@ -251,10 +255,10 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
251255

252256
/* Recurse on children, ensuring their outputs are marked */
253257
lplan = recurse_set_operations(op->larg, parse,
254-
op->colTypes, 0,
258+
op->colTypes, false, 0,
255259
refnames_tlist);
256260
rplan = recurse_set_operations(op->rarg, parse,
257-
op->colTypes, 1,
261+
op->colTypes, false, 1,
258262
refnames_tlist);
259263
/*
260264
* Append the child results together.
@@ -267,7 +271,7 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
267271
make_append(makeList2(lplan, rplan),
268272
0,
269273
NIL,
270-
generate_setop_tlist(op->colTypes, 0,
274+
generate_setop_tlist(op->colTypes, 0, false,
271275
lplan->targetlist,
272276
refnames_tlist));
273277
/*
@@ -321,17 +325,27 @@ recurse_union_children(Node *setOp, Query *parse,
321325
top_union, refnames_tlist));
322326
}
323327
}
324-
/* Not same, so plan this child separately */
328+
/*
329+
* Not same, so plan this child separately.
330+
*
331+
* Note we disallow any resjunk columns in child results. This
332+
* is necessary since the Append node that implements the union
333+
* won't do any projection, and upper levels will get confused if
334+
* some of our output tuples have junk and some don't. This case
335+
* only arises when we have an EXCEPT or INTERSECT as child, else
336+
* there won't be resjunk anyway.
337+
*/
325338
return makeList1(recurse_set_operations(setOp, parse,
326-
top_union->colTypes, -1,
327-
refnames_tlist));
339+
top_union->colTypes, false,
340+
-1, refnames_tlist));
328341
}
329342

330343
/*
331344
* Generate targetlist for a set-operation plan node
332345
*/
333346
static List *
334347
generate_setop_tlist(List *colTypes, int flag,
348+
bool hack_constants,
335349
List *input_tlist,
336350
List *refnames_tlist)
337351
{
@@ -359,14 +373,17 @@ generate_setop_tlist(List *colTypes, int flag,
359373
* HACK: constants in the input's targetlist are copied up as-is
360374
* rather than being referenced as subquery outputs. This is mainly
361375
* to ensure that when we try to coerce them to the output column's
362-
* datatype, the right things happen for UNKNOWN constants.
376+
* datatype, the right things happen for UNKNOWN constants. But do
377+
* this only at the first level of subquery-scan plans; we don't
378+
* want phony constants appearing in the output tlists of upper-level
379+
* nodes!
363380
*/
364381
resdom = makeResdom((AttrNumber) resno++,
365382
colType,
366383
-1,
367384
pstrdup(reftle->resdom->resname),
368385
false);
369-
if (inputtle->expr && IsA(inputtle->expr, Const))
386+
if (hack_constants && inputtle->expr && IsA(inputtle->expr, Const))
370387
expr = inputtle->expr;
371388
else
372389
expr = (Node *) makeVar(0,
@@ -407,18 +424,24 @@ generate_setop_tlist(List *colTypes, int flag,
407424
/*
408425
* Does tlist have same datatypes as requested colTypes?
409426
*
410-
* Resjunk columns are ignored.
427+
* Resjunk columns are ignored if junkOK is true; otherwise presence of
428+
* a resjunk column will always cause a 'false' result.
411429
*/
412430
static bool
413-
tlist_same_datatypes(List *tlist, List *colTypes)
431+
tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
414432
{
415433
List *i;
416434

417435
foreach(i, tlist)
418436
{
419437
TargetEntry *tle = (TargetEntry *) lfirst(i);
420438

421-
if (!tle->resdom->resjunk)
439+
if (tle->resdom->resjunk)
440+
{
441+
if (! junkOK)
442+
return false;
443+
}
444+
else
422445
{
423446
if (colTypes == NIL)
424447
return false;

0 commit comments

Comments
 (0)