]> 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 65a5b33d58c174c43fdc3a8d0bc1f3d063e9e5ec..10892bc292d73014c9645a36e2b2b25ad30150d9 100644 (file)
@@ -3,30 +3,33 @@
  * parse_target.c
  *       handle target lists
  *
- * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
+ * 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.57 2000/03/14 23:06:33 thomas 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 char *FigureColname(Node *node);
+static int     FigureColnameInternal(Node *node, char **name);
 
 
 /*
@@ -54,28 +57,30 @@ transformTargetEntry(ParseState *pstate,
 
        /* Transform the node if caller didn't do it already */
        if (expr == NULL)
-               expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
+               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
+               /*
+                * Generate a suitable column name for a column without any
                 * explicit 'AS ColumnName' clause.
                 */
-               colname = FigureColname(expr, node);
+               colname = FigureColname(node);
        }
 
-       resnode = makeResdom((AttrNumber) pstate->p_last_resno++,
+       resnode = makeResdom((AttrNumber) pstate->p_next_resno++,
                                                 type_id,
                                                 type_mod,
                                                 colname,
-                                                (Index) 0,
-                                                (Oid) InvalidOid,
                                                 resjunk);
 
-       return makeTargetEntry(resnode, expr);
+       return makeTargetEntry(resnode, (Expr *) expr);
 }
 
 
