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
4141static 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 );
4444static Plan * generate_union_plan (SetOperationStmt * op ,Query * parse ,
4545List * refnames_tlist );
4646static Plan * generate_nonunion_plan (SetOperationStmt * op ,Query * parse ,
@@ -49,9 +49,10 @@ static List *recurse_union_children(Node *setOp, Query *parse,
4949SetOperationStmt * top_union ,
5050List * refnames_tlist );
5151static List * generate_setop_tlist (List * colTypes ,int flag ,
52+ bool hack_constants ,
5253List * input_tlist ,
5354List * refnames_tlist );
54- static bool tlist_same_datatypes (List * tlist ,List * colTypes );
55+ static bool tlist_same_datatypes (List * tlist ,List * colTypes , bool junkOK );
5556static void fix_parsetree_attnums (Index rt_index ,Oid old_relid ,
5657Oid new_relid ,Query * parsetree );
5758static 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 */
100102return recurse_set_operations ((Node * )topop ,parse ,
101- topop -> colTypes ,-1 ,
103+ topop -> colTypes ,true, -1 ,
102104leftmostQuery -> 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 */
113116static Plan *
114117recurse_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{
118121if (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 */
135138plan = (Plan * )
136- make_subqueryscan (generate_setop_tlist (colTypes ,flag ,
139+ make_subqueryscan (generate_setop_tlist (colTypes ,flag , true,
137140subplan -> targetlist ,
138141refnames_tlist ),
139142NIL ,
@@ -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{
170174plan = (Plan * )
171- make_result (generate_setop_tlist (colTypes ,flag ,
175+ make_result (generate_setop_tlist (colTypes ,flag , false,
172176plan -> targetlist ,
173177refnames_tlist ),
174178NULL ,
@@ -215,7 +219,7 @@ generate_union_plan(SetOperationStmt *op, Query *parse,
215219make_append (planlist ,
2162200 ,
217221NIL ,
218- generate_setop_tlist (op -> colTypes ,-1 ,
222+ generate_setop_tlist (op -> colTypes ,-1 , false,
219223((Plan * )lfirst (planlist ))-> targetlist ,
220224refnames_tlist ));
221225/*
@@ -251,10 +255,10 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
251255
252256/* Recurse on children, ensuring their outputs are marked */
253257lplan = recurse_set_operations (op -> larg ,parse ,
254- op -> colTypes ,0 ,
258+ op -> colTypes ,false, 0 ,
255259refnames_tlist );
256260rplan = recurse_set_operations (op -> rarg ,parse ,
257- op -> colTypes ,1 ,
261+ op -> colTypes ,false, 1 ,
258262refnames_tlist );
259263/*
260264 * Append the child results together.
@@ -267,7 +271,7 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
267271make_append (makeList2 (lplan ,rplan ),
2682720 ,
269273NIL ,
270- generate_setop_tlist (op -> colTypes ,0 ,
274+ generate_setop_tlist (op -> colTypes ,0 , false,
271275lplan -> targetlist ,
272276refnames_tlist ));
273277/*
@@ -321,17 +325,27 @@ recurse_union_children(Node *setOp, Query *parse,
321325top_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+ */
325338return 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 */
333346static List *
334347generate_setop_tlist (List * colTypes ,int flag ,
348+ bool hack_constants ,
335349List * input_tlist ,
336350List * 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 */
364381resdom = makeResdom ((AttrNumber )resno ++ ,
365382colType ,
366383-1 ,
367384pstrdup (reftle -> resdom -> resname ),
368385false);
369- if (inputtle -> expr && IsA (inputtle -> expr ,Const ))
386+ if (hack_constants && inputtle -> expr && IsA (inputtle -> expr ,Const ))
370387expr = inputtle -> expr ;
371388else
372389expr = (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 */
412430static bool
413- tlist_same_datatypes (List * tlist ,List * colTypes )
431+ tlist_same_datatypes (List * tlist ,List * colTypes , bool junkOK )
414432{
415433List * i ;
416434
417435foreach (i ,tlist )
418436{
419437TargetEntry * 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{
423446if (colTypes == NIL )
424447return false;