]> 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 271645573e0f04401c259136a592d5312481d93b..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.8 1998/02/10 04:01:57 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.101 2003/05/06 00:20:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
 #include "postgres.h"
-#include "catalog/pg_type.h"
+
+#include "miscadmin.h"
 #include "nodes/makefuncs.h"
-#include "nodes/primnodes.h"
+#include "parser/parsetree.h"
+#include "parser/parse_coerce.h"
 #include "parser/parse_expr.h"
 #include "parser/parse_func.h"
-#include "parser/parse_node.h"
 #include "parser/parse_relation.h"
 #include "parser/parse_target.h"
+#include "parser/parse_type.h"
 #include "utils/builtins.h"
-#include "utils/lsyscache.h"
 
-static List *expandAllTables(ParseState *pstate);
-static char *figureColname(Node *expr, Node *resval);
-static TargetEntry *make_targetlist_expr(ParseState *pstate,
-                                        char *colname,
+
+static void markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var);
+static List *ExpandAllTables(ParseState *pstate);
+static char *FigureColname(Node *node);
+static int     FigureColnameInternal(Node *node, char **name);
+
+
+/*
+ * 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.
+ *
+ * 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 *
+transformTargetEntry(ParseState *pstate,
+                                        Node *node,
                                         Node *expr,
-                                        List *arrayRef);
+                                        char *colname,
+                                        bool resjunk)
+{
+       Oid                     type_id;
+       int32           type_mod;
+       Resdom     *resnode;
+
+       /* Transform the node if caller didn't do it already */
+       if (expr == NULL)
+               expr = transformExpr(pstate, node);
+
+       if (IsA(expr, RangeVar))
+               elog(ERROR, "You can't use relation names alone in the target list, try relation.*.");
+
+       type_id = exprType(expr);
+       type_mod = exprTypmod(expr);
+
+       if (colname == NULL)
+       {
+               /*
+                * Generate a suitable column name for a column without any
+                * explicit 'AS ColumnName' clause.
+                */
+               colname = FigureColname(node);
+       }
+
+       resnode = makeResdom((AttrNumber) pstate->p_next_resno++,
+                                                type_id,
+                                                type_mod,
+                                                colname,
+                                                resjunk);
+
+       return makeTargetEntry(resnode, (Expr *) expr);
+}
+
 
 /*
- * transformTargetList -
- *       turns a list of ResTarget's into a list of TargetEntry's
+ * transformTargetList()
+ * Turns a list of ResTarget's into a list of TargetEntry's.
+ *
+ * At this point, we don't care whether we are doing SELECT, INSERT,
+ * or UPDATE; we just transform the given expressions.
  */
 List *
 transformTargetList(ParseState *pstate, List *targetlist)
 {
        List       *p_target = NIL;
-       List       *tail_p_target = NIL;
 
        while (targetlist != NIL)
        {
                ResTarget  *res = (ResTarget *) lfirst(targetlist);
-               TargetEntry *tent = makeNode(TargetEntry);
 
-               switch (nodeTag(res->val))
+               if (IsA(res->val, ColumnRef))
                {
-                       case T_Ident:
-                               {
-                                       Node       *expr;
-                                       Oid                     type_id;
-                                       int                     type_mod;
-                                       char       *identname;
-                                       char       *resname;
-
-                                       identname = ((Ident *) res->val)->name;
-                                       handleTargetColname(pstate, &res->name, NULL, identname);
-
-                                       /*
-                                        * here we want to look for column names only, not relation
-                                        * names (even though they can be stored in Ident nodes, too)
-                                        */
-                                       expr = transformIdent(pstate, (Node *) res->val, EXPR_COLUMN_FIRST);
-                                       type_id = exprType(expr);
-                                       if (nodeTag(expr) == T_Var)
-                                               type_mod = ((Var *)expr)->vartypmod;
-                                       else
-                                               type_mod = -1;
-                                       resname = (res->name) ? res->name : identname;
-                                       tent->resdom = makeResdom((AttrNumber) pstate->p_last_resno++,
-                                                                                         (Oid) type_id,
-                                                                                         type_mod,
-                                                                                         resname,
-                                                                                         (Index) 0,
-                                                                                         (Oid) 0,
-                                                                                         0);
-
-                                       tent->expr = expr;
-                                       break;
-                               }
-                       case T_ParamNo:
-                       case T_FuncCall:
-                       case T_A_Const:
-                       case T_A_Expr:
-                               {
-                                       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, "yyparse: string constant expected");
-
-                                               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");
-                                                       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;
-                                                       }
-                                                       else
-                                                       {
-                                                               lindx[i] = 1;
-                                                       }
-                                                       if (lindx[i] > uindx[i])
-                                                               elog(ERROR, "yyparse: lower index cannot be greater than upper index");
-                                                       sprintf(str, "[%d:%d]", lindx[i], uindx[i]);
-                                                       str += strlen(str);
-                                                       i++;
-                                               }
-                                               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, "yyparse: array dimensions do not match");
-                                               constval = makeNode(Value);
-                                               constval->type = T_String;
-                                               constval->val.str = save_str;
-                                               tent = make_targetlist_expr(pstate, res->name,
-                                                                                  (Node *) make_const(constval),
-                                                                                                       NULL);
-                                               pfree(save_str);
-                                       }
-                                       else
-                                       {
-                                               char       *colname = res->name;
+                       ColumnRef  *cref = (ColumnRef *) res->val;
+                       List       *fields = cref->fields;
+                       int                     numnames = length(fields);
 
-                                               /* this is not an array assignment */
-                                               if (colname == NULL)
+                       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));
+                       }
+                       else if (strcmp(strVal(nth(numnames - 1, fields)), "*") == 0)
+                       {
+                               /*
+                                * 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));
 
                                                        /*
-                                                        * if you're wondering why this is here, look
-                                                        * at the yacc grammar for why a name can be
-                                                        * missing. -ay
+                                                        * We check the catalog name and then ignore
+                                                        * it.
                                                         */
-                                                       colname = figureColname(expr, res->val);
+                                                       if (strcmp(name1, DatabaseName) != 0)
+                                                               elog(ERROR, "Cross-database references are not implemented");
+                                                       schemaname = strVal(lsecond(fields));
+                                                       relname = strVal(lthird(fields));
+                                                       break;
                                                }