@@ -95,61 +100,75 @@ transformTargetList(ParseState *pstate, List *targetlist)
        {
                ResTarget  *res = (ResTarget *) lfirst(targetlist);
 
-               if (IsA(res->val, Attr))
+               if (IsA(res->val, ColumnRef))
                {
-                       Attr       *att = (Attr *) res->val;
+                       ColumnRef  *cref = (ColumnRef *) res->val;
+                       List       *fields = cref->fields;
+                       int                     numnames = length(fields);
 
-                       if (att->relname != NULL && strcmp(att->relname, "*") == 0)
+                       if (numnames == 1 && strcmp(strVal(lfirst(fields)), "*") == 0)
                        {
                                /*
-                                * Target item is a single '*', expand all tables
-                                * (eg. SELECT * FROM emp)
+                                * Target item is a single '*', expand all tables (eg.
+                                * SELECT * FROM emp)
                                 */
-                               if (pstate->p_shape != NULL)
-                               {
-                                       List *s, *a;
-                                       int i;
-
-                                       Assert(length(pstate->p_shape) == length(pstate->p_alias));
-
-                                       s = pstate->p_shape;
-                                       a = pstate->p_alias;
-                                       for (i = 0; i < length(pstate->p_shape); i++)
-                                       {
-                                               TargetEntry        *te;
-                                               char               *colname;
-                                               Attr *shape = lfirst(s);
-                                               Attr *alias = lfirst(a);
-
-                                               Assert(IsA(shape, Attr) && IsA(alias, Attr));
-
-                                               colname = strVal(lfirst(alias->attrs));
-                                               te = transformTargetEntry(pstate, (Node *) shape,
-                                                                                                 NULL, colname, false);
-                                               p_target = lappend(p_target, te);
-                                               s = lnext(s);
-                                               a = lnext(a);
-                                       }
-                               }
-                               else
-                                       p_target = nconc(p_target,
-                                                                        ExpandAllTables(pstate));
+                               p_target = nconc(p_target,
+                                                                ExpandAllTables(pstate));
                        }
-                       else if (att->attrs != NIL &&
-                                        strcmp(strVal(lfirst(att->attrs)), "*") == 0)
+                       else if (strcmp(strVal(nth(numnames - 1, fields)), "*") == 0)
                        {
                                /*
-                                * Target item is relation.*, expand that table
-                                * (eg. SELECT emp.*, dname FROM emp, dept)
+                                * 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));
+
+                                                       /*
+                                                        * 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;
+                               }
+
+                               rte = refnameRangeTblEntry(pstate, schemaname, relname,
+                                                                                  &sublevels_up);
+                               if (rte == NULL)
+                                       rte = addImplicitRTE(pstate, makeRangeVar(schemaname,
+                                                                                                                         relname));
+
                                p_target = nconc(p_target,
-                                                                expandAll(pstate, att->relname,
-                                                                                  makeAttr(att->relname, NULL),
-                                                                                  &pstate->p_last_resno));
+                                                                expandRelAttrs(pstate, rte));
                        }
                        else
                        {
-                               /* Plain Attr node, treat it as an expression */
+                               /* Plain ColumnRef node, treat it as an expression */
                                p_target = lappend(p_target,
                                                                   transformTargetEntry(pstate,
                                                                                                                res->val,
@@ -158,9 +177,19 @@ transformTargetList(ParseState *pstate, List *targetlist)
                                                                                                                false));
                        }
                }
+               else if (IsA(res->val, InsertDefault))
+               {
+                       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
                {
-                       /* Everything else but Attr */
+                       /* Everything else but ColumnRef and InsertDefault */
                        p_target = lappend(p_target,
                                                           transformTargetEntry(pstate,
                                                                                                        res->val,
@@ -176,9 +205,97 @@ transformTargetList(ParseState *pstate, List *targetlist)
 }
 
 
+/*
+ * 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.
+ */
+void
+markTargetListOrigins(ParseState *pstate, List *targetlist)
+{
+       List       *l;
+
+       foreach(l, targetlist)
+       {
+               TargetEntry *tle = (TargetEntry *) lfirst(l);
+
+               markTargetListOrigin(pstate, tle->resdom, (Var *) tle->expr);
+       }
+}
+
+/*
+ * 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;
+
+       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 (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;
+
+                               foreach(subtl, rte->subquery->targetList)
+                               {
+                                       TargetEntry *subte = (TargetEntry *) lfirst(subtl);
+
+                                       if (subte->resdom->resjunk ||
+                                               subte->resdom->resno != attnum)
+                                               continue;
+                                       res->resorigtbl = subte->resdom->resorigtbl;
+                                       res->resorigcol = subte->resdom->resorigcol;
+                                       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;
+
+                               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;
+       }
+}
+
+
 /*
  * updateTargetListEntry()
- *     This is used in INSERT and UPDATE statements only.  It prepares a
+ *     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
@@ -197,7 +314,7 @@ updateTargetListEntry(ParseState *pstate,
                                          int attrno,
                                          List *indirection)
 {
-       Oid                     type_id = exprType(tle->expr); /* type of value provided */
+       Oid                     type_id = exprType((Node *) tle->expr); /* type of value provided */
        Oid                     attrtype;               /* type of target column */
        int32           attrtypmod;
        Resdom     *resnode = tle->resdom;
@@ -210,81 +327,78 @@ updateTargetListEntry(ParseState *pstate,
        attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod;
 
        /*
-        * 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 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)
        {
-#ifndef DISABLE_JOIN_SYNTAX
-               Attr       *att = makeAttr(pstrdup(RelationGetRelationName(rd)), colname);
-#else
-               Attr       *att = makeNode(Attr);
-#endif
                Node       *arrayBase;
                ArrayRef   *aref;
 
-#ifdef DISABLE_JOIN_SYNTAX
-               att->relname = pstrdup(RelationGetRelationName(rd));
-               att->attrs = lcons(makeString(colname), NIL);
-#endif
-               arrayBase = ParseNestedFuncOrColumn(pstate, att,
-                                                                                       &pstate->p_last_resno,
-                                                                                       EXPR_COLUMN_FIRST);
-               aref = transformArraySubscripts(pstate, arrayBase,
-                                                                               indirection,
-                                                                               pstate->p_is_insert,
-                                                                               tle->expr);
                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 forced transformArraySubscripts to treat
-                        * the subscripting op as an array-slice op above, so the
-                        * source data will have been coerced to array type.
+                        * 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.
                         */
-                       aref->refexpr = NULL; /* signal there is no source array */
+                       arrayBase = NULL;       /* signal there is no source array */
                }
-               tle->expr = (Node *) aref;
+               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
        {
                /*
-                * For normal non-subscripted target column, do type checking
-                * and coercion.  But accept InvalidOid, which indicates the
-                * source is a NULL constant.
+                * 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)
                {
-                       if (type_id != attrtype)
-                       {
-                               tle->expr = CoerceTargetExpr(pstate, tle->expr, type_id,
-                                                                                        attrtype, attrtypmod);
-                               if (tle->expr == NULL)
-                                       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 the target is a fixed-length type, it may need a length
-                        * coercion as well as a type coercion.
-                        */
-                       tle->expr = coerce_type_typmod(pstate, tle->expr,
-                                                                                  attrtype, attrtypmod);
+                       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));
                }
        }
 
        /*
-        * 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!
+        * 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;
@@ -293,49 +407,11 @@ updateTargetListEntry(ParseState *pstate,
 }
 
 
-Node *
-CoerceTargetExpr(ParseState *pstate,
-                                Node *expr,
-                                Oid type_id,
-                                Oid attrtype,
-                                int32 attrtypmod)
-{
-       if (can_coerce_type(1, &type_id, &attrtype))
-               expr = coerce_type(pstate, expr, type_id, attrtype, attrtypmod);
-
-#ifndef DISABLE_STRING_HACKS
-
-       /*
-        * string hacks to get transparent conversions w/o explicit
-        * conversions
-        */
-       else if ((attrtype == BPCHAROID) || (attrtype == VARCHAROID))
-       {
-               Oid                     text_id = TEXTOID;
-
-               if (type_id == TEXTOID)
-               {
-               }
-               else if (can_coerce_type(1, &type_id, &text_id))
-                       expr = coerce_type(pstate, expr, type_id, text_id, attrtypmod);
-               else
-                       expr = NULL;
-       }
-#endif
-
-       else
-               expr = NULL;
-
-       return expr;
-}
-
-
 /*
  * checkInsertTargets -
- *       generate a list of column names if not supplied or
+ *       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.
- *       (used exclusively for inserts)
  */
 List *
 checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
