]> granicus.if.org Git - postgresql/blobdiff - src/backend/parser/parse_target.c
Implement feature of new FE/BE protocol whereby RowDescription identifies
[postgresql] / src / backend / parser / parse_target.c
index 35d064b09808d5c44a8a26afb7a00fb7a9c2c053..10892bc292d73014c9645a36e2b2b25ad30150d9 100644 (file)
  * parse_target.c
  *       handle target lists
  *
- * Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.45 1999/07/17 20:17:26 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.101 2003/05/06 00:20:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-
 #include "postgres.h"
+
+#include "miscadmin.h"
 #include "nodes/makefuncs.h"
+#include "parser/parsetree.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_expr.h"
 #include "parser/parse_func.h"
 #include "parser/parse_relation.h"
 #include "parser/parse_target.h"
+#include "parser/parse_type.h"
 #include "utils/builtins.h"
-#include "utils/lsyscache.h"
-#include "utils/syscache.h"
 
 
+static void markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var);
 static List *ExpandAllTables(ParseState *pstate);
-static char *FigureColname(Node *expr, Node *resval);
-static Node *SizeTargetExpr(ParseState *pstate,
-                          Node *expr,
-                          Oid attrtype,
-                          int32 attrtypmod);
+static char *FigureColname(Node *node);
+static int     FigureColnameInternal(Node *node, char **name);
 
 
-/* MakeTargetEntryIdent()
- * Transforms an Ident Node to a Target Entry
- * Created this function to allow the ORDER/GROUP BY clause to be able
- *     to construct a TargetEntry from an Ident.
- *
- * resjunk = TRUE will hide the target entry in the final result tuple.
- *             daveh@insightdist.com     5/20/98
+/*
+ * transformTargetEntry()
+ *     Transform any ordinary "expression-type" node into a targetlist entry.
+ *     This is exported so that parse_clause.c can generate targetlist entries
+ *     for ORDER/GROUP BY items that are not already in the targetlist.
  *
- * Added more conversion logic to match up types from source to target.
- * - thomas 1998-06-02
+ * node                the (untransformed) parse tree for the value expression.
+ * expr                the transformed expression, or NULL if caller didn't do it yet.
+ * colname     the column name to be assigned, or NULL if none yet set.
+ * resjunk     true if the target should be marked resjunk, ie, it is not
+ *                     wanted in the final projected tuple.
  */
 TargetEntry *
-MakeTargetEntryIdent(ParseState *pstate,
+transformTargetEntry(ParseState *pstate,
                                         Node *node,
-                                        char **resname,
-                                        char *refname,
+                                        Node *expr,
                                         char *colname,
                                         bool resjunk)
 {
-       Node       *expr = NULL;
-       Oid                     attrtype_target;
-       TargetEntry *tent = makeNode(TargetEntry);
+       Oid                     type_id;
+       int32           type_mod;
+       Resdom     *resnode;
 
-       if (pstate->p_is_insert && !resjunk)
-       {
+       /* Transform the node if caller didn't do it already */
+       if (expr == NULL)
+               expr = transformExpr(pstate, node);
 
-               /*
-                * Assign column name of destination column to the new TLE. XXX
-                * this is probably WRONG in INSERT ... SELECT case, since
-                * handling of GROUP BY and so forth probably should use the
-                * source table's names not the destination's names.
-                */
-               if (pstate->p_insert_columns != NIL)
-               {
-                       Ident      *id = lfirst(pstate->p_insert_columns);
+       if (IsA(expr, RangeVar))
+               elog(ERROR, "You can't use relation names alone in the target list, try relation.*.");
 
-                       *resname = id->name;
-                       pstate->p_insert_columns = lnext(pstate->p_insert_columns);
-               }
-               else
-                       elog(ERROR, "INSERT has more expressions than target columns");
-       }
+       type_id = exprType(expr);
+       type_mod = exprTypmod(expr);
 
-       if ((pstate->p_is_insert || pstate->p_is_update) && !resjunk)
+       if (colname == NULL)
        {
-               Oid                     attrtype_id;
-               int                     resdomno_id,
-                                       resdomno_target;
-               RangeTblEntry *rte;
-               char       *target_colname;
-               int32           attrtypmod,
-                                       attrtypmod_target;
-
-               target_colname = *resname;
-
                /*
-                * this looks strange to me, returning an empty TargetEntry bjm
-                * 1998/08/24
+                * Generate a suitable column name for a column without any
+                * explicit 'AS ColumnName' clause.
                 */
-               if (target_colname == NULL || colname == NULL)
-                       return tent;
-
-               if (refname != NULL)
-                       rte = refnameRangeTableEntry(pstate, refname);
-               else
-               {
-                       rte = colnameRangeTableEntry(pstate, colname);
-                       if (rte == (RangeTblEntry *) NULL)
-                               elog(ERROR, "Attribute %s not found", colname);
-                       refname = rte->refname;
-               }
-
-               resdomno_id = get_attnum(rte->relid, colname);
-               attrtype_id = get_atttype(rte->relid, resdomno_id);
-               attrtypmod = get_atttypmod(rte->relid, resdomno_id);
-
-               resdomno_target = attnameAttNum(pstate->p_target_relation, target_colname);
-               attrtype_target = attnumTypeId(pstate->p_target_relation, resdomno_target);
-               attrtypmod_target = get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target);
-
-               if ((attrtype_id != attrtype_target)
-                       || ((attrtypmod_target >= 0) && (attrtypmod_target != attrtypmod)))
-               {
-                       if (can_coerce_type(1, &attrtype_id, &attrtype_target))
-                       {
-                               expr = coerce_type(pstate, node, attrtype_id,
-                                                                  attrtype_target,
-                                                                  get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target));
-                               expr = transformExpr(pstate, expr, EXPR_COLUMN_FIRST);
-                               tent = MakeTargetEntryExpr(pstate, *resname, expr, false, false);
-                               expr = tent->expr;
-                       }
-                       else
-                       {
-                               elog(ERROR, "Unable to convert %s to %s for column %s",
-                                        typeidTypeName(attrtype_id), typeidTypeName(attrtype_target),
-                                        target_colname);
-                       }
-               }
+               colname = FigureColname(node);
        }
 