-                                               if (res->indirection)
-                                               {
-                                                       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);
-                                                       }
-                                               }
-                                               res->name = colname;
-                                               tent = make_targetlist_expr(pstate, res->name, expr,
-                                                                                                       res->indirection);
-                                       }
-                                       break;
+                                       default:
+                                               elog(ERROR, "Invalid qualified name syntax (too many names)");
+                                               schemaname = NULL;              /* keep compiler quiet */
+                                               relname = NULL;
+                                               break;
                                }
-                       case T_Attr:
-                               {
-                                       Oid                     type_id;
-                                       int                     type_mod;
-                                       Attr       *att = (Attr *) res->val;
-                                       Node       *result;
-                                       char       *attrname;
-                                       char       *resname;
-                                       Resdom     *resnode;
-                                       List       *attrs = att->attrs;
-
-                                       /*
-                                        * 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);
-
-                                               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);
-                                               continue;
-                                       }
 
-                                       /*
-                                        * 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, "*"))
-                                       {
+                               rte = refnameRangeTblEntry(pstate, schemaname, relname,
+                                                                                  &sublevels_up);
+                               if (rte == NULL)
+                                       rte = addImplicitRTE(pstate, makeRangeVar(schemaname,
+                                                                                                                         relname));
 
-                                               /*
-                                                * 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);
-                                               while (lnext(tail_p_target) != NIL)
-                                                       /* make sure we point to the last target entry */
-                                                       tail_p_target = lnext(tail_p_target);
-
-                                               /*
-                                                * skip the rest of the while loop
-                                                */
-                                               targetlist = lnext(targetlist);
-                                               continue;
-                                       }
-
-
-                                       /*
-                                        * 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));
-                                       resnode = makeResdom((AttrNumber) pstate->p_last_resno++,
-                                                                                (Oid) type_id,
-                                                                                type_mod,
-                                                                                resname,
-                                                                                (Index) 0,
-                                                                                (Oid) 0,
-                                                                                0);
-                                       tent->resdom = resnode;
-                                       tent->expr = result;
-                                       break;
-                               }
-                       default:
-                               /* internal error */
-                               elog(ERROR,
-                                        "internal error: do not know how to transform targetlist");
-                               break;
+                               p_target = nconc(p_target,
+                                                                expandRelAttrs(pstate, rte));
+                       }
+                       else
+                       {
+                               /* Plain ColumnRef node, treat it as an expression */
+                               p_target = lappend(p_target,
+                                                                  transformTargetEntry(pstate,
+                                                                                                               res->val,
+                                                                                                               NULL,
+                                                                                                               res->name,
+                                                                                                               false));
+                       }
                }