@@ -353,20 +429,17 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
 
                for (i = 0; i < numcol; i++)
                {
-                       Ident      *id = makeNode(Ident);
+                       ResTarget  *col;
 
-#ifdef _DROP_COLUMN_HACK__
-                       if (COLUMN_IS_DROPPED(attr[i]))
-                       {
+                       if (attr[i]->attisdropped)
                                continue;
-                       }
-#endif /* _DROP_COLUMN_HACK__ */
-                       id->name = palloc(NAMEDATALEN);
-                       StrNCpy(id->name, NameStr(attr[i]->attname), NAMEDATALEN);
-                       id->indirection = NIL;
-                       id->isRel = false;
-                       cols = lappend(cols, id);
-                       *attrnos = lappendi(*attrnos, i+1);
+
+                       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
@@ -378,11 +451,11 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
 
                foreach(tl, cols)
                {
-                       char       *name = ((Ident *) lfirst(tl))->name;
+                       char       *name = ((ResTarget *) lfirst(tl))->name;
                        int                     attrno;
 
                        /* Lookup column name, elog on failure */
-                       attrno = attnameAttNum(pstate->p_target_relation, name);
+                       attrno = attnameAttNum(pstate->p_target_relation, name, false);
                        /* Check for duplicates */
                        if (intMember(attrno, *attrnos))
                                elog(ERROR, "Attribute '%s' specified more than once", name);
@@ -394,45 +467,52 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
 }
 
 /* ExpandAllTables()
- * Turns '*' (in the target list) into a list of attributes
- * (of all relations in the range table)
+ * 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       *rt,
-                          *rtable;
+       bool            found_table = false;
+       List       *ns;
 
-       rtable = pstate->p_rtable;
-       if (pstate->p_is_rule)
+       foreach(ns, pstate->p_namespace)
        {
-               /*
-                * skip first two entries, "*new*" and "*current*"
-                */
-               rtable = lnext(lnext(rtable));
-       }
-
-       /* SELECT *; */
-       if (rtable == NIL)
-               elog(ERROR, "Wildcard with no tables specified not allowed");
-
-       foreach(rt, rtable)
-       {
-               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 listed 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;
 
-               target = nconc(target,
-                                          expandAll(pstate, rte->eref->relname, rte->eref,
-                                                                &pstate->p_last_resno));
+               found_table = true;
+               target = nconc(target, expandRelAttrs(pstate, rte));
        }
+
+       /* Check for SELECT *; */
+       if (!found_table)
+               elog(ERROR, "Wildcard with no tables specified not allowed");
+
        return target;
 }
 
@@ -442,51 +522,109 @@ ExpandAllTables(ParseState *pstate)
  *       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)
 {
-       /* Some of these are easiest to do with the untransformed node */
-       switch (nodeTag(resval))
+       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_Ident:
-                       return ((Ident *) resval)->name;
-               case T_Attr:
+               case T_ColumnRef:
                        {
-                               List       *attrs = ((Attr *) resval)->attrs;
-                               if (attrs)
+                               char       *cname = strVal(llast(((ColumnRef *) node)->fields));
+
+                               if (strcmp(cname, "*") != 0)
                                {
-                                       while (lnext(attrs) != NIL)
-                                               attrs = lnext(attrs);
-                                       return strVal(lfirst(attrs));
+                                       *name = cname;
+                                       return 2;
                                }
                        }
                        break;
-               default:
+               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;
-       }
-       /* Otherwise, work with the transformed node */
-       switch (nodeTag(expr))
-       {
-               case T_Expr:
-                       if (((Expr *) expr)->opType == FUNC_EXPR && IsA(resval, FuncCall))
-                               return ((FuncCall *) resval)->funcname;
+               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_Aggref:
-                       return ((Aggref *) expr)->aggname;
                case T_CaseExpr:
+                       strength = FigureColnameInternal((Node *) ((CaseExpr *) node)->defresult,
+                                                                                        name);
+                       if (strength <= 1)
                        {
-                               char       *name;
-
-                               name = FigureColname(((CaseExpr *) expr)->defresult, resval);
-                               if (strcmp(name, "?column?") == 0)
-                                       name = "case";
-                               return name;
+                               *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;
 }