9
9
*
10
10
*
11
11
* 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 $
13
13
*
14
14
*-------------------------------------------------------------------------
15
15
*/
@@ -39,8 +39,8 @@ typedef struct
39
39
} fix_parsetree_attnums_context ;
40
40
41
41
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 );
44
44
static Plan * generate_union_plan (SetOperationStmt * op , Query * parse ,
45
45
List * refnames_tlist );
46
46
static Plan * generate_nonunion_plan (SetOperationStmt * op , Query * parse ,
@@ -49,9 +49,10 @@ static List *recurse_union_children(Node *setOp, Query *parse,
49
49
SetOperationStmt * top_union ,
50
50
List * refnames_tlist );
51
51
static List * generate_setop_tlist (List * colTypes , int flag ,
52
+ bool hack_constants ,
52
53
List * input_tlist ,
53
54
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 );
55
56
static void fix_parsetree_attnums (Index rt_index , Oid old_relid ,
56
57
Oid new_relid , Query * parsetree );
57
58
static bool fix_parsetree_attnums_walker (Node * node ,
@@ -95,10 +96,11 @@ plan_set_operations(Query *parse)
95
96
/*
96
97
* Recurse on setOperations tree to generate plans for set ops.
97
98
* 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).
99
101
*/
100
102
return recurse_set_operations ((Node * ) topop , parse ,
101
- topop -> colTypes , -1 ,
103
+ topop -> colTypes , true, -1 ,
102
104
leftmostQuery -> targetList );
103
105
}
104
106
@@ -107,13 +109,14 @@ plan_set_operations(Query *parse)
107
109
* Recursively handle one step in a tree of set operations
108
110
*
109
111
* colTypes: integer list of type OIDs of expected output columns
112
+ * junkOK: if true, child resjunk columns may be left in the result
110
113
* flag: if >= 0, add a resjunk output column indicating value of flag
111
114
* refnames_tlist: targetlist to take column names from
112
115
*/
113
116
static Plan *
114
117
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 )
117
120
{
118
121
if (IsA (setOp , RangeTblRef ))
119
122
{
@@ -133,7 +136,7 @@ recurse_set_operations(Node *setOp, Query *parse,
133
136
* Add a SubqueryScan with the caller-requested targetlist
134
137
*/
135
138
plan = (Plan * )
136
- make_subqueryscan (generate_setop_tlist (colTypes , flag ,
139
+ make_subqueryscan (generate_setop_tlist (colTypes , flag , true,
137
140
subplan -> targetlist ,
138
141
refnames_tlist ),
139
142
NIL ,
@@ -165,10 +168,11 @@ recurse_set_operations(Node *setOp, Query *parse,
165
168
* the referencing Vars will equal the tlist entries they reference.
166
169
* Ugly but I don't feel like making that code more general right now.
167
170
*/
168
- if (flag >= 0 || ! tlist_same_datatypes (plan -> targetlist , colTypes ))
171
+ if (flag >= 0 ||
172
+ ! tlist_same_datatypes (plan -> targetlist , colTypes , junkOK ))
169
173
{
170
174
plan = (Plan * )
171
- make_result (generate_setop_tlist (colTypes , flag ,
175
+ make_result (generate_setop_tlist (colTypes , flag , false,
172
176
plan -> targetlist ,
173
177
refnames_tlist ),
174
178
NULL ,
@@ -215,7 +219,7 @@ generate_union_plan(SetOperationStmt *op, Query *parse,
215
219
make_append (planlist ,
216
220
0 ,
217
221
NIL ,
218
- generate_setop_tlist (op -> colTypes , -1 ,
222
+ generate_setop_tlist (op -> colTypes , -1 , false,
219
223
((Plan * ) lfirst (planlist ))-> targetlist ,
220
224
refnames_tlist ));
221
225
/*
@@ -251,10 +255,10 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
251
255
252
256
/* Recurse on children, ensuring their outputs are marked */
253
257
lplan = recurse_set_operations (op -> larg , parse ,
254
- op -> colTypes , 0 ,
258
+ op -> colTypes , false, 0 ,
255
259
refnames_tlist );
256
260
rplan = recurse_set_operations (op -> rarg , parse ,
257
- op -> colTypes , 1 ,
261
+ op -> colTypes , false, 1 ,
258
262
refnames_tlist );
259
263
/*
260
264
* Append the child results together.
@@ -267,7 +271,7 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
267
271
make_append (makeList2 (lplan , rplan ),
268
272
0 ,
269
273
NIL ,
270
- generate_setop_tlist (op -> colTypes , 0 ,
274
+ generate_setop_tlist (op -> colTypes , 0 , false,
271
275
lplan -> targetlist ,
272
276
refnames_tlist ));
273
277
/*
@@ -321,17 +325,27 @@ recurse_union_children(Node *setOp, Query *parse,
321
325
top_union , refnames_tlist ));
322
326
}
323
327
}
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
+ */
325
338
return makeList1 (recurse_set_operations (setOp , parse ,
326
- top_union -> colTypes , -1 ,
327
- refnames_tlist ));
339
+ top_union -> colTypes , false ,
340
+ -1 , refnames_tlist ));
328
341
}
329
342
330
343
/*
331
344
* Generate targetlist for a set-operation plan node
332
345
*/
333
346
static List *
334
347
generate_setop_tlist (List * colTypes , int flag ,
348
+ bool hack_constants ,
335
349
List * input_tlist ,
336
350
List * refnames_tlist )
337
351
{
@@ -359,14 +373,17 @@ generate_setop_tlist(List *colTypes, int flag,
359
373
* HACK: constants in the input's targetlist are copied up as-is
360
374
* rather than being referenced as subquery outputs. This is mainly
361
375
* 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!
363
380
*/
364
381
resdom = makeResdom ((AttrNumber ) resno ++ ,
365
382
colType ,
366
383
-1 ,
367
384
pstrdup (reftle -> resdom -> resname ),
368
385
false);
369
- if (inputtle -> expr && IsA (inputtle -> expr , Const ))
386
+ if (hack_constants && inputtle -> expr && IsA (inputtle -> expr , Const ))
370
387
expr = inputtle -> expr ;
371
388
else
372
389
expr = (Node * ) makeVar (0 ,
@@ -407,18 +424,24 @@ generate_setop_tlist(List *colTypes, int flag,
407
424
/*
408
425
* Does tlist have same datatypes as requested colTypes?
409
426
*
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.
411
429
*/
412
430
static bool
413
- tlist_same_datatypes (List * tlist , List * colTypes )
431
+ tlist_same_datatypes (List * tlist , List * colTypes , bool junkOK )
414
432
{
415
433
List * i ;
416
434
417
435
foreach (i , tlist )
418
436
{
419
437
TargetEntry * tle = (TargetEntry * ) lfirst (i );
420
438
421
- if (!tle -> resdom -> resjunk )
439
+ if (tle -> resdom -> resjunk )
440
+ {
441
+ if (! junkOK )
442
+ return false;
443
+ }
444
+ else
422
445
{
423
446
if (colTypes == NIL )
424
447
return false;
0 commit comments