-
-               if (p_target == NIL)
+               else if (IsA(res->val, InsertDefault))
                {
-                       p_target = tail_p_target = lcons(tent, NIL);
+                       InsertDefault *newnode = makeNode(InsertDefault);
+
+                       /*
+                        * If this is a DEFAULT element, we make a junk entry which
+                        * will get dropped on return to transformInsertStmt().
+                        */
+                       p_target = lappend(p_target, newnode);
                }
                else
                {
-                       lnext(tail_p_target) = lcons(tent, NIL);
-                       tail_p_target = lnext(tail_p_target);
+                       /* Everything else but ColumnRef and InsertDefault */
+                       p_target = lappend(p_target,
+                                                          transformTargetEntry(pstate,
+                                                                                                       res->val,
+                                                                                                       NULL,
+                                                                                                       res->name,
+                                                                                                       false));
                }
+
                targetlist = lnext(targetlist);
        }
 
@@ -319,342 +206,425 @@ transformTargetList(ParseState *pstate, List *targetlist)
 
 
 /*
- * make_targetlist_expr -
- *       make a TargetEntry from an expression
+ * markTargetListOrigins()
+ *             Mark targetlist columns that are simple Vars with the source
+ *             table's OID and column number.
  *
- * arrayRef is a list of transformed A_Indices
+ * 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 *
-make_targetlist_expr(ParseState *pstate,
-                                        char *colname,
-                                        Node *expr,
-                                        List *arrayRef)
+void
+markTargetListOrigins(ParseState *pstate, List *targetlist)
 {
-       Oid                     type_id,
-                               attrtype;
-       int                     type_mod,
-                               attrtypmod;
-       int                     resdomno;
-       Relation        rd;
-       bool            attrisset;
-       TargetEntry *tent;
-       Resdom     *resnode;
+       List       *l;
 
-       if (expr == NULL)
-               elog(ERROR, "make_targetlist_expr: invalid use of NULL expression");
-
-       type_id = exprType(expr);
-       if (nodeTag(expr) == T_Var)
-               type_mod = ((Var *)expr)->vartypmod;
-       else
-               type_mod = -1;
-
-       /* Processes target columns that will be receiving results */
-       if (pstate->p_is_insert || pstate->p_is_update)
+       foreach(l, targetlist)
        {
+               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, 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;
-#if 0
-               if (Input_is_string && Typecast_ok)
-               {
-                       Datum           val;
+               markTargetListOrigin(pstate, tle->resdom, (Var *) tle->expr);
+       }
+}
 
-                       if (type_id == typeTypeId(type("unknown")))
-                       {
-                               val = (Datum) textout((struct varlena *)
-                                                                         ((Const) lnext(expr))->constvalue);
-                       }
-                       else
-                       {
-                               val = ((Const) lnext(expr))->constvalue;
-                       }
-                       if (attrisset)
-                       {
-                               lnext(expr) = makeConst(attrtype,
-                                                                               attrlen,
-                                                                               val,
-                                                                               false,
-                                                                               true,
-                                                                               true,   /* is set */
-                                                                               false);
-                       }
-                       else
-                       {
-                               lnext(expr) =
-                                       makeConst(attrtype,
-                                                         attrlen,
-                                                         (Datum) fmgr(typeidRetinfunc(attrtype),
-                                                                                val, typeidTypElem(attrtype), -1),
-                                                         false,
-                                                         true /* Maybe correct-- 80% chance */ ,
-                                                         false,        /* is not a set */
-                                                         false);
-                       }
-               }
-               else if ((Typecast_ok) && (attrtype != type_id))
-               {
-                       lnext(expr) =
-                               parser_typecast2(expr, typeidType(attrtype));
-               }
-               else if (attrtype != type_id)
-               {
-                       if ((attrtype == INT2OID) && (type_id == INT4OID))
-                               lfirst(expr) = lispInteger(INT2OID);    /* handle CASHOID too */
-                       else if ((attrtype == FLOAT4OID) && (type_id == FLOAT8OID))
-                               lfirst(expr) = lispInteger(FLOAT4OID);
-                       else
-                               elog(ERROR, "unequal type in tlist : %s \n", colname);
-               }
+/*
+ * 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.
+ */
+static void
+markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var)
+{
+       RangeTblEntry *rte;
+       AttrNumber      attnum;
 
-               Input_is_string = false;
-               Input_is_integer = false;
-               Typecast_ok = true;
-#endif
+       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;
 
-               if (attrtype != type_id)
-               {
-                       if (IsA(expr, Const))
+       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:
                        {
-                               /* try to cast the constant */
-                               if (arrayRef && !(((A_Indices *) lfirst(arrayRef))->lidx))
+                               /* Subselect-in-FROM: copy up from the subselect */
+                               List       *subtl;
+
+                               foreach(subtl, rte->subquery->targetList)
                                {
-                                       /* updating a single item */
-                                       Oid                     typelem = typeidTypElem(attrtype);
+                                       TargetEntry *subte = (TargetEntry *) lfirst(subtl);
 
-                                       expr = (Node *) parser_typecast2(expr,
-                                                                                                        type_id,
-                                                                                                        typeidType(typelem),
-                                                                                                        attrtypmod);
+                                       if (subte->resdom->resjunk ||
+                                               subte->resdom->resno != attnum)
+                                               continue;
+                                       res->resorigtbl = subte->resdom->resorigtbl;
+                                       res->resorigcol = subte->resdom->resorigcol;
+                                       break;
                                }
-                               else
-                                       expr = (Node *) parser_typecast2(expr,
-                                                                                                        type_id,
-                                                                                                        typeidType(attrtype),
-                                                                                                        attrtypmod);
+                               /* falling off end of list shouldn't happen... */
+                               if (subtl == NIL)
+                                       elog(ERROR, "Subquery %s does not have attribute %d",
+                                                rte->eref->aliasname, attnum);
                        }
-                       else
+                       break;
+               case RTE_JOIN:
                        {
-                               /* currently, we can't handle casting of expressions */
-                               elog(ERROR, "parser: attribute '%s' is of type '%s' but expression is of type '%s'",
-                                        colname,
-                                        typeidTypeName(attrtype),
-                                        typeidTypeName(type_id));
+                               /* Join RTE --- recursively inspect the alias variable */
+                               Var        *aliasvar;
+
+                               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;
+       }
+}
 
-               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)
-                       {
-                               A_Indices  *ind = lfirst(ar);
 
-                               if (lowerIndexpr || (!upperIndexpr && ind->lidx))
-                               {
+/*
+ * 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)
+{
+       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;
 
-                                       /*
-                                        * XXX assume all lowerIndexpr is non-null in this
-                                        * case
-                                        */
-                                       lowerIndexpr = lappend(lowerIndexpr, ind->lidx);
-                               }
-                               upperIndexpr = lappend(upperIndexpr, ind->uidx);
-                               ar = lnext(ar);
-                       }
+       /*
+        * 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.
+        */
+       if (indirection)
+       {
+               Node       *arrayBase;
+               ArrayRef   *aref;
 
-                       expr = (Node *) make_array_set(target_expr,
-                                                                                  upperIndexpr,
-                                                                                  lowerIndexpr,
-                                                                                  (Expr *) expr);
-                       attrtype = attnumTypeId(rd, resdomno);
-                       attrtypmod = get_atttypmod(rd->rd_id, resdomno);
+               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
+               {
+                       /*
+                        * 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
        {
-               resdomno = pstate->p_last_resno++;
-               attrtype = type_id;
-               attrtypmod = type_mod;
+               /*
+                * 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));
+               }
        }
-       tent = makeNode(TargetEntry);
-
-       resnode = makeResdom((AttrNumber) resdomno,
-                                                (Oid) attrtype,
-                                                attrtypmod,
-                                                colname,
-                                                (Index) 0,
-                                                (Oid) 0,
-                                                0);
-
-       tent->resdom = resnode;
-       tent->expr = expr;
 
-       return tent;
+       /*
+        * 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;
+               /*
+                * 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;
-               AttributeTupleForm *attr = pstate->p_target_relation->rd_att->attrs;
 
-               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)
+ExpandAllTables(ParseState *pstate)
 {
        List       *target = NIL;
-       List       *legit_rtable = NIL;
-       List       *rt,
-                          *rtable;
-
-       rtable = pstate->p_rtable;
-       if (pstate->p_is_rule)
-       {
-
-               /*
-                * skip first two entries, "*new*" and "*current*"
-                */
-               rtable = lnext(lnext(pstate->p_rtable));
-       }
+       bool            found_table = false;
+       List       *ns;
 
-       /* this should not happen */
-       if (rtable == NULL)
-               elog(ERROR, "cannot expand: null p_rtable");
-
-       /*
-        * go through the range table and make a list of range table entries
-        * which we will expand.
-        */
-       foreach(rt, rtable)
+       foreach(ns, pstate->p_namespace)
        {
-               RangeTblEntry *rte = lfirst(rt);
+               Node       *n = (Node *) lfirst(ns);
+               RangeTblEntry *rte;
+
+               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 -
+ * 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)
 {
-       switch (nodeTag(expr))
+       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)
+{
+       int                     strength = 0;
+
+       if (node == NULL)
+               return strength;
+
+       switch (nodeTag(node))
        {
-               case T_Aggreg:
-                       return (char *) ((Aggreg *) 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_ExprFieldSelect:
+                       {
+                               ExprFieldSelect *efs = (ExprFieldSelect *) node;
+
+                               if (efs->fields)
+                               {
+                                       char       *fname = strVal(llast(efs->fields));
+
+                                       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;
 }