]> granicus.if.org Git - postgresql/blob - src/backend/parser/parse_target.c
First cut at full support for OUTER JOINs. There are still a few loose
[postgresql] / src / backend / parser / parse_target.c
1 /*-------------------------------------------------------------------------
2  *
3  * parse_target.c
4  *        handle target lists
5  *
6  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.62 2000/09/12 21:07:02 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15
16 #include "postgres.h"
17 #include "nodes/makefuncs.h"
18 #include "parser/parsetree.h"
19 #include "parser/parse_coerce.h"
20 #include "parser/parse_expr.h"
21 #include "parser/parse_func.h"
22 #include "parser/parse_relation.h"
23 #include "parser/parse_target.h"
24 #include "parser/parse_type.h"
25
26
27 static List *ExpandAllTables(ParseState *pstate);
28 static char *FigureColname(Node *expr, Node *resval);
29
30
31 /*
32  * transformTargetEntry()
33  *      Transform any ordinary "expression-type" node into a targetlist entry.
34  *      This is exported so that parse_clause.c can generate targetlist entries
35  *      for ORDER/GROUP BY items that are not already in the targetlist.
36  *
37  * node         the (untransformed) parse tree for the value expression.
38  * expr         the transformed expression, or NULL if caller didn't do it yet.
39  * colname      the column name to be assigned, or NULL if none yet set.
40  * resjunk      true if the target should be marked resjunk, ie, it is not
41  *                      wanted in the final projected tuple.
42  */
43 TargetEntry *
44 transformTargetEntry(ParseState *pstate,
45                                          Node *node,
46                                          Node *expr,
47                                          char *colname,
48                                          bool resjunk)
49 {
50         Oid                     type_id;
51         int32           type_mod;
52         Resdom     *resnode;
53
54         /* Transform the node if caller didn't do it already */
55         if (expr == NULL)
56                 expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
57
58         type_id = exprType(expr);
59         type_mod = exprTypmod(expr);
60
61         if (colname == NULL)
62         {
63
64                 /*
65                  * Generate a suitable column name for a column without any
66                  * explicit 'AS ColumnName' clause.
67                  */
68                 colname = FigureColname(expr, node);
69         }
70
71         resnode = makeResdom((AttrNumber) pstate->p_last_resno++,
72                                                  type_id,
73                                                  type_mod,
74                                                  colname,
75                                                  resjunk);
76
77         return makeTargetEntry(resnode, expr);
78 }
79
80
81 /*
82  * transformTargetList()
83  * Turns a list of ResTarget's into a list of TargetEntry's.
84  *
85  * At this point, we don't care whether we are doing SELECT, INSERT,
86  * or UPDATE; we just transform the given expressions.
87  */
88 List *
89 transformTargetList(ParseState *pstate, List *targetlist)
90 {
91         List       *p_target = NIL;
92
93         while (targetlist != NIL)
94         {
95                 ResTarget  *res = (ResTarget *) lfirst(targetlist);
96
97                 if (IsA(res->val, Attr))
98                 {
99                         Attr       *att = (Attr *) res->val;
100
101                         if (att->relname != NULL && strcmp(att->relname, "*") == 0)
102                         {
103
104                                 /*
105                                  * Target item is a single '*', expand all tables (eg.
106                                  * SELECT * FROM emp)
107                                  */
108                                 p_target = nconc(p_target,
109                                                                  ExpandAllTables(pstate));
110                         }
111                         else if (att->attrs != NIL &&
112                                          strcmp(strVal(lfirst(att->attrs)), "*") == 0)
113                         {
114
115                                 /*
116                                  * Target item is relation.*, expand that table (eg.
117                                  * SELECT emp.*, dname FROM emp, dept)
118                                  */
119                                 Node       *rteorjoin;
120                                 int                     sublevels_up;
121
122                                 rteorjoin = refnameRangeOrJoinEntry(pstate, att->relname,
123                                                                                                         &sublevels_up);
124
125                                 if (rteorjoin == NULL)
126                                 {
127                                         rteorjoin = (Node *) addImplicitRTE(pstate, att->relname);
128                                         sublevels_up = 0;
129                                 }
130
131                                 if (IsA(rteorjoin, RangeTblEntry))
132                                         p_target = nconc(p_target,
133                                                                          expandRelAttrs(pstate,
134                                                                                                         (RangeTblEntry *) rteorjoin));
135                                 else if (IsA(rteorjoin, JoinExpr))
136                                         p_target = nconc(p_target,
137                                                                          expandJoinAttrs(pstate,
138                                                                                                          (JoinExpr *) rteorjoin,
139                                                                                                          sublevels_up));
140                                 else
141                                         elog(ERROR, "transformTargetList: unexpected node type %d",
142                                                  nodeTag(rteorjoin));
143                         }
144                         else
145                         {
146                                 /* Plain Attr node, treat it as an expression */
147                                 p_target = lappend(p_target,
148                                                                    transformTargetEntry(pstate,
149                                                                                                                 res->val,
150                                                                                                                 NULL,
151                                                                                                                 res->name,
152                                                                                                                 false));
153                         }
154                 }
155                 else
156                 {
157                         /* Everything else but Attr */
158                         p_target = lappend(p_target,
159                                                            transformTargetEntry(pstate,
160                                                                                                         res->val,
161                                                                                                         NULL,
162                                                                                                         res->name,
163                                                                                                         false));
164                 }
165
166                 targetlist = lnext(targetlist);
167         }
168
169         return p_target;
170 }
171
172
173 /*
174  * updateTargetListEntry()
175  *      This is used in INSERT and UPDATE statements only.      It prepares a
176  *      TargetEntry for assignment to a column of the target table.
177  *      This includes coercing the given value to the target column's type
178  *      (if necessary), and dealing with any subscripts attached to the target
179  *      column itself.
180  *
181  * pstate               parse state
182  * tle                  target list entry to be modified
183  * colname              target column name (ie, name of attribute to be assigned to)
184  * attrno               target attribute number
185  * indirection  subscripts for target column, if any
186  */
187 void
188 updateTargetListEntry(ParseState *pstate,
189                                           TargetEntry *tle,
190                                           char *colname,
191                                           int attrno,
192                                           List *indirection)
193 {
194         Oid                     type_id = exprType(tle->expr);  /* type of value provided */
195         Oid                     attrtype;               /* type of target column */
196         int32           attrtypmod;
197         Resdom     *resnode = tle->resdom;
198         Relation        rd = pstate->p_target_relation;
199
200         Assert(rd != NULL);
201         if (attrno <= 0)
202                 elog(ERROR, "Cannot assign to system attribute '%s'", colname);
203         attrtype = attnumTypeId(rd, attrno);
204         attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod;
205
206         /*
207          * If there are subscripts on the target column, prepare an array
208          * assignment expression.  This will generate an array value that the
209          * source value has been inserted into, which can then be placed in
210          * the new tuple constructed by INSERT or UPDATE. Note that
211          * transformArraySubscripts takes care of type coercion.
212          */
213         if (indirection)
214         {
215                 Attr       *att = makeAttr(pstrdup(RelationGetRelationName(rd)),
216                                                                    colname);
217                 Node       *arrayBase;
218                 ArrayRef   *aref;
219
220                 arrayBase = ParseNestedFuncOrColumn(pstate, att, EXPR_COLUMN_FIRST);
221                 aref = transformArraySubscripts(pstate, arrayBase,
222                                                                                 indirection,
223                                                                                 pstate->p_is_insert,
224                                                                                 tle->expr);
225                 if (pstate->p_is_insert)
226                 {
227
228                         /*
229                          * The command is INSERT INTO table (arraycol[subscripts]) ...
230                          * so there is not really a source array value to work with.
231                          * Let the executor do something reasonable, if it can. Notice
232                          * that we forced transformArraySubscripts to treat the
233                          * subscripting op as an array-slice op above, so the source
234                          * data will have been coerced to array type.
235                          */
236                         aref->refexpr = NULL;           /* signal there is no source array */
237                 }
238                 tle->expr = (Node *) aref;
239         }
240         else
241         {
242
243                 /*
244                  * For normal non-subscripted target column, do type checking and
245                  * coercion.  But accept InvalidOid, which indicates the source is
246                  * a NULL constant.
247                  */
248                 if (type_id != InvalidOid)
249                 {
250                         if (type_id != attrtype)
251                         {
252                                 tle->expr = CoerceTargetExpr(pstate, tle->expr, type_id,
253                                                                                          attrtype, attrtypmod);
254                                 if (tle->expr == NULL)
255                                         elog(ERROR, "Attribute '%s' is of type '%s'"
256                                                  " but expression is of type '%s'"
257                                         "\n\tYou will need to rewrite or cast the expression",
258                                                  colname,
259                                                  typeidTypeName(attrtype),
260                                                  typeidTypeName(type_id));
261                         }
262
263                         /*
264                          * If the target is a fixed-length type, it may need a length
265                          * coercion as well as a type coercion.
266                          */
267                         tle->expr = coerce_type_typmod(pstate, tle->expr,
268                                                                                    attrtype, attrtypmod);
269                 }
270         }
271
272         /*
273          * The result of the target expression should now match the
274          * destination column's type.  Also, reset the resname and resno to
275          * identify the destination column --- rewriter and planner depend on
276          * that!
277          */
278         resnode->restype = attrtype;
279         resnode->restypmod = attrtypmod;
280         resnode->resname = colname;
281         resnode->resno = (AttrNumber) attrno;
282 }
283
284
285 Node *
286 CoerceTargetExpr(ParseState *pstate,
287                                  Node *expr,
288                                  Oid type_id,
289                                  Oid attrtype,
290                                  int32 attrtypmod)
291 {
292         if (can_coerce_type(1, &type_id, &attrtype))
293                 expr = coerce_type(pstate, expr, type_id, attrtype, attrtypmod);
294
295 #ifndef DISABLE_STRING_HACKS
296
297         /*
298          * string hacks to get transparent conversions w/o explicit
299          * conversions
300          */
301         else if ((attrtype == BPCHAROID) || (attrtype == VARCHAROID))
302         {
303                 Oid                     text_id = TEXTOID;
304
305                 if (type_id == TEXTOID)
306                 {
307                 }
308                 else if (can_coerce_type(1, &type_id, &text_id))
309                         expr = coerce_type(pstate, expr, type_id, text_id, attrtypmod);
310                 else
311                         expr = NULL;
312         }
313 #endif
314
315         else
316                 expr = NULL;
317
318         return expr;
319 }
320
321
322 /*
323  * checkInsertTargets -
324  *        generate a list of column names if not supplied or
325  *        test supplied column names to make sure they are in target table.
326  *        Also return an integer list of the columns' attribute numbers.
327  *        (used exclusively for inserts)
328  */
329 List *
330 checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
331 {
332         *attrnos = NIL;
333
334         if (cols == NIL)
335         {
336
337                 /*
338                  * Generate default column list for INSERT.
339                  */
340                 Form_pg_attribute *attr = pstate->p_target_relation->rd_att->attrs;
341                 int                     numcol = pstate->p_target_relation->rd_rel->relnatts;
342                 int                     i;
343
344                 for (i = 0; i < numcol; i++)
345                 {
346                         Ident      *id = makeNode(Ident);
347
348 #ifdef  _DROP_COLUMN_HACK__
349                         if (COLUMN_IS_DROPPED(attr[i]))
350                                 continue;
351 #endif   /* _DROP_COLUMN_HACK__ */
352                         id->name = palloc(NAMEDATALEN);
353                         StrNCpy(id->name, NameStr(attr[i]->attname), NAMEDATALEN);
354                         id->indirection = NIL;
355                         id->isRel = false;
356                         cols = lappend(cols, id);
357                         *attrnos = lappendi(*attrnos, i + 1);
358                 }
359         }
360         else
361         {
362
363                 /*
364                  * Do initial validation of user-supplied INSERT column list.
365                  */
366                 List       *tl;
367
368                 foreach(tl, cols)
369                 {
370                         char       *name = ((Ident *) lfirst(tl))->name;
371                         int                     attrno;
372
373                         /* Lookup column name, elog on failure */
374                         attrno = attnameAttNum(pstate->p_target_relation, name);
375                         /* Check for duplicates */
376                         if (intMember(attrno, *attrnos))
377                                 elog(ERROR, "Attribute '%s' specified more than once", name);
378                         *attrnos = lappendi(*attrnos, attrno);
379                 }
380         }
381
382         return cols;
383 }
384
385 /* ExpandAllTables()
386  * Turns '*' (in the target list) into a list of targetlist entries.
387  *
388  * tlist entries are generated for each relation appearing in the FROM list,
389  * which by now has been expanded into a join tree.
390  */
391 static List *
392 ExpandAllTables(ParseState *pstate)
393 {
394         List       *target = NIL;
395         List       *jt;
396
397         /* SELECT *; */
398         if (pstate->p_jointree == NIL)
399                 elog(ERROR, "Wildcard with no tables specified not allowed");
400
401         foreach(jt, pstate->p_jointree)
402         {
403                 Node       *n = (Node *) lfirst(jt);
404
405                 if (IsA(n, RangeTblRef))
406                 {
407                         RangeTblEntry *rte;
408
409                         rte = rt_fetch(((RangeTblRef *) n)->rtindex,
410                                                    pstate->p_rtable);
411
412                         /*
413                          * Ignore added-on relations that were not listed in the FROM
414                          * clause.
415                          */
416                         if (!rte->inFromCl)
417                                 continue;
418
419                         target = nconc(target, expandRelAttrs(pstate, rte));
420                 }
421                 else if (IsA(n, JoinExpr))
422                 {
423                         /* A newfangled join expression */
424                         JoinExpr   *j = (JoinExpr *) n;
425
426                         /* Currently, a join expr could only have come from FROM. */
427                         target = nconc(target, expandJoinAttrs(pstate, j, 0));
428                 }
429                 else
430                         elog(ERROR, "ExpandAllTables: unexpected node (internal error)"
431                                  "\n\t%s", nodeToString(n));
432         }
433
434         return target;
435 }
436
437 /*
438  * FigureColname -
439  *        if the name of the resulting column is not specified in the target
440  *        list, we have to guess a suitable name.  The SQL spec provides some
441  *        guidance, but not much...
442  *
443  */
444 static char *
445 FigureColname(Node *expr, Node *resval)
446 {
447         /* Some of these are easiest to do with the untransformed node */
448         switch (nodeTag(resval))
449         {
450                         case T_Ident:
451                         return ((Ident *) resval)->name;
452                 case T_Attr:
453                         {
454                                 List       *attrs = ((Attr *) resval)->attrs;
455
456                                 if (attrs)
457                                 {
458                                         while (lnext(attrs) != NIL)
459                                                 attrs = lnext(attrs);
460                                         return strVal(lfirst(attrs));
461                                 }
462                         }
463                         break;
464                 default:
465                         break;
466         }
467         /* Otherwise, work with the transformed node */
468         switch (nodeTag(expr))
469         {
470                 case T_Expr:
471                         if (((Expr *) expr)->opType == FUNC_EXPR && IsA(resval, FuncCall))
472                                 return ((FuncCall *) resval)->funcname;
473                         break;
474                 case T_Aggref:
475                         return ((Aggref *) expr)->aggname;
476                 case T_CaseExpr:
477                         {
478                                 char       *name;
479
480                                 name = FigureColname(((CaseExpr *) expr)->defresult, resval);
481                                 if (strcmp(name, "?column?") == 0)
482                                         name = "case";
483                                 return name;
484                         }
485                         break;
486                 default:
487                         break;
488         }
489
490         return "?column?";
491 }