-       /*
-        * here we want to look for column names only, not relation names
-        * (even though they can be stored in Ident nodes, too)
-        */
-       if (expr == NULL)
-       {
-               char       *name;
-               int32           type_mod;
-
-               name = ((*resname != NULL) ? *resname : colname);
-
-               expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
-
-               attrtype_target = exprType(expr);
-               if (nodeTag(expr) == T_Var)
-                       type_mod = ((Var *) expr)->vartypmod;
-               else
-                       type_mod = -1;
-
-               tent->resdom = makeResdom((AttrNumber) pstate->p_last_resno++,
-                                                                 (Oid) attrtype_target,
-                                                                 type_mod,
-                                                                 name,
-                                                                 (Index) 0,
-                                                                 (Oid) 0,
-                                                                 resjunk);
-               tent->expr = expr;
-       }
+       resnode = makeResdom((AttrNumber) pstate->p_next_resno++,
+                                                type_id,
+                                                type_mod,
+                                                colname,
+                                                resjunk);
 
-       return tent;
-}      /* MakeTargetEntryIdent() */
+       return makeTargetEntry(resnode, (Expr *) expr);
+}
 
 
-/* MakeTargetEntryExpr()
- * Make a TargetEntry from an expression.
- * arrayRef is a list of transformed A_Indices.
- *
- * For type mismatches between expressions and targets, use the same
- *     techniques as for function and operator type coersion.
- * - thomas 1998-05-08
+/*
+ * transformTargetList()
+ * Turns a list of ResTarget's into a list of TargetEntry's.
  *
- * Added resjunk flag and made extern so that it can be use by GROUP/
- * ORDER BY a function or expression not in the target_list
- * -  daveh@insightdist.com 1998-07-31
+ * At this point, we don't care whether we are doing SELECT, INSERT,
+ * or UPDATE; we just transform the given expressions.
  */
-TargetEntry *
-MakeTargetEntryExpr(ParseState *pstate,
-                                       char *colname,
-                                       Node *expr,
-                                       List *arrayRef,
-                                       bool resjunk)
+List *
+transformTargetList(ParseState *pstate, List *targetlist)
 {
-       Oid                     type_id,
-                               attrtype;
-       int32           type_mod,
-                               attrtypmod;
-       int                     resdomno;
-       Relation        rd;
-       bool            attrisset;
-       Resdom     *resnode;
-
-       if (expr == NULL)
-               elog(ERROR, "Invalid use of NULL expression (internal error)");
-
-       type_id = exprType(expr);
-       if (nodeTag(expr) == T_Var)
-               type_mod = ((Var *) expr)->vartypmod;
-       else
-               type_mod = -1;
+       List       *p_target = NIL;
 
-       /* Process target columns that will be receiving results */
-       if ((pstate->p_is_insert || pstate->p_is_update) && !resjunk)
+       while (targetlist != NIL)
        {
+               ResTarget  *res = (ResTarget *) lfirst(targetlist);
 
-               /*
-                * insert or update query -- insert, update work only on one
-                * relation, so multiple occurence of same resdomno is bogus
-                */
-               rd = pstate->p_target_relation;
-               Assert(rd != NULL);
-               resdomno = attnameAttNum(rd, colname);
-               if (resdomno <= 0)
-                       elog(ERROR, "Cannot assign to system attribute '%s'", colname);
-               attrisset = attnameIsSet(rd, colname);
-               attrtype = attnumTypeId(rd, resdomno);
-               if ((arrayRef != NIL) && (lfirst(arrayRef) == NIL))
-                       attrtype = GetArrayElementType(attrtype);
-               attrtypmod = rd->rd_att->attrs[resdomno - 1]->atttypmod;
-
-               /*
-                * Check for InvalidOid since that seems to indicate a NULL
-                * constant...
-                */
-               if (type_id != InvalidOid)
+               if (IsA(res->val, ColumnRef))
                {
-                       /* Mismatch on types? then try to coerce to target...  */
-                       if (attrtype != type_id)
-                       {
-                               Oid                     typelem;
-
-                               if (arrayRef && !(((A_Indices *) lfirst(arrayRef))->lidx))
-                                       typelem = typeTypElem(typeidType(attrtype));
-                               else
-                                       typelem = attrtype;
+                       ColumnRef  *cref = (ColumnRef *) res->val;
+                       List       *fields = cref->fields;
+                       int                     numnames = length(fields);
 
-                               expr = CoerceTargetExpr(pstate, expr, type_id, typelem);
-
-                               if (!HeapTupleIsValid(expr))
-                                       elog(ERROR, "Attribute '%s' is of type '%s'"
-                                                " but expression is of type '%s'"
-                                       "\n\tYou will need to rewrite or cast the expression",
-                                                colname,
-                                                typeidTypeName(attrtype),
-                                                typeidTypeName(type_id));
+                       if (numnames == 1 && strcmp(strVal(lfirst(fields)), "*") == 0)
+                       {
+                               /*
+                                * Target item is a single '*', expand all tables (eg.
+                                * SELECT * FROM emp)
+                                */
+                               p_target = nconc(p_target,
+                                                                ExpandAllTables(pstate));
                        }
-
-                       /*
-                        * Apparently going to a fixed-length string? Then explicitly
-                        * size for storage...
-                        */
-                       if (attrtypmod > 0)
-                               expr = SizeTargetExpr(pstate, expr, attrtype, attrtypmod);
-               }
-
-               if (arrayRef != NIL)
-               {
-                       Expr       *target_expr;
-                       Attr       *att = makeNode(Attr);
-                       List       *ar = arrayRef;
-                       List       *upperIndexpr = NIL;
-                       List       *lowerIndexpr = NIL;
-
-                       att->relname = pstrdup(RelationGetRelationName(rd)->data);
-                       att->attrs = lcons(makeString(colname), NIL);
-                       target_expr = (Expr *) ParseNestedFuncOrColumn(pstate, att,
-                                                                                                  &pstate->p_last_resno,
-                                                                                                         EXPR_COLUMN_FIRST);
-                       while (ar != NIL)
+                       else if (strcmp(strVal(nth(numnames - 1, fields)), "*") == 0)
                        {
-                               A_Indices  *ind = lfirst(ar);
-
-                               if (lowerIndexpr || (!upperIndexpr && ind->lidx))
+                               /*
+                                * Target item is relation.*, expand that table (eg.
+                                * SELECT emp.*, dname FROM emp, dept)
+                                */
+                               char       *schemaname;
+                               char       *relname;
+                               RangeTblEntry *rte;
+                               int                     sublevels_up;
+
+                               switch (numnames)
                                {
+                                       case 2:
+                                               schemaname = NULL;
+                                               relname = strVal(lfirst(fields));
+                                               break;
+                                       case 3:
+                                               schemaname = strVal(lfirst(fields));
+                                               relname = strVal(lsecond(fields));
+                                               break;
+                                       case 4:
+                                               {
+                                                       char       *name1 = strVal(lfirst(fields));
 
-                                       /*
-                                        * XXX assume all lowerIndexpr is non-null in this
-                                        * case
-                                        */
-                                       lowerIndexpr = lappend(lowerIndexpr, ind->lidx);
+                                                       /*
+                                                        * We check the catalog name and then ignore
+                                                        * it.
+                                                        */
+                                                       if (strcmp(name1, DatabaseName) != 0)
+                                                               elog(ERROR, "Cross-database references are not implemented");
+                                                       schemaname = strVal(lsecond(fields));
+                                                       relname = strVal(lthird(fields));
+                                                       break;
+                                               }
+                                       default:
+                                               elog(ERROR, "Invalid qualified name syntax (too many names)");
+                                               schemaname = NULL;              /* keep compiler quiet */
+                                               relname = NULL;
+                                               break;
                                }
-                               upperIndexpr = lappend(upperIndexpr, ind->uidx);
-                               ar = lnext(ar);
-                       }
-
-                       expr = (Node *) make_array_set(target_expr,
-                                                                                  upperIndexpr,
-                                                                                  lowerIndexpr,
-                                                                                  (Expr *) expr);
-                       attrtype = attnumTypeId(rd, resdomno);
-                       attrtypmod = get_atttypmod(RelationGetRelid(rd), resdomno);
-               }
-       }
-       else
-       {
-               resdomno = pstate->p_last_resno++;
-               attrtype = type_id;
-               attrtypmod = type_mod;
-       }
-
-       resnode = makeResdom((AttrNumber) resdomno,
-                                                (Oid) attrtype,
-                                                attrtypmod,
-                                                colname,
-                                                (Index) 0,
-                                                (Oid) 0,
-                                                resjunk);
-
-       return makeTargetEntry(resnode, expr);
-}      /* MakeTargetEntryExpr() */
-
-/*
- *     MakeTargetEntryCase()
- *     Make a TargetEntry from a case node.
- */
-static TargetEntry *
-MakeTargetEntryCase(ParseState *pstate,
-                                       ResTarget *res)
-{
-       TargetEntry *tent;
-       CaseExpr   *expr;
-       Resdom     *resnode;
-       int                     resdomno;
-       Oid                     type_id;
-       int32           type_mod;
-
-       expr = (CaseExpr *) transformExpr(pstate, (Node *) res->val, EXPR_COLUMN_FIRST);
-
-       type_id = expr->casetype;
-       type_mod = -1;
-       handleTargetColname(pstate, &res->name, NULL, NULL);
-       if (res->name == NULL)
-               res->name = FigureColname((Node *) expr, res->val);
-
-       resdomno = pstate->p_last_resno++;
-       resnode = makeResdom((AttrNumber) resdomno,
-                                                (Oid) type_id,
-                                                type_mod,
-                                                res->name,
-                                                (Index) 0,
-                                                (Oid) 0,
-                                                false);
-
-       tent = makeNode(TargetEntry);
-       tent->resdom = resnode;
-       tent->expr = (Node *) expr;
-
-       return tent;
-}      /* MakeTargetEntryCase() */
-
-/*
- *     MakeTargetEntryComplex()
- *     Make a TargetEntry from a complex node.
- */
-static TargetEntry *
-MakeTargetEntryComplex(ParseState *pstate,
-                                          ResTarget *res)
-{
-       Node       *expr = transformExpr(pstate, (Node *) res->val, EXPR_COLUMN_FIRST);
-
-       handleTargetColname(pstate, &res->name, NULL, NULL);
-       /* note indirection has not been transformed */
-       if (pstate->p_is_insert && res->indirection != NIL)
-       {
-               /* this is an array assignment */
-               char       *val;
-               char       *str,
-                                  *save_str;
-               List       *elt;
-               int                     i = 0,
-                                       ndims;
-               int                     lindx[MAXDIM],
-                                       uindx[MAXDIM];
-               int                     resdomno;
-               Relation        rd;
-               Value      *constval;
-
-               if (exprType(expr) != UNKNOWNOID || !IsA(expr, Const))
-                       elog(ERROR, "String constant expected (internal error)");
-
-               val = (char *) textout((struct varlena *)
-                                                          ((Const *) expr)->constvalue);
-               str = save_str = (char *) palloc(strlen(val) + MAXDIM * 25 + 2);
-               foreach(elt, res->indirection)
-               {
-                       A_Indices  *aind = (A_Indices *) lfirst(elt);
 
-                       aind->uidx = transformExpr(pstate, aind->uidx, EXPR_COLUMN_FIRST);
-                       if (!IsA(aind->uidx, Const))
-                               elog(ERROR, "Array Index for Append should be a constant");
+                               rte = refnameRangeTblEntry(pstate, schemaname, relname,
+                                                                                  &sublevels_up);
+                               if (rte == NULL)
+                                       rte = addImplicitRTE(pstate, makeRangeVar(schemaname,
+                                                                                                                         relname));
 
-                       uindx[i] = ((Const *) aind->uidx)->constvalue;
-                       if (aind->lidx != NULL)
-                       {
-                               aind->lidx = transformExpr(pstate, aind->lidx, EXPR_COLUMN_FIRST);
-                               if (!IsA(aind->lidx, Const))
-                                       elog(ERROR, "Array Index for Append should be a constant");
-
-                               lindx[i] = ((Const *) aind->lidx)->constvalue;
+                               p_target = nconc(p_target,
+                                                                expandRelAttrs(pstate, rte));
                        }
                        else
-                               lindx[i] = 1;
-                       if (lindx[i] > uindx[i])
-                               elog(ERROR, "Lower index cannot be greater than upper index");
-
-                       sprintf(str, "[%d:%d]", lindx[i], uindx[i]);
-                       str += strlen(str);
-                       i++;
+                       {
+                               /* Plain ColumnRef node, treat it as an expression */
+                               p_target = lappend(p_target,
+                                                                  transformTargetEntry(pstate,
+                                                                                                               res->val,
+                                                                                                               NULL,
+                                                                                                               res->name,
+                                                                                                               false));
+                       }
                }
-               sprintf(str, "=%s", val);
-               rd = pstate->p_target_relation;
-               Assert(rd != NULL);
-               resdomno = attnameAttNum(rd, res->name);
-               ndims = attnumAttNelems(rd, resdomno);
-               if (i != ndims)
-                       elog(ERROR, "Array dimensions do not match");
-
-               constval = makeNode(Value);
-               constval->type = T_String;
-               constval->val.str = save_str;
-               return MakeTargetEntryExpr(pstate, res->name,
-                                                                  (Node *) make_const(constval),
-                                                                  NULL, false);
-               pfree(save_str);
-       }
-       else
-       {
-               /* this is not an array assignment */
-               char       *colname = res->name;
-
-               if (colname == NULL)
+               else if (IsA(res->val, InsertDefault))
                {
+                       InsertDefault *newnode = makeNode(InsertDefault);
 
                        /*
-                        * if you're wondering why this is here, look at the yacc
-                        * grammar for why a name can be missing. -ay
+                        * If this is a DEFAULT element, we make a junk entry which
+                        * will get dropped on return to transformInsertStmt().
                         */
-                       colname = FigureColname(expr, res->val);
+                       p_target = lappend(p_target, newnode);
                }
-               if (res->indirection)
+               else
                {
-                       List       *ilist = res->indirection;
-
-                       while (ilist != NIL)
-                       {
-                               A_Indices  *ind = lfirst(ilist);
-
-                               ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST);
-                               ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST);
-                               ilist = lnext(ilist);
-                       }
+                       /* Everything else but ColumnRef and InsertDefault */
+                       p_target = lappend(p_target,
+                                                          transformTargetEntry(pstate,
+                                                                                                       res->val,
+                                                                                                       NULL,
+                                                                                                       res->name,
+                                                                                                       false));
                }
-               res->name = colname;
-               return MakeTargetEntryExpr(pstate, res->name, expr,
-                                                                  res->indirection, false);
+
+               targetlist = lnext(targetlist);
        }
+
+       return p_target;
 }
 
+
 /*
- *     MakeTargetEntryAttr()
- *     Make a TargetEntry from a complex node.
+ * markTargetListOrigins()
+ *             Mark targetlist columns that are simple Vars with the source
+ *             table's OID and column number.
+ *
+ * Currently, this is done only for SELECT targetlists, since we only
+ * need the info if we are going to send it to the frontend.
  */
-static TargetEntry *
-MakeTargetEntryAttr(ParseState *pstate,
-                                       ResTarget *res)
+void
+markTargetListOrigins(ParseState *pstate, List *targetlist)
 {
-       Oid                     type_id;
-       int32           type_mod;
-       Attr       *att = (Attr *) res->val;
-       Node       *result;
-       char       *attrname;
-       char       *resname;
-       Resdom     *resnode;
-       int                     resdomno;
-       List       *attrs = att->attrs;
-       TargetEntry *tent;
+       List       *l;
 
-       attrname = strVal(lfirst(att->attrs));
-
-       /*
-        * Target item is fully specified: ie. relation.attribute
-        */
-       result = ParseNestedFuncOrColumn(pstate, att, &pstate->p_last_resno, EXPR_COLUMN_FIRST);
-       handleTargetColname(pstate, &res->name, att->relname, attrname);
-       if (att->indirection != NIL)
-       {
-               List       *ilist = att->indirection;
-
-               while (ilist != NIL)
-               {
-                       A_Indices  *ind = lfirst(ilist);
-
-                       ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST);
-                       ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST);
-                       ilist = lnext(ilist);
-               }
-               result = (Node *) make_array_ref(result, att->indirection);
-       }
-       type_id = exprType(result);
-       if (nodeTag(result) == T_Var)
-               type_mod = ((Var *) result)->vartypmod;
-       else
-               type_mod = -1;
-       /* move to last entry */
-       while (lnext(attrs) != NIL)
-               attrs = lnext(attrs);
-       resname = (res->name) ? res->name : strVal(lfirst(attrs));
-       if (pstate->p_is_insert || pstate->p_is_update)
+       foreach(l, targetlist)
        {
-               Relation        rd;
+               TargetEntry *tle = (TargetEntry *) lfirst(l);
 
-               /*
-                * insert or update query -- insert, update work only on one
-                * relation, so multiple occurence of same resdomno is bogus
-                */
-               rd = pstate->p_target_relation;
-               Assert(rd != NULL);
-               resdomno = attnameAttNum(rd, res->name);
+               markTargetListOrigin(pstate, tle->resdom, (Var *) tle->expr);
        }
-       else
-               resdomno = pstate->p_last_resno++;
-       resnode = makeResdom((AttrNumber) resdomno,
-                                                (Oid) type_id,
-                                                type_mod,
-                                                resname,
-                                                (Index) 0,
-                                                (Oid) 0,
-                                                false);
-       tent = makeNode(TargetEntry);
-       tent->resdom = resnode;
-       tent->expr = result;
-       return tent;
 }
 
-
-/* transformTargetList()
- * Turns a list of ResTarget's into a list of TargetEntry's.
+/*
+ * markTargetListOrigin()
+ *             If 'var' is a Var of a plain relation, mark 'res' with its origin
+ *
+ * This is split out so it can recurse for join references.  Note that we
+ * do not drill down into views, but report the view as the column owner.
  */
-List *
-transformTargetList(ParseState *pstate, List *targetlist)
+static void
+markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var)
 {
-       List       *p_target = NIL;
-       List       *tail_p_target = NIL;
+       RangeTblEntry *rte;
+       AttrNumber      attnum;
 
-       while (targetlist != NIL)
-       {
-               ResTarget  *res = (ResTarget *) lfirst(targetlist);
-               TargetEntry *tent = NULL;
+       if (var == NULL || !IsA(var, Var))
+               return;
+       Assert(var->varno > 0 &&
+                  (int) var->varno <= length(pstate->p_rtable));
+       rte = rt_fetch(var->varno, pstate->p_rtable);
+       attnum = var->varattno;
 
-               switch (nodeTag(res->val))
-               {
-                       case T_Ident:
-                               {
-                                       char       *identname;
+       switch (rte->rtekind)
+       {
+               case RTE_RELATION:
+                       /* It's a table or view, report it */
+                       res->resorigtbl = rte->relid;
+                       res->resorigcol = attnum;
+                       break;
+               case RTE_SUBQUERY:
+                       {
+                               /* Subselect-in-FROM: copy up from the subselect */
+                               List       *subtl;
 
-                                       identname = ((Ident *) res->val)->name;
-                                       tent = MakeTargetEntryIdent(pstate,
-                                                                                               (Node *) res->val, &res->name, NULL, identname, false);
-                                       break;
-                               }
-                       case T_ParamNo:
-                       case T_FuncCall:
-                       case T_A_Const:
-                       case T_A_Expr:
-                               {
-                                       tent = MakeTargetEntryComplex(pstate, res);
-                                       break;
-                               }
-                       case T_CaseExpr:
+                               foreach(subtl, rte->subquery->targetList)
                                {
-                                       tent = MakeTargetEntryCase(pstate, res);
-                                       break;
-                               }
-                       case T_Attr:
-                               {
-                                       bool            expand_star = false;
-                                       char       *attrname;
-                                       Attr       *att = (Attr *) res->val;
-
-                                       /*
-                                        * Target item is a single '*', expand all tables (eg.
-                                        * SELECT * FROM emp)
-                                        */
-                                       if (att->relname != NULL && !strcmp(att->relname, "*"))
-                                       {
-                                               if (tail_p_target == NIL)
-                                                       p_target = tail_p_target = ExpandAllTables(pstate);
-                                               else
-                                                       lnext(tail_p_target) = ExpandAllTables(pstate);
-                                               expand_star = true;
-                                       }
-                                       else
-                                       {
+                                       TargetEntry *subte = (TargetEntry *) lfirst(subtl);
 
-                                               /*
-                                                * Target item is relation.*, expand the table
-                                                * (eg. SELECT emp.*, dname FROM emp, dept)
-                                                */
-                                               attrname = strVal(lfirst(att->attrs));
-                                               if (att->attrs != NIL && !strcmp(attrname, "*"))
-                                               {
-
-                                                       /*
-                                                        * tail_p_target is the target list we're
-                                                        * building in the while loop. Make sure we
-                                                        * fix it after appending more nodes.
-                                                        */
-                                                       if (tail_p_target == NIL)
-                                                               p_target = tail_p_target = expandAll(pstate, att->relname,
-                                                                       att->relname, &pstate->p_last_resno);
-                                                       else
-                                                               lnext(tail_p_target) = expandAll(pstate, att->relname, att->relname,
-                                                                                                 &pstate->p_last_resno);
-                                                       expand_star = true;
-                                               }
-                                       }
-                                       if (expand_star)
-                                       {
-                                               while (lnext(tail_p_target) != NIL)
-                                                       /* make sure we point to the last target entry */
-                                                       tail_p_target = lnext(tail_p_target);
-
-                                               /*
-                                                * skip rest of while loop
-                                                */
-                                               targetlist = lnext(targetlist);
+                                       if (subte->resdom->resjunk ||
+                                               subte->resdom->resno != attnum)
                                                continue;
-                                       }
-                                       else
-                                       {
-                                               tent = MakeTargetEntryAttr(pstate, res);
-                                               break;
-                                       }
+                                       res->resorigtbl = subte->resdom->resorigtbl;
+                                       res->resorigcol = subte->resdom->resorigcol;
+                                       break;
                                }
-                       default:
-                               /* internal error */
-                               elog(ERROR, "Unable to transform targetlist (internal error)");
-                               break;
-               }
+                               /* falling off end of list shouldn't happen... */
+                               if (subtl == NIL)
+                                       elog(ERROR, "Subquery %s does not have attribute %d",
+                                                rte->eref->aliasname, attnum);
+                       }
+                       break;
+               case RTE_JOIN:
+                       {
+                               /* Join RTE --- recursively inspect the alias variable */
+                               Var        *aliasvar;
 
-               if (p_target == NIL)
-                       p_target = tail_p_target = lcons(tent, NIL);
-               else
-               {
-                       lnext(tail_p_target) = lcons(tent, NIL);
-                       tail_p_target = lnext(tail_p_target);
-               }
-               targetlist = lnext(targetlist);
+                               Assert(attnum > 0 && attnum <= length(rte->joinaliasvars));
+                               aliasvar = (Var *) nth(attnum - 1, rte->joinaliasvars);
+                               markTargetListOrigin(pstate, res, aliasvar);
+                       }
+                       break;
+               case RTE_SPECIAL:
+               case RTE_FUNCTION:
+                       /* not a simple relation, leave it unmarked */
+                       break;
        }
-
-       return p_target;
-}      /* transformTargetList() */
+}
 
 
-Node *
-CoerceTargetExpr(ParseState *pstate,
-                                Node *expr,
-                                Oid type_id,
-                                Oid attrtype)
+/*
+ * updateTargetListEntry()
+ *     This is used in INSERT and UPDATE statements only.      It prepares a
+ *     TargetEntry for assignment to a column of the target table.
+ *     This includes coercing the given value to the target column's type
+ *     (if necessary), and dealing with any subscripts attached to the target
+ *     column itself.
+ *
+ * pstate              parse state
+ * tle                 target list entry to be modified
+ * colname             target column name (ie, name of attribute to be assigned to)
+ * attrno              target attribute number
+ * indirection subscripts for target column, if any
+ */
+void
+updateTargetListEntry(ParseState *pstate,
+                                         TargetEntry *tle,
+                                         char *colname,
+                                         int attrno,
+                                         List *indirection)
 {
-       if (can_coerce_type(1, &type_id, &attrtype))
-               expr = coerce_type(pstate, expr, type_id, attrtype, -1);
-
-#ifndef DISABLE_STRING_HACKS
+       Oid                     type_id = exprType((Node *) tle->expr); /* type of value provided */
+       Oid                     attrtype;               /* type of target column */
+       int32           attrtypmod;
+       Resdom     *resnode = tle->resdom;
+       Relation        rd = pstate->p_target_relation;
+
+       Assert(rd != NULL);
+       if (attrno <= 0)
+               elog(ERROR, "Cannot assign to system attribute '%s'", colname);
+       attrtype = attnumTypeId(rd, attrno);
+       attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod;
 
        /*
-        * string hacks to get transparent conversions w/o explicit
-        * conversions
+        * If there are subscripts on the target column, prepare an array
+        * assignment expression.  This will generate an array value that the
+        * source value has been inserted into, which can then be placed in
+        * the new tuple constructed by INSERT or UPDATE. Note that
+        * transformArraySubscripts takes care of type coercion.
         */
-       else if ((attrtype == BPCHAROID) || (attrtype == VARCHAROID))
+       if (indirection)
        {
-               Oid                     text_id = TEXTOID;
+               Node       *arrayBase;
+               ArrayRef   *aref;
 
-               if (type_id == TEXTOID)
+               if (pstate->p_is_insert)
                {
+                       /*
+                        * The command is INSERT INTO table (arraycol[subscripts]) ...
+                        * so there is not really a source array value to work with.
+                        * Let the executor do something reasonable, if it can. Notice
+                        * that we force transformArraySubscripts to treat the
+                        * subscripting op as an array-slice op below, so the source
+                        * data will have been coerced to the array type.
+                        */
+                       arrayBase = NULL;       /* signal there is no source array */
                }
-               else if (can_coerce_type(1, &type_id, &text_id))
-                       expr = coerce_type(pstate, expr, type_id, text_id, -1);
                else
-                       expr = NULL;
-       }
-#endif
+               {
+                       /*
+                        * Build a Var for the array to be updated.
+                        */
+                       arrayBase = (Node *) make_var(pstate,
+                                                                                 pstate->p_target_rangetblentry,
+                                                                                 attrno);
+               }
 
+               aref = transformArraySubscripts(pstate,
+                                                                               arrayBase,
+                                                                               attrtype,
+                                                                               attrtypmod,
+                                                                               indirection,
+                                                                               pstate->p_is_insert,
+                                                                               (Node *) tle->expr);
+               tle->expr = (Expr *) aref;
+       }
        else
-               expr = NULL;
-
-       return expr;
-}      /* CoerceTargetExpr() */
-
-
-/* SizeTargetExpr()
- * Apparently going to a fixed-length string?
- * Then explicitly size for storage...
- */
-static Node *
-SizeTargetExpr(ParseState *pstate,
-                          Node *expr,
-                          Oid attrtype,
-                          int32 attrtypmod)
-{
-       int                     i;
-       HeapTuple       ftup;
-       char       *funcname;
-       Oid                     oid_array[MAXFARGS];
-
-       FuncCall   *func;
-       A_Const    *cons;
-
-       funcname = typeidTypeName(attrtype);
-       oid_array[0] = attrtype;
-       oid_array[1] = INT4OID;
-       for (i = 2; i < MAXFARGS; i++)
-               oid_array[i] = InvalidOid;
-
-       /* attempt to find with arguments exactly as specified... */
-       ftup = SearchSysCacheTuple(PRONAME,
-                                                          PointerGetDatum(funcname),
-                                                          Int32GetDatum(2),
-                                                          PointerGetDatum(oid_array),
-                                                          0);
-
-       if (HeapTupleIsValid(ftup))
        {
-               func = makeNode(FuncCall);
-               func->funcname = funcname;
-
-               cons = makeNode(A_Const);
-               cons->val.type = T_Integer;
-               cons->val.val.ival = attrtypmod;
-               func->args = lappend(lcons(expr, NIL), cons);
-
-               expr = transformExpr(pstate, (Node *) func, EXPR_COLUMN_FIRST);
+               /*
+                * For normal non-subscripted target column, do type checking and
+                * coercion.  But accept InvalidOid, which indicates the source is
+                * a NULL constant.  (XXX is that still true?)
+                */
+               if (type_id != InvalidOid)
+               {
+                       tle->expr = (Expr *)
+                               coerce_to_target_type(pstate,
+                                                                         (Node *) tle->expr, type_id,
+                                                                         attrtype, attrtypmod,
+                                                                         COERCION_ASSIGNMENT,
+                                                                         COERCE_IMPLICIT_CAST);
+                       if (tle->expr == NULL)
+                               elog(ERROR, "column \"%s\" is of type %s"
+                                        " but expression is of type %s"
+                                        "\n\tYou will need to rewrite or cast the expression",
+                                        colname,
+                                        format_type_be(attrtype),
+                                        format_type_be(type_id));
+               }
        }
 
-       return expr;
-}      /* SizeTargetExpr() */
+       /*
+        * The result of the target expression should now match the
+        * destination column's type.  Also, reset the resname and resno to
+        * identify the destination column --- rewriter and planner depend on
+        * that!
+        */
+       resnode->restype = attrtype;
+       resnode->restypmod = attrtypmod;
+       resnode->resname = colname;
+       resnode->resno = (AttrNumber) attrno;
+}
 
 
 /*
- * makeTargetNames -
- *       generate a list of column names if not supplied or
- *       test supplied column names to make sure they are in target table
- *       (used exclusively for inserts)
+ * checkInsertTargets -
+ *       generate a list of INSERT column targets if not supplied, or
+ *       test supplied column names to make sure they are in target table.
+ *       Also return an integer list of the columns' attribute numbers.
  */
 List *
-makeTargetNames(ParseState *pstate, List *cols)
+checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
 {
-       List       *tl = NULL;
-
-       /* Generate ResTarget if not supplied */
+       *attrnos = NIL;
 
        if (cols == NIL)
        {
-               int                     numcol;
-               int                     i;
+               /*
+                * Generate default column list for INSERT.
+                */
                Form_pg_attribute *attr = pstate->p_target_relation->rd_att->attrs;
+               int                     numcol = pstate->p_target_relation->rd_rel->relnatts;
+               int                     i;
 
-               numcol = pstate->p_target_relation->rd_rel->relnatts;
                for (i = 0; i < numcol; i++)
                {
-                       Ident      *id = makeNode(Ident);
-
-                       id->name = palloc(NAMEDATALEN);
-                       StrNCpy(id->name, attr[i]->attname.data, NAMEDATALEN);
-                       id->indirection = NIL;
-                       id->isRel = false;
-                       if (tl == NIL)
-                               cols = tl = lcons(id, NIL);
-                       else
-                       {
-                               lnext(tl) = lcons(id, NIL);
-                               tl = lnext(tl);
-                       }
+                       ResTarget  *col;
+
+                       if (attr[i]->attisdropped)
+                               continue;
+
+                       col = makeNode(ResTarget);
+                       col->name = pstrdup(NameStr(attr[i]->attname));
+                       col->indirection = NIL;
+                       col->val = NULL;
+                       cols = lappend(cols, col);
+                       *attrnos = lappendi(*attrnos, i + 1);
                }
        }
        else
        {
+               /*
+                * Do initial validation of user-supplied INSERT column list.
+                */
+               List       *tl;
+
                foreach(tl, cols)
                {
-                       List       *nxt;
-                       char       *name = ((Ident *) lfirst(tl))->name;
-
-                       /* elog on failure */
-                       attnameAttNum(pstate->p_target_relation, name);
-                       foreach(nxt, lnext(tl))
-                               if (!strcmp(name, ((Ident *) lfirst(nxt))->name))
-                               elog(ERROR, "Attribute '%s' should be specified only once", name);
+                       char       *name = ((ResTarget *) lfirst(tl))->name;
+                       int                     attrno;
+
+                       /* Lookup column name, elog on failure */
+                       attrno = attnameAttNum(pstate->p_target_relation, name, false);
+                       /* Check for duplicates */
+                       if (intMember(attrno, *attrnos))
+                               elog(ERROR, "Attribute '%s' specified more than once", name);
+                       *attrnos = lappendi(*attrnos, attrno);
                }
        }
 
        return cols;
 }
 
-/*
- * ExpandAllTables -
- *       turns '*' (in the target list) into a list of attributes
- *        (of all relations in the range table)
+/* ExpandAllTables()
+ * Turns '*' (in the target list) into a list of targetlist entries.
+ *
+ * tlist entries are generated for each relation appearing at the top level
+ * of the query's namespace, except for RTEs marked not inFromCl.  (These
+ * may include NEW/OLD pseudo-entries, implicit RTEs, etc.)
  */
 static List *
 ExpandAllTables(ParseState *pstate)
 {
        List       *target = NIL;
-       List       *legit_rtable = NIL;
-       List       *rt,
-                          *rtable;
+       bool            found_table = false;
+       List       *ns;
 
-       rtable = pstate->p_rtable;
-       if (pstate->p_is_rule)
+       foreach(ns, pstate->p_namespace)
        {
+               Node       *n = (Node *) lfirst(ns);
+               RangeTblEntry *rte;
 
-               /*
-                * skip first two entries, "*new*" and "*current*"
-                */
-               rtable = lnext(lnext(pstate->p_rtable));
-       }
-
-       /* SELECT *; */
-       if (rtable == NULL)
-               elog(ERROR, "Wildcard with no tables specified.");
-
-       /*
-        * go through the range table and make a list of range table entries
-        * which we will expand.
-        */
-       foreach(rt, rtable)
-       {
-               RangeTblEntry *rte = lfirst(rt);
+               if (IsA(n, RangeTblRef))
+                       rte = rt_fetch(((RangeTblRef *) n)->rtindex,
+                                                  pstate->p_rtable);
+               else if (IsA(n, JoinExpr))
+                       rte = rt_fetch(((JoinExpr *) n)->rtindex,
+                                                  pstate->p_rtable);
+               else
+               {
+                       elog(ERROR, "ExpandAllTables: unexpected node (internal error)"
+                                "\n\t%s", nodeToString(n));
+                       rte = NULL;                     /* keep compiler quiet */
+               }
 
                /*
-                * we only expand those specify in the from clause. (This will
-                * also prevent us from using the wrong table in inserts: eg.
-                * tenk2 in "insert into tenk2 select * from tenk1;")
+                * Ignore added-on relations that were not listed in the FROM
+                * clause.
                 */
                if (!rte->inFromCl)
                        continue;
-               legit_rtable = lappend(legit_rtable, rte);
+
+               found_table = true;
+               target = nconc(target, expandRelAttrs(pstate, rte));
        }
 
-       foreach(rt, legit_rtable)
-       {
-               RangeTblEntry *rte = lfirst(rt);
-               List       *temp = target;
+       /* Check for SELECT *; */
+       if (!found_table)
+               elog(ERROR, "Wildcard with no tables specified not allowed");
 
-               if (temp == NIL)
-                       target = expandAll(pstate, rte->relname, rte->refname,
-                                                          &pstate->p_last_resno);
-               else
-               {
-                       while (temp != NIL && lnext(temp) != NIL)
-                               temp = lnext(temp);
-                       lnext(temp) = expandAll(pstate, rte->relname, rte->refname,
-                                                                       &pstate->p_last_resno);
-               }
-       }
        return target;
 }
 
 /*
  * FigureColname -
  *       if the name of the resulting column is not specified in the target
- *       list, we have to guess.
+ *       list, we have to guess a suitable name.  The SQL spec provides some
+ *       guidance, but not much...
  *
+ * Note that the argument is the *untransformed* parse tree for the target
+ * item.  This is a shade easier to work with than the transformed tree.
  */
 static char *
-FigureColname(Node *expr, Node *resval)
+FigureColname(Node *node)
+{
+       char       *name = NULL;
+
+       FigureColnameInternal(node, &name);
+       if (name != NULL)
+               return name;
+       /* default result if we can't guess anything */
+       return "?column?";
+}
+
+static int
+FigureColnameInternal(Node *node, char **name)
 {
-       switch (nodeTag(expr))
+       int                     strength = 0;
+
+       if (node == NULL)
+               return strength;
+
+       switch (nodeTag(node))
        {
-                       case T_Aggref:
-                       return (char *) ((Aggref *) expr)->aggname;
-               case T_Expr:
-                       if (((Expr *) expr)->opType == FUNC_EXPR)
+               case T_ColumnRef:
                        {
-                               if (nodeTag(resval) == T_FuncCall)
-                                       return ((FuncCall *) resval)->funcname;
+                               char       *cname = strVal(llast(((ColumnRef *) node)->fields));
+
+                               if (strcmp(cname, "*") != 0)
+                               {
+                                       *name = cname;
+                                       return 2;
+                               }
                        }
                        break;
-               case T_CaseExpr:
+               case T_ExprFieldSelect:
                        {
-                               char       *name;
+                               ExprFieldSelect *efs = (ExprFieldSelect *) node;
+
+                               if (efs->fields)
+                               {
+                                       char       *fname = strVal(llast(efs->fields));
 
-                               name = FigureColname(((CaseExpr *) expr)->defresult, resval);
-                               if (!strcmp(name, "?column?"))
-                                       name = "case";
-                               return name;
+                                       if (strcmp(fname, "*") != 0)
+                                       {
+                                               *name = fname;
+                                               return 2;
+                                       }
+                               }
+                               return FigureColnameInternal(efs->arg, name);
+                       }
+                       break;
+               case T_FuncCall:
+                       *name = strVal(llast(((FuncCall *) node)->funcname));
+                       return 2;
+               case T_A_Expr:
+                       /* make nullif() act like a regular function */
+                       if (((A_Expr *) node)->kind == AEXPR_NULLIF)
+                       {
+                               *name = "nullif";
+                               return 2;
+                       }
+                       break;
+               case T_A_Const:
+                       if (((A_Const *) node)->typename != NULL)
+                       {
+                               *name = strVal(llast(((A_Const *) node)->typename->names));
+                               return 1;
+                       }
+                       break;
+               case T_TypeCast:
+                       strength = FigureColnameInternal(((TypeCast *) node)->arg,
+                                                                                        name);
+                       if (strength <= 1)
+                       {
+                               if (((TypeCast *) node)->typename != NULL)
+                               {
+                                       *name = strVal(llast(((TypeCast *) node)->typename->names));
+                                       return 1;
+                               }
                        }
                        break;
+               case T_CaseExpr:
+                       strength = FigureColnameInternal((Node *) ((CaseExpr *) node)->defresult,
+                                                                                        name);
+                       if (strength <= 1)
+                       {
+                               *name = "case";
+                               return 1;
+                       }
+                       break;
+               case T_ArrayExpr:
+                       /* make ARRAY[] act like a function */
+                       *name = "array";
+                       return 2;
+               case T_CoalesceExpr:
+                       /* make coalesce() act like a regular function */
+                       *name = "coalesce";
+                       return 2;
                default:
                        break;
        }
 
-       return "?column?";
+       return strength;
 }