1414 *
1515 *
1616 * IDENTIFICATION
17- * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.68 2001/10/28 06:25:46 momjian Exp $
17+ * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.69 2001/11/12 20:04:20 tgl Exp $
1818 *
1919 *-------------------------------------------------------------------------
2020 */
@@ -63,6 +63,7 @@ static List *generate_setop_tlist(List *colTypes, int flag,
6363bool hack_constants ,
6464List * input_tlist ,
6565List * refnames_tlist );
66+ static void merge_tlist_typmods (List * tlist ,List * planlist );
6667static bool tlist_same_datatypes (List * tlist ,List * colTypes ,bool junkOK );
6768static Node * adjust_inherited_attrs_mutator (Node * node ,
6869adjust_inherited_attrs_context * context );
@@ -204,6 +205,7 @@ generate_union_plan(SetOperationStmt *op, Query *parse,
204205List * refnames_tlist )
205206{
206207List * planlist ;
208+ List * tlist ;
207209Plan * plan ;
208210
209211/*
@@ -218,29 +220,31 @@ generate_union_plan(SetOperationStmt *op, Query *parse,
218220op ,refnames_tlist ));
219221
220222/*
221- *Append the child results together .
223+ *Generate tlist for Append plan node .
222224 *
223225 * The tlist for an Append plan isn't important as far as the Append is
224226 * concerned, but we must make it look real anyway for the benefit of
225227 * the next plan level up.
226228 */
227- plan = (Plan * )
228- make_append (planlist ,
229- false,
230- generate_setop_tlist (op -> colTypes ,-1 , false,
229+ tlist = generate_setop_tlist (op -> colTypes ,-1 , false,
231230 ((Plan * )lfirst (planlist ))-> targetlist ,
232- refnames_tlist ));
231+ refnames_tlist );
232+ merge_tlist_typmods (tlist ,planlist );
233+
234+ /*
235+ * Append the child results together.
236+ */
237+ plan = (Plan * )make_append (planlist , false,tlist );
233238
234239/*
235240 * For UNION ALL, we just need the Append plan. For UNION, need to
236241 * add Sort and Unique nodes to produce unique output.
237242 */
238243if (!op -> all )
239244{
240- List * tlist ,
241- * sortList ;
245+ List * sortList ;
242246
243- tlist = new_unsorted_tlist (plan -> targetlist );
247+ tlist = new_unsorted_tlist (tlist );
244248sortList = addAllTargetsToSortList (NIL ,tlist );
245249plan = make_sortplan (parse ,tlist ,plan ,sortList );
246250plan = (Plan * )make_unique (tlist ,plan ,copyObject (sortList ));
@@ -259,7 +263,8 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
259263* rplan ,
260264* plan ;
261265List * tlist ,
262- * sortList ;
266+ * sortList ,
267+ * planlist ;
263268SetOpCmd cmd ;
264269
265270/* Recurse on children, ensuring their outputs are marked */
@@ -269,28 +274,32 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
269274rplan = recurse_set_operations (op -> rarg ,parse ,
270275op -> colTypes , false,1 ,
271276refnames_tlist );
277+ planlist = makeList2 (lplan ,rplan );
272278
273279/*
274- *Append the child results together .
280+ *Generate tlist for Append plan node .
275281 *
276282 * The tlist for an Append plan isn't important as far as the Append is
277283 * concerned, but we must make it look real anyway for the benefit of
278284 * the next plan level up.In fact, it has to be real enough that the
279285 * flag column is shown as a variable not a constant, else setrefs.c
280286 * will get confused.
281287 */
282- plan = (Plan * )
283- make_append (makeList2 (lplan ,rplan ),
284- false,
285- generate_setop_tlist (op -> colTypes ,2 , false,
286- lplan -> targetlist ,
287- refnames_tlist ));
288+ tlist = generate_setop_tlist (op -> colTypes ,2 , false,
289+ lplan -> targetlist ,
290+ refnames_tlist );
291+ merge_tlist_typmods (tlist ,planlist );
292+
293+ /*
294+ * Append the child results together.
295+ */
296+ plan = (Plan * )make_append (planlist , false,tlist );
288297
289298/*
290299 * Sort the child results, then add a SetOp plan node to generate the
291300 * correct output.
292301 */
293- tlist = new_unsorted_tlist (plan -> targetlist );
302+ tlist = new_unsorted_tlist (tlist );
294303sortList = addAllTargetsToSortList (NIL ,tlist );
295304plan = make_sortplan (parse ,tlist ,plan ,sortList );
296305switch (op -> op )
@@ -332,9 +341,11 @@ recurse_union_children(Node *setOp, Query *parse,
332341{
333342/* Same UNION, so fold children into parent's subplan list */
334343return nconc (recurse_union_children (op -> larg ,parse ,
335- top_union ,refnames_tlist ),
344+ top_union ,
345+ refnames_tlist ),
336346recurse_union_children (op -> rarg ,parse ,
337- top_union ,refnames_tlist ));
347+ top_union ,
348+ refnames_tlist ));
338349}
339350}
340351
@@ -380,6 +391,7 @@ generate_setop_tlist(List *colTypes, int flag,
380391Oid colType = (Oid )lfirsti (i );
381392TargetEntry * inputtle = (TargetEntry * )lfirst (input_tlist );
382393TargetEntry * reftle = (TargetEntry * )lfirst (refnames_tlist );
394+ int32 colTypmod ;
383395
384396Assert (inputtle -> resdom -> resno == resno );
385397Assert (reftle -> resdom -> resno == resno );
@@ -399,11 +411,6 @@ generate_setop_tlist(List *colTypes, int flag,
399411 * subquery-scan plans; we don't want phony constants appearing in
400412 * the output tlists of upper-level nodes!
401413 */
402- resdom = makeResdom ((AttrNumber )resno ++ ,
403- colType ,
404- -1 ,
405- pstrdup (reftle -> resdom -> resname ),
406- false);
407414if (hack_constants && inputtle -> expr && IsA (inputtle -> expr ,Const ))
408415expr = inputtle -> expr ;
409416else
@@ -412,10 +419,24 @@ generate_setop_tlist(List *colTypes, int flag,
412419inputtle -> resdom -> restype ,
413420inputtle -> resdom -> restypmod ,
4144210 );
415- expr = coerce_to_common_type (NULL ,
416- expr ,
417- colType ,
418- "UNION/INTERSECT/EXCEPT" );
422+ if (inputtle -> resdom -> restype == colType )
423+ {
424+ /* no coercion needed, and believe the input typmod */
425+ colTypmod = inputtle -> resdom -> restypmod ;
426+ }
427+ else
428+ {
429+ expr = coerce_to_common_type (NULL ,
430+ expr ,
431+ colType ,
432+ "UNION/INTERSECT/EXCEPT" );
433+ colTypmod = -1 ;
434+ }
435+ resdom = makeResdom ((AttrNumber )resno ++ ,
436+ colType ,
437+ colTypmod ,
438+ pstrdup (reftle -> resdom -> resname ),
439+ false);
419440tlist = lappend (tlist ,makeTargetEntry (resdom ,expr ));
420441input_tlist = lnext (input_tlist );
421442refnames_tlist = lnext (refnames_tlist );
@@ -455,6 +476,47 @@ generate_setop_tlist(List *colTypes, int flag,
455476return tlist ;
456477}
457478
479+ /*
480+ * Merge typmods of a list of set-operation subplans.
481+ *
482+ * If the inputs all agree on type and typmod of a particular column,
483+ * use that typmod; else use -1. We assume the result tlist has been
484+ * initialized with the types and typmods of the first input subplan.
485+ */
486+ static void
487+ merge_tlist_typmods (List * tlist ,List * planlist )
488+ {
489+ List * planl ;
490+
491+ foreach (planl ,planlist )
492+ {
493+ Plan * subplan = (Plan * )lfirst (planl );
494+ List * subtlist = subplan -> targetlist ;
495+ List * restlist ;
496+
497+ foreach (restlist ,tlist )
498+ {
499+ TargetEntry * restle = (TargetEntry * )lfirst (restlist );
500+ TargetEntry * subtle ;
501+
502+ if (restle -> resdom -> resjunk )
503+ continue ;
504+ Assert (subtlist != NIL );
505+ subtle = (TargetEntry * )lfirst (subtlist );
506+ while (subtle -> resdom -> resjunk )
507+ {
508+ subtlist = lnext (subtlist );
509+ Assert (subtlist != NIL );
510+ subtle = (TargetEntry * )lfirst (subtlist );
511+ }
512+ if (restle -> resdom -> restype != subtle -> resdom -> restype ||
513+ restle -> resdom -> restypmod != subtle -> resdom -> restypmod )
514+ restle -> resdom -> restypmod = -1 ;
515+ subtlist = lnext (subtlist );
516+ }
517+ }
518+ }
519+
458520/*
459521 * Does tlist have same datatypes as requested colTypes?
460522 *