]> granicus.if.org Git - postgresql/blob - src/backend/parser/parse_clause.c
Move some system includes into c.h, and remove duplicates.
[postgresql] / src / backend / parser / parse_clause.c
1 /*-------------------------------------------------------------------------
2  *
3  * parse_clause.c
4  *        handle clauses in parser
5  *
6  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.41 1999/07/17 20:17:23 momjian Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14
15 #include "postgres.h"
16 #include "access/heapam.h"
17 #include "nodes/relation.h"
18 #include "parse.h"
19 #include "parser/parse_clause.h"
20 #include "parser/parse_coerce.h"
21 #include "parser/parse_expr.h"
22 #include "parser/parse_oper.h"
23 #include "parser/parse_relation.h"
24 #include "parser/parse_target.h"
25
26
27 #define ORDER_CLAUSE 0
28 #define GROUP_CLAUSE 1
29
30 static char *clauseText[] = {"ORDER", "GROUP"};
31
32 static TargetEntry *
33                         findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause);
34
35 static void parseFromClause(ParseState *pstate, List *frmList, Node **qual);
36
37 #ifdef ENABLE_OUTER_JOINS
38 Node       *transformUsingClause(ParseState *pstate, List *onList, char *lname, char *rname);
39
40 #endif
41
42 static char     *transformTableEntry(ParseState *pstate, RangeVar *r);
43
44
45 /*
46  * makeRangeTable -
47  *        make a range table with the specified relation (optional) and the
48  *        from_clause.
49  */
50 void
51 makeRangeTable(ParseState *pstate, char *relname, List *frmList, Node **qual)
52 {
53         RangeTblEntry *rte;
54         int                     sublevels_up;
55
56         parseFromClause(pstate, frmList, qual);
57
58         if (relname == NULL)
59                 return;
60
61         if ((refnameRangeTablePosn(pstate, relname, &sublevels_up) == 0)
62                 || (sublevels_up != 0))
63                 rte = addRangeTableEntry(pstate, relname, relname, FALSE, FALSE);
64         else
65                 rte = refnameRangeTableEntry(pstate, relname);
66
67         /* This could only happen for multi-action rules */
68         if (pstate->p_target_relation != NULL)
69                 heap_close(pstate->p_target_relation);
70
71         pstate->p_target_rangetblentry = rte;
72         pstate->p_target_relation = heap_open(rte->relid);
73         /* will close relation later */
74 }
75
76 /*
77  * transformWhereClause -
78  *        transforms the qualification and make sure it is of type Boolean
79  *
80  * Now accept an additional argument, which is a qualification derived
81  * from the JOIN/ON or JOIN/USING syntax.
82  * - thomas 1998-12-16
83  */
84 Node *
85 transformWhereClause(ParseState *pstate, Node *a_expr, Node *o_expr)
86 {
87         A_Expr     *expr;
88         Node       *qual;
89
90         if ((a_expr == NULL) && (o_expr == NULL))
91                 return NULL;                    /* no qualifiers */
92
93         if ((a_expr != NULL) && (o_expr != NULL))
94         {
95                 A_Expr     *a = makeNode(A_Expr);
96
97                 a->oper = AND;
98                 a->opname = NULL;
99                 a->lexpr = o_expr;
100                 a->rexpr = a_expr;
101                 expr = a;
102         }
103         else if (o_expr != NULL)
104                 expr = (A_Expr *) o_expr;
105         else
106                 expr = (A_Expr *) a_expr;
107
108         pstate->p_in_where_clause = true;
109         qual = transformExpr(pstate, (Node *) expr, EXPR_COLUMN_FIRST);
110         pstate->p_in_where_clause = false;
111
112         if (exprType(qual) != BOOLOID)
113         {
114                 elog(ERROR, "WHERE clause must return type bool, not type %s",
115                          typeidTypeName(exprType(qual)));
116         }
117         return qual;
118 }
119
120 #ifdef NOT_USED
121 static Attr *
122 makeAttr(char *relname, char *attname)
123 {
124         Attr       *a = makeNode(Attr);
125
126         a->relname = relname;
127         a->paramNo = NULL;
128         a->attrs = lcons(makeString(attname), NIL);
129         a->indirection = NULL;
130
131         return a;
132 }
133 #endif
134
135 #ifdef ENABLE_OUTER_JOINS
136 /* transformUsingClause()
137  * Take an ON or USING clause from a join expression and expand if necessary.
138  */
139 Node *
140 transformUsingClause(ParseState *pstate, List *onList, char *lname, char *rname)
141 {
142         A_Expr     *expr = NULL;
143         List       *on;
144         Node       *qual;
145
146         foreach(on, onList)
147         {
148                 qual = lfirst(on);
149
150                 /*
151                  * Ident node means it is just a column name from a real USING
152                  * clause...
153                  */
154                 if (IsA(qual, Ident))
155                 {
156                         Ident      *i = (Ident *) qual;
157                         Attr       *lattr = makeAttr(lname, i->name);
158                         Attr       *rattr = makeAttr(rname, i->name);
159                         A_Expr     *e = makeNode(A_Expr);
160
161                         e->oper = OP;
162                         e->opname = "=";
163                         e->lexpr = (Node *) lattr;
164                         e->rexpr = (Node *) rattr;
165
166                         if (expr != NULL)
167                         {
168                                 A_Expr     *a = makeNode(A_Expr);
169
170                                 a->oper = AND;
171                                 a->opname = NULL;
172                                 a->lexpr = (Node *) expr;
173                                 a->rexpr = (Node *) e;
174                                 expr = a;
175                         }
176                         else
177                                 expr = e;
178                 }
179
180                 /* otherwise, we have an expression from an ON clause... */
181                 else
182                 {
183                         if (expr != NULL)
184                         {
185                                 A_Expr     *a = makeNode(A_Expr);
186
187                                 a->oper = AND;
188                                 a->opname = NULL;
189                                 a->lexpr = (Node *) expr;
190                                 a->rexpr = (Node *) qual;
191                                 expr = a;
192                         }
193                         else
194                                 expr = (A_Expr *) qual;
195                 }
196         }
197         return ((Node *) transformExpr(pstate, (Node *) expr, EXPR_COLUMN_FIRST));
198 }
199
200 #endif
201
202 static char *
203 transformTableEntry(ParseState *pstate, RangeVar *r)
204 {
205         RelExpr    *baserel = r->relExpr;
206         char       *relname = baserel->relname;
207         char       *refname = r->name;
208         RangeTblEntry *rte;
209
210         if (refname == NULL)
211                 refname = relname;
212
213         /*
214          * marks this entry to indicate it comes from the FROM clause. In SQL,
215          * the target list can only refer to range variables specified in the
216          * from clause but we follow the more powerful POSTQUEL semantics and
217          * automatically generate the range variable if not specified. However
218          * there are times we need to know whether the entries are legitimate.
219          *
220          * eg. select * from foo f where f.x = 1; will generate wrong answer if
221          * we expand * to foo.x.
222          */
223
224         rte = addRangeTableEntry(pstate, relname, refname, baserel->inh, TRUE);
225
226         return refname;
227 }
228
229 /*
230  * parseFromClause -
231  *        turns the table references specified in the from-clause into a
232  *        range table. The range table may grow as we transform the expressions
233  *        in the target list. (Note that this happens because in POSTQUEL, we
234  *        allow references to relations not specified in the from-clause. We
235  *        also allow now as an extension.)
236  *
237  * The FROM clause can now contain JoinExpr nodes, which contain parsing info
238  * for inner and outer joins. The USING clause must be expanded into a qualification
239  * for an inner join at least, since that is compatible with the old syntax.
240  * Not sure yet how to handle outer joins, but it will become clear eventually?
241  * - thomas 1998-12-16
242  */
243 static void
244 parseFromClause(ParseState *pstate, List *frmList, Node **qual)
245 {
246         List       *fl;
247
248         if (qual != NULL)
249                 *qual = NULL;
250
251         foreach(fl, frmList)
252         {
253                 Node       *n = lfirst(fl);
254
255                 /*
256                  * marks this entry to indicate it comes from the FROM clause. In
257                  * SQL, the target list can only refer to range variables
258                  * specified in the from clause but we follow the more powerful
259                  * POSTQUEL semantics and automatically generate the range
260                  * variable if not specified. However there are times we need to
261                  * know whether the entries are legitimate.
262                  *
263                  * eg. select * from foo f where f.x = 1; will generate wrong answer
264                  * if we expand * to foo.x.
265                  */
266                 if (IsA(n, RangeVar))
267                         transformTableEntry(pstate, (RangeVar *) n);
268                 else if (IsA(n, JoinExpr))
269                 {
270                         JoinExpr   *j = (JoinExpr *) n;
271
272 #ifdef ENABLE_OUTER_JOINS
273                         char       *lname = transformTableEntry(pstate, (RangeVar *) j->larg);
274
275 #endif
276                         char       *rname;
277
278                         if (IsA((Node *) j->rarg, RangeVar))
279                                 rname = transformTableEntry(pstate, (RangeVar *) j->rarg);
280                         else
281                                 elog(ERROR, "Nested JOINs are not yet supported");
282
283 #ifdef ENABLE_OUTER_JOINS
284                         if (j->jointype == INNER_P)
285                         {
286
287                                 /*
288                                  * This is an inner join, so rip apart the join node and
289                                  * transform into a traditional FROM list. NATURAL JOIN
290                                  * and USING clauses both change the shape of the result.
291                                  * Need to generate a list of result columns to use for
292                                  * target list expansion and validation. Not doing this
293                                  * yet though!
294                                  */
295                                 if (IsA(j->quals, List))
296                                         j->quals = lcons(transformUsingClause(pstate, (List *) j->quals, lname, rname), NIL);
297
298                                 Assert(qual != NULL);
299
300                                 if (*qual == NULL)
301                                         *qual = lfirst(j->quals);
302                                 else
303                                         elog(ERROR, "Multiple JOIN/ON clauses not handled (internal error)");
304
305                                 /*
306                                  * if we are transforming this node back into a FROM list,
307                                  * then we will need to replace the node with two nodes.
308                                  * Will need access to the previous list item to change
309                                  * the link pointer to reference these new nodes. Try
310                                  * accumulating and returning a new list. - thomas
311                                  * 1999-01-08 Not doing this yet though!
312                                  */
313
314                         }
315                         else if ((j->jointype == LEFT)
316                                          || (j->jointype == RIGHT)
317                                          || (j->jointype == FULL))
318                                 elog(ERROR, "OUTER JOIN is not implemented");
319                         else
320                                 elog(ERROR, "Unrecognized JOIN clause; tag is %d (internal error)",
321                                          j->jointype);
322 #else
323                         elog(ERROR, "JOIN expressions are not yet implemented");
324 #endif
325                 }
326                 else
327                         elog(ERROR, "parseFromClause: unexpected FROM clause node (internal error)"
328                                  "\n\t%s", nodeToString(n));
329         }
330 }
331
332 /*
333  *      findTargetlistEntry -
334  *        returns the Resdom in the target list matching the specified varname
335  *        and range. If none exist one is created.
336  *
337  *        Rewritten for ver 6.4 to handle expressions in the GROUP/ORDER BY clauses.
338  *         - daveh@insightdist.com      1998-07-31
339  *
340  */
341 static TargetEntry *
342 findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause)
343 {
344         List       *l;
345         int                     rtable_pos = 0,
346                                 target_pos = 0,
347                                 targetlist_pos = 0;
348         TargetEntry *target_result = NULL;
349         Value      *val = NULL;
350         char       *relname = NULL;
351         char       *name = NULL;
352         Node       *expr = NULL;
353         int                     relCnt = 0;
354
355         /* Pull out some values before looping thru target list  */
356         switch (nodeTag(node))
357         {
358                 case T_Attr:
359                         relname = ((Attr *) node)->relname;
360                         val = (Value *) lfirst(((Attr *) node)->attrs);
361                         name = strVal(val);
362                         rtable_pos = refnameRangeTablePosn(pstate, relname, NULL);
363                         relCnt = length(pstate->p_rtable);
364                         break;
365
366                 case T_Ident:
367                         name = ((Ident *) node)->name;
368                         relCnt = length(pstate->p_rtable);
369                         break;
370
371                 case T_A_Const:
372                         val = &((A_Const *) node)->val;
373
374                         if (nodeTag(val) != T_Integer)
375                                 elog(ERROR, "Illegal Constant in %s BY", clauseText[clause]);
376                         target_pos = intVal(val);
377                         break;
378
379                 case T_FuncCall:
380                 case T_A_Expr:
381                         expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
382                         break;
383
384                 default:
385                         elog(ERROR, "Illegal %s BY node = %d", clauseText[clause], nodeTag(node));
386         }
387
388         /*
389          * Loop through target entries and try to match to node
390          */
391         foreach(l, tlist)
392         {
393                 TargetEntry *target = (TargetEntry *) lfirst(l);
394                 Resdom     *resnode = target->resdom;
395                 Var                *var = (Var *) target->expr;
396                 char       *resname = resnode->resname;
397                 int                     test_rtable_pos = var->varno;
398
399                 ++targetlist_pos;
400
401                 switch (nodeTag(node))
402                 {
403                         case T_Attr:
404                                 if (strcmp(resname, name) == 0 && rtable_pos == test_rtable_pos)
405                                 {
406
407                                         /*
408                                          * Check for only 1 table & ORDER BY -ambiguity does
409                                          * not matter here
410                                          */
411                                         if (clause == ORDER_CLAUSE && relCnt == 1)
412                                                 return target;
413
414                                         if (target_result != NULL)
415                                                 elog(ERROR, "%s BY '%s' is ambiguous", clauseText[clause], name);
416                                         else
417                                                 target_result = target;
418                                         /* Stay in loop to check for ambiguity */
419                                 }
420                                 break;
421
422                         case T_Ident:
423                                 if (strcmp(resname, name) == 0)
424                                 {
425
426                                         /*
427                                          * Check for only 1 table & ORDER BY  -ambiguity does
428                                          * not matter here
429                                          */
430                                         if (clause == ORDER_CLAUSE && relCnt == 1)
431                                                 return target;
432
433                                         if (target_result != NULL)
434                                                 elog(ERROR, "%s BY '%s' is ambiguous", clauseText[clause], name);
435                                         else
436                                                 target_result = target;
437                                         /* Stay in loop to check for ambiguity  */
438                                 }
439                                 break;
440
441                         case T_A_Const:
442                                 if (target_pos == targetlist_pos)
443                                 {
444                                         /* Can't be ambigious and we got what we came for  */
445                                         return target;
446                                 }
447                                 break;
448
449                         case T_FuncCall:
450                         case T_A_Expr:
451                                 if (equal(expr, target->expr))
452                                 {
453
454                                         /*
455                                          * Check for only 1 table & ORDER BY  -ambiguity does
456                                          * not matter here
457                                          */
458                                         if (clause == ORDER_CLAUSE)
459                                                 return target;
460
461                                         if (target_result != NULL)
462                                                 elog(ERROR, "GROUP BY has ambiguous expression");
463                                         else
464                                                 target_result = target;
465                                 }
466                                 break;
467
468                         default:
469                                 elog(ERROR, "Illegal %s BY node = %d", clauseText[clause], nodeTag(node));
470                 }
471         }
472
473         /*
474          * If no matches, construct a new target entry which is appended to
475          * the end of the target list.   This target is set to be  resjunk =
476          * TRUE so that it will not be projected into the final tuple.
477          */
478         if (target_result == NULL)
479         {
480                 switch (nodeTag(node))
481                 {
482                         case T_Attr:
483                                 target_result = MakeTargetEntryIdent(pstate, node,
484                                                                                  &((Attr *) node)->relname, NULL,
485                                                                                  ((Attr *) node)->relname, true);
486                                 lappend(tlist, target_result);
487                                 break;
488
489                         case T_Ident:
490                                 target_result = MakeTargetEntryIdent(pstate, node,
491                                                                                    &((Ident *) node)->name, NULL,
492                                                                                    ((Ident *) node)->name, true);
493                                 lappend(tlist, target_result);
494                                 break;
495
496                         case T_A_Const:
497
498                                 /*
499                                  * If we got this far, then must have been an out-of-range
500                                  * column number
501                                  */
502                                 elog(ERROR, "%s BY position %d is not in target list", clauseText[clause], target_pos);
503                                 break;
504
505                         case T_FuncCall:
506                         case T_A_Expr:
507                                 target_result = MakeTargetEntryExpr(pstate, "resjunk", expr, false, true);
508                                 lappend(tlist, target_result);
509                                 break;
510
511                         default:
512                                 elog(ERROR, "Illegal %s BY node = %d", clauseText[clause], nodeTag(node));
513                                 break;
514                 }
515         }
516
517         return target_result;
518 }
519
520
521
522
523 /*
524  * transformGroupClause -
525  *        transform a Group By clause
526  *
527  */
528 List *
529 transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist)
530 {
531         List       *glist = NIL,
532                            *gl = NIL;
533
534         while (grouplist != NIL)
535         {
536                 GroupClause *grpcl = makeNode(GroupClause);
537                 TargetEntry *restarget;
538                 Resdom     *resdom;
539
540                 restarget = findTargetlistEntry(pstate, lfirst(grouplist), targetlist, GROUP_CLAUSE);
541
542                 resdom = restarget->resdom;
543                 grpcl->grpOpoid = oprid(oper("<",
544                                                                          resdom->restype,
545                                                                          resdom->restype, false));
546                 if (glist == NIL)
547                 {
548                         int                     groupref = length(glist) + 1;
549
550                         restarget->resdom->resgroupref = groupref;
551                         grpcl->tleGroupref = groupref;
552
553                         gl = glist = lcons(grpcl, NIL);
554                 }
555                 else
556                 {
557                         List       *i;
558
559                         foreach(i, glist)
560                         {
561                                 GroupClause *gcl = (GroupClause *) lfirst(i);
562
563                                 if (equal(get_groupclause_expr(gcl, targetlist),
564                                                   restarget->expr))
565                                         break;
566                         }
567                         if (i == NIL)           /* not in grouplist already */
568                         {
569                                 int                     groupref = length(glist) + 1;
570
571                                 restarget->resdom->resgroupref = groupref;
572                                 grpcl->tleGroupref = groupref;
573
574                                 lnext(gl) = lcons(grpcl, NIL);
575                                 gl = lnext(gl);
576                         }
577                         else
578                                 pfree(grpcl);   /* get rid of this */
579                 }
580                 grouplist = lnext(grouplist);
581         }
582
583         return glist;
584 }
585
586 /*
587  * transformSortClause -
588  *        transform an Order By clause
589  *
590  */
591 List *
592 transformSortClause(ParseState *pstate,
593                                         List *orderlist,
594                                         List *sortlist,
595                                         List *targetlist,
596                                         char *uniqueFlag)
597 {
598         List       *s = NIL;
599
600         while (orderlist != NIL)
601         {
602                 SortGroupBy *sortby = lfirst(orderlist);
603                 SortClause *sortcl = makeNode(SortClause);
604                 TargetEntry *restarget;
605                 Resdom     *resdom;
606
607                 restarget = findTargetlistEntry(pstate, sortby->node, targetlist, ORDER_CLAUSE);
608
609                 sortcl->resdom = resdom = restarget->resdom;
610
611                 /*
612                  * if we have InvalidOid, then this is a NULL field and don't need
613                  * to sort
614                  */
615                 if (resdom->restype == InvalidOid)
616                         resdom->restype = INT4OID;
617
618                 sortcl->opoid = oprid(oper(sortby->useOp,
619                                                                    resdom->restype,
620                                                                    resdom->restype, false));
621                 if (sortlist == NIL)
622                         s = sortlist = lcons(sortcl, NIL);
623                 else
624                 {
625                         List       *i;
626
627                         foreach(i, sortlist)
628                         {
629                                 SortClause *scl = (SortClause *) lfirst(i);
630
631                                 if (scl->resdom == sortcl->resdom)
632                                         break;
633                         }
634                         if (i == NIL)           /* not in sortlist already */
635                         {
636                                 lnext(s) = lcons(sortcl, NIL);
637                                 s = lnext(s);
638                         }
639                         else
640                                 pfree(sortcl);  /* get rid of this */
641                 }
642                 orderlist = lnext(orderlist);
643         }
644
645         if (uniqueFlag)
646         {
647                 List       *i;
648
649                 if (uniqueFlag[0] == '*')
650                 {
651
652                         /*
653                          * concatenate all elements from target list that are not
654                          * already in the sortby list
655                          */
656                         foreach(i, targetlist)
657                         {
658                                 TargetEntry *tlelt = (TargetEntry *) lfirst(i);
659
660                                 s = sortlist;
661                                 while (s != NIL)
662                                 {
663                                         SortClause *sortcl = lfirst(s);
664
665                                         /*
666                                          * We use equal() here because we are called for UNION
667                                          * from the optimizer, and at that point, the sort
668                                          * clause resdom pointers don't match the target list
669                                          * resdom pointers
670                                          */
671                                         if (equal(sortcl->resdom, tlelt->resdom))
672                                                 break;
673                                         s = lnext(s);
674                                 }
675                                 if (s == NIL)
676                                 {
677                                         /* not a member of the sortclauses yet */
678                                         SortClause *sortcl = makeNode(SortClause);
679
680                                         if (tlelt->resdom->restype == InvalidOid)
681                                                 tlelt->resdom->restype = INT4OID;
682
683                                         sortcl->resdom = tlelt->resdom;
684                                         sortcl->opoid = any_ordering_op(tlelt->resdom->restype);
685
686                                         sortlist = lappend(sortlist, sortcl);
687                                 }
688                         }
689                 }
690                 else
691                 {
692                         TargetEntry *tlelt = NULL;
693                         char       *uniqueAttrName = uniqueFlag;
694
695                         /* only create sort clause with the specified unique attribute */
696                         foreach(i, targetlist)
697                         {
698                                 tlelt = (TargetEntry *) lfirst(i);
699                                 if (strcmp(tlelt->resdom->resname, uniqueAttrName) == 0)
700                                         break;
701                         }
702                         if (i == NIL)
703                                 elog(ERROR, "All fields in the UNIQUE ON clause must appear in the target list");
704
705                         foreach(s, sortlist)
706                         {
707                                 SortClause *sortcl = lfirst(s);
708
709                                 if (sortcl->resdom == tlelt->resdom)
710                                         break;
711                         }
712                         if (s == NIL)
713                         {
714                                 /* not a member of the sortclauses yet */
715                                 SortClause *sortcl = makeNode(SortClause);
716
717                                 sortcl->resdom = tlelt->resdom;
718                                 sortcl->opoid = any_ordering_op(tlelt->resdom->restype);
719
720                                 sortlist = lappend(sortlist, sortcl);
721                         }
722                 }
723         }
724
725         return sortlist;
726 }
727
728 /* transformUnionClause()
729  * Transform a UNION clause.
730  * Note that the union clause is actually a fully-formed select structure.
731  * So, it is evaluated as a select, then the resulting target fields
732  *      are matched up to ensure correct types in the results.
733  * The select clause parsing is done recursively, so the unions are evaluated
734  *      right-to-left. One might want to look at all columns from all clauses before
735  *      trying to coerce, but unless we keep track of the call depth we won't know
736  *      when to do this because of the recursion.
737  * Let's just try matching in pairs for now (right to left) and see if it works.
738  * - thomas 1998-05-22
739  */
740 #ifdef NOT_USED
741 static List *
742 transformUnionClause(List *unionClause, List *targetlist)
743 {
744         List       *union_list = NIL;
745         List       *qlist,
746                            *qlist_item;
747
748         if (unionClause)
749         {
750                 /* recursion */
751                 qlist = parse_analyze(unionClause, NULL);
752
753                 foreach(qlist_item, qlist)
754                 {
755                         Query      *query = (Query *) lfirst(qlist_item);
756                         List       *prev_target = targetlist;
757                         List       *next_target;
758                         int                     prev_len = 0,
759                                                 next_len = 0;
760
761                         foreach(prev_target, targetlist)
762                                 if (!((TargetEntry *) lfirst(prev_target))->resdom->resjunk)
763                                 prev_len++;
764
765                         foreach(next_target, query->targetList)
766                                 if (!((TargetEntry *) lfirst(next_target))->resdom->resjunk)
767                                 next_len++;
768
769                         if (prev_len != next_len)
770                                 elog(ERROR, "Each UNION clause must have the same number of columns");
771
772                         foreach(next_target, query->targetList)
773                         {
774                                 Oid                     itype;
775                                 Oid                     otype;
776
777                                 otype = ((TargetEntry *) lfirst(prev_target))->resdom->restype;
778                                 itype = ((TargetEntry *) lfirst(next_target))->resdom->restype;
779
780                                 /* one or both is a NULL column? then don't convert... */
781                                 if (otype == InvalidOid)
782                                 {
783                                         /* propagate a known type forward, if available */
784                                         if (itype != InvalidOid)
785                                                 ((TargetEntry *) lfirst(prev_target))->resdom->restype = itype;
786 #if FALSE
787                                         else
788                                         {
789                                                 ((TargetEntry *) lfirst(prev_target))->resdom->restype = UNKNOWNOID;
790                                                 ((TargetEntry *) lfirst(next_target))->resdom->restype = UNKNOWNOID;
791                                         }
792 #endif
793                                 }
794                                 else if (itype == InvalidOid)
795                                 {
796                                 }
797                                 /* they don't match in type? then convert... */
798                                 else if (itype != otype)
799                                 {
800                                         Node       *expr;
801
802                                         expr = ((TargetEntry *) lfirst(next_target))->expr;
803                                         expr = CoerceTargetExpr(NULL, expr, itype, otype);
804                                         if (expr == NULL)
805                                         {
806                                                 elog(ERROR, "Unable to transform %s to %s"
807                                                          "\n\tEach UNION clause must have compatible target types",
808                                                          typeidTypeName(itype),
809                                                          typeidTypeName(otype));
810                                         }
811                                         ((TargetEntry *) lfirst(next_target))->expr = expr;
812                                         ((TargetEntry *) lfirst(next_target))->resdom->restype = otype;
813                                 }
814
815                                 /* both are UNKNOWN? then evaluate as text... */
816                                 else if (itype == UNKNOWNOID)
817                                 {
818                                         ((TargetEntry *) lfirst(next_target))->resdom->restype = TEXTOID;
819                                         ((TargetEntry *) lfirst(prev_target))->resdom->restype = TEXTOID;
820                                 }
821                                 prev_target = lnext(prev_target);
822                         }
823                         union_list = lappend(union_list, query);
824                 }
825                 return union_list;
826         }
827         else
828                 return NIL;
829 }
830 #endif