OSDN Git Service

Get rid of crocky use of RangeVar nodes in parser to represent partially
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 2 Apr 2004 19:07:02 +0000 (19:07 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 2 Apr 2004 19:07:02 +0000 (19:07 +0000)
transformed whole-row variables.  Cleaner to use regular whole-row Vars.

src/backend/optimizer/util/clauses.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_func.c
src/backend/parser/parse_relation.c
src/backend/parser/parse_target.c
src/include/parser/parse_relation.h

index b05f760..6aea081 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.167 2004/03/24 22:40:28 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.168 2004/04/02 19:06:57 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -2550,16 +2550,6 @@ expression_tree_walker(Node *node,
                                        return true;
                        }
                        break;
-               case T_RangeVar:
-                       /*
-                        * Give a useful complaint if someone uses a bare relation name
-                        * in an expression (see comments in transformColumnRef()).
-                        */
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_SYNTAX_ERROR),
-                                        errmsg("relation reference \"%s\" cannot be used in an expression",
-                                                       ((RangeVar *) node)->relname)));
-                       break;
                default:
                        elog(ERROR, "unrecognized node type: %d",
                                 (int) nodeTag(node));
@@ -3031,16 +3021,6 @@ expression_tree_mutator(Node *node,
                                return (Node *) newnode;
                        }
                        break;
-               case T_RangeVar:
-                       /*
-                        * Give a useful complaint if someone uses a bare relation name
-                        * in an expression (see comments in transformColumnRef()).
-                        */
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_SYNTAX_ERROR),
-                                        errmsg("relation reference \"%s\" cannot be used in an expression",
-                                                       ((RangeVar *) node)->relname)));
-                       break;
                default:
                        elog(ERROR, "unrecognized node type: %d",
                                 (int) nodeTag(node));
index 83daae9..acee2b3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.167 2004/03/24 22:40:29 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.168 2004/04/02 19:06:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,6 +40,8 @@ bool          Transform_null_equals = false;
 static Node *typecast_expression(ParseState *pstate, Node *expr,
                                        TypeName *typename);
 static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
+static Node *transformWholeRowRef(ParseState *pstate, char *schemaname,
+                                                                 char *relname);
 static Node *transformIndirection(ParseState *pstate, Node *basenode,
                                         List *indirection);
 
@@ -932,34 +934,29 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
 {
        int                     numnames = length(cref->fields);
        Node       *node;
-       RangeVar   *rv;
        int                     levels_up;
 
        /*----------
         * The allowed syntaxes are:
         *
         * A            First try to resolve as unqualified column name;
-        *                      if no luck, try to resolve as unqual. table name (A.*).
-        * A.B          A is an unqual. table name; B is either a
+        *                      if no luck, try to resolve as unqualified table name (A.*).
+        * A.B          A is an unqualified table name; B is either a
         *                      column or function name (trying column name first).
         * A.B.C        schema A, table B, col or func name C.
         * A.B.C.D      catalog A, schema B, table C, col or func D.
-        * A.*          A is an unqual. table name; means whole-row value.
+        * A.*          A is an unqualified table name; means whole-row value.
         * A.B.*        whole-row value of table B in schema A.
         * A.B.C.*      whole-row value of table C in schema B in catalog A.
         *
         * We do not need to cope with bare "*"; that will only be accepted by
         * the grammar at the top level of a SELECT list, and transformTargetList
-        * will take care of it before it ever gets here.
+        * will take care of it before it ever gets here.  Also, "A.*" etc will
+        * be expanded by transformTargetList if they appear at SELECT top level,
+        * so here we are only going to see them as function or operator inputs.
         *
         * Currently, if a catalog name is given then it must equal the current
         * database name; we check it here and then discard it.
-        *
-        * For whole-row references, the result is an untransformed RangeVar,
-        * which will work as the argument to a function call, but not in any
-        * other context at present.  (We could instead coerce to a whole-row Var,
-        * but that will fail for subselect and join RTEs, because there is no
-        * pg_type entry for their rowtypes.)
         *----------
         */
        switch (numnames)
@@ -1001,16 +998,12 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
                                        if (cref->indirection == NIL &&
                                                refnameRangeTblEntry(pstate, NULL, name,
                                                                                         &levels_up) != NULL)
-                                       {
-                                               rv = makeNode(RangeVar);
-                                               rv->relname = name;
-                                               rv->inhOpt = INH_DEFAULT;
-                                               node = (Node *) rv;
-                                       }
+                                               node = transformWholeRowRef(pstate, NULL, name);
                                        else
                                                ereport(ERROR,
                                                                (errcode(ERRCODE_UNDEFINED_COLUMN),
-                                                       errmsg("column \"%s\" does not exist", name)));
+                                                                errmsg("column \"%s\" does not exist",
+                                                                               name)));
                                }
                                break;
                        }
@@ -1022,10 +1015,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
                                /* Whole-row reference? */
                                if (strcmp(name2, "*") == 0)
                                {
-                                       rv = makeNode(RangeVar);
-                                       rv->relname = name1;
-                                       rv->inhOpt = INH_DEFAULT;
-                                       node = (Node *) rv;
+                                       node = transformWholeRowRef(pstate, NULL, name1);
                                        break;
                                }
 
@@ -1038,12 +1028,10 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
                                         * try it as a function call.  Here, we will create an
                                         * implicit RTE for tables not already entered.
                                         */
-                                       rv = makeNode(RangeVar);
-                                       rv->relname = name1;
-                                       rv->inhOpt = INH_DEFAULT;
+                                       node = transformWholeRowRef(pstate, NULL, name1);
                                        node = ParseFuncOrColumn(pstate,
                                                                                         makeList1(makeString(name2)),
-                                                                                        makeList1(rv),
+                                                                                        makeList1(node),
                                                                                         false, false, true);
                                }
                                break;
@@ -1057,11 +1045,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
                                /* Whole-row reference? */
                                if (strcmp(name3, "*") == 0)
                                {
-                                       rv = makeNode(RangeVar);
-                                       rv->schemaname = name1;
-                                       rv->relname = name2;
-                                       rv->inhOpt = INH_DEFAULT;
-                                       node = (Node *) rv;
+                                       node = transformWholeRowRef(pstate, name1, name2);
                                        break;
                                }
 
@@ -1070,13 +1054,10 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
                                if (node == NULL)
                                {
                                        /* Try it as a function call */
-                                       rv = makeNode(RangeVar);
-                                       rv->schemaname = name1;
-                                       rv->relname = name2;
-                                       rv->inhOpt = INH_DEFAULT;
+                                       node = transformWholeRowRef(pstate, name1, name2);
                                        node = ParseFuncOrColumn(pstate,
                                                                                         makeList1(makeString(name3)),
-                                                                                        makeList1(rv),
+                                                                                        makeList1(node),
                                                                                         false, false, true);
                                }
                                break;
@@ -1100,11 +1081,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
                                /* Whole-row reference? */
                                if (strcmp(name4, "*") == 0)
                                {
-                                       rv = makeNode(RangeVar);
-                                       rv->schemaname = name2;
-                                       rv->relname = name3;
-                                       rv->inhOpt = INH_DEFAULT;
-                                       node = (Node *) rv;
+                                       node = transformWholeRowRef(pstate, name2, name3);
                                        break;
                                }
 
@@ -1113,13 +1090,10 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
                                if (node == NULL)
                                {
                                        /* Try it as a function call */
-                                       rv = makeNode(RangeVar);
-                                       rv->schemaname = name2;
-                                       rv->relname = name3;
-                                       rv->inhOpt = INH_DEFAULT;
+                                       node = transformWholeRowRef(pstate, name2, name3);
                                        node = ParseFuncOrColumn(pstate,
                                                                                         makeList1(makeString(name4)),
-                                                                                        makeList1(rv),
+                                                                                        makeList1(node),
                                                                                         false, false, true);
                                }
                                break;
@@ -1137,6 +1111,99 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
 }
 
 /*
+ * Construct a whole-row reference to represent the notation "relation.*".
+ *
+ * In simple cases, this will be a Var with varno set to the correct range
+ * table entry, and varattno == 0 to signal that it references the whole
+ * tuple.  (Use of zero here is unclean, since it could easily be confused
+ * with error cases, but it's not worth changing now.)  The vartype indicates
+ * a rowtype; either a named composite type, or RECORD.
+ *
+ * We also need the ability to build a row-constructor expression, but the
+ * infrastructure for that doesn't exist just yet.
+ */
+static Node *
+transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname)
+{
+       Node       *result;
+       RangeTblEntry *rte;
+       int                     vnum;
+       int                     sublevels_up;
+       Oid                     toid;
+
+       /* Look up the referenced RTE, creating it if needed */
+
+       rte = refnameRangeTblEntry(pstate, schemaname, relname,
+                                                          &sublevels_up);
+
+       if (rte == NULL)
+               rte = addImplicitRTE(pstate, makeRangeVar(schemaname, relname));
+
+       vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
+
+       /* Build the appropriate referencing node */
+
+       switch (rte->rtekind)
+       {
+               case RTE_RELATION:
+                       /* relation: the rowtype is a named composite type */
+                       toid = get_rel_type_id(rte->relid);
+                       if (!OidIsValid(toid))
+                               elog(ERROR, "could not find type OID for relation %u",
+                                        rte->relid);
+                       result = (Node *) makeVar(vnum,
+                                                                         InvalidAttrNumber,
+                                                                         toid,
+                                                                         -1,
+                                                                         sublevels_up);
+                       break;
+               case RTE_FUNCTION:
+                       toid = exprType(rte->funcexpr);
+                       if (toid == RECORDOID || get_typtype(toid) == 'c')
+                       {
+                               /* func returns composite; same as relation case */
+                               result = (Node *) makeVar(vnum,
+                                                                                 InvalidAttrNumber,
+                                                                                 toid,
+                                                                                 -1,
+                                                                                 sublevels_up);
+                       }
+                       else
+                       {
+                               /*
+                                * func returns scalar; instead of making a whole-row Var,
+                                * just reference the function's scalar output.  (XXX this
+                                * seems a tad inconsistent, especially if "f.*" was
+                                * explicitly written ...)
+                                */
+                               result = (Node *) makeVar(vnum,
+                                                                                 1,
+                                                                                 toid,
+                                                                                 -1,
+                                                                                 sublevels_up);
+                       }
+                       break;
+               default:
+                       /*
+                        * RTE is a join or subselect.  For the moment we represent this
+                        * as a whole-row Var of RECORD type, but this will not actually
+                        * work; need a row-constructor expression instead.
+                        *
+                        * XXX after fixing, be sure that unknown_attribute still
+                        * does the right thing.
+                        */
+                       result = (Node *) makeVar(vnum,
+                                                                         InvalidAttrNumber,
+                                                                         RECORDOID,
+                                                                         -1,
+                                                                         sublevels_up);
+                       break;
+       }
+
+       return result;
+}
+
+/*
  *     exprType -
  *       returns the Oid of the type of the expression. (Used for typechecking.)
  */
@@ -1294,19 +1361,6 @@ exprType(Node *expr)
                case T_SetToDefault:
                        type = ((SetToDefault *) expr)->typeId;
                        break;
-               case T_RangeVar:
-
-                       /*
-                        * If someone uses a bare relation name in an expression, we
-                        * will likely first notice a problem here (see comments in
-                        * transformColumnRef()).  Issue an appropriate error message.
-                        */
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_SYNTAX_ERROR),
-                                        errmsg("relation reference \"%s\" cannot be used in an expression",
-                                                       ((RangeVar *) expr)->relname)));
-                       type = InvalidOid;      /* keep compiler quiet */
-                       break;
                default:
                        elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
                        type = InvalidOid;      /* keep compiler quiet */
index 1677493..deab6c2 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.166 2004/04/01 21:28:44 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.167 2004/04/02 19:06:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "utils/syscache.h"
 
 
-static Node *ParseComplexProjection(char *funcname, Node *first_arg);
+static Node *ParseComplexProjection(ParseState *pstate, char *funcname,
+                                                                       Node *first_arg);
 static Oid **argtype_inherit(int nargs, Oid *argtypes);
 
 static int     find_inheritors(Oid relid, Oid **supervec);
 static Oid **gen_cross_product(InhPaths *arginh, int nargs);
 static FieldSelect *setup_field_select(Node *input, char *attname, Oid relid);
-static void unknown_attribute(const char *schemaname, const char *relname,
-                                 const char *attname);
+static void unknown_attribute(ParseState *pstate, Node *relref, char *attname);
 
 
 /*
@@ -48,7 +48,9 @@ static void unknown_attribute(const char *schemaname, const char *relname,
  *     For historical reasons, Postgres tries to treat the notations tab.col
  *     and col(tab) as equivalent: if a single-argument function call has an
  *     argument of complex type and the (unqualified) function name matches
- *     any attribute of the type, we take it as a column projection.
+ *     any attribute of the type, we take it as a column projection.  Conversely
+ *     a function of a single complex-type argument can be written like a
+ *     column reference, allowing functions to act like computed columns.
  *
  *     Hence, both cases come through here.  The is_column parameter tells us
  *     which syntactic construct is actually being dealt with, but this is
@@ -57,9 +59,7 @@ static void unknown_attribute(const char *schemaname, const char *relname,
  *     a single argument (the putative table), unqualified function name
  *     equal to the column name, and no aggregate decoration.
  *
- *     In the function-call case, the argument expressions have been transformed
- *     already.  In the column case, we may get either a transformed expression
- *     or a RangeVar node as argument.
+ *     The argument expressions (in fargs) must have been transformed already.
  */
 Node *
 ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
@@ -96,44 +96,32 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
        }
 
        /*
-        * check for column projection: if function has one argument, and that
+        * Check for column projection: if function has one argument, and that
         * argument is of complex type, and function name is not qualified,
         * then the "function call" could be a projection.      We also check that
         * there wasn't any aggregate decoration.
         */
        if (nargs == 1 && !agg_star && !agg_distinct && length(funcname) == 1)
        {
-               char       *cname = strVal(lfirst(funcname));
+               Oid                     argtype = exprType(first_arg);
 
-               /* Is it a not-yet-transformed RangeVar node? */
-               if (IsA(first_arg, RangeVar))
+               if (argtype == RECORDOID || ISCOMPLEX(argtype))
                {
-                       /* First arg is a relation. This could be a projection. */
-                       retval = qualifiedNameToVar(pstate,
-                                                                       ((RangeVar *) first_arg)->schemaname,
-                                                                               ((RangeVar *) first_arg)->relname,
-                                                                               cname,
-                                                                               true);
+                       retval = ParseComplexProjection(pstate,
+                                                                                       strVal(lfirst(funcname)),
+                                                                                       first_arg);
                        if (retval)
                                return retval;
-               }
-               else if (ISCOMPLEX(exprType(first_arg)))
-               {
                        /*
-                        * Attempt to handle projection of a complex argument. If
-                        * ParseComplexProjection can't handle the projection, we have
-                        * to keep going.
+                        * If ParseComplexProjection doesn't recognize it as a projection,
+                        * just press on.
                         */
-                       retval = ParseComplexProjection(cname, first_arg);
-                       if (retval)
-                               return retval;
                }
        }
 
        /*
         * Okay, it's not a column projection, so it must really be a
-        * function. Extract arg type info and transform RangeVar arguments
-        * into varnodes of the appropriate form.
+        * function. Extract arg type info in preparation for function lookup.
         */
        MemSet(actual_arg_types, 0, FUNC_MAX_ARGS * sizeof(Oid));
 
@@ -141,96 +129,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
        foreach(i, fargs)
        {
                Node       *arg = lfirst(i);
-               Oid                     toid;
-
-               if (IsA(arg, RangeVar))
-               {
-                       char       *schemaname;
-                       char       *relname;
-                       RangeTblEntry *rte;
-                       int                     vnum;
-                       int                     sublevels_up;
-
-                       /*
-                        * a relation: look it up in the range table, or add if needed
-                        */
-                       schemaname = ((RangeVar *) arg)->schemaname;
-                       relname = ((RangeVar *) arg)->relname;
 
-                       rte = refnameRangeTblEntry(pstate, schemaname, relname,
-                                                                          &sublevels_up);
-
-                       if (rte == NULL)
-                               rte = addImplicitRTE(pstate, (RangeVar *) arg);
-
-                       vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
-
-                       /*
-                        * The parameter to be passed to the function is the whole
-                        * tuple from the relation.  We build a special VarNode to
-                        * reflect this -- it has varno set to the correct range table
-                        * entry, but has varattno == 0 to signal that the whole tuple
-                        * is the argument.
-                        */
-                       switch (rte->rtekind)
-                       {
-                               case RTE_RELATION:
-                                       toid = get_rel_type_id(rte->relid);
-                                       if (!OidIsValid(toid))
-                                               elog(ERROR, "could not find type OID for relation %u",
-                                                        rte->relid);
-                                       /* replace RangeVar in the arg list */
-                                       lfirst(i) = makeVar(vnum,
-                                                                               InvalidAttrNumber,
-                                                                               toid,
-                                                                               -1,
-                                                                               sublevels_up);
-                                       break;
-                               case RTE_FUNCTION:
-                                       toid = exprType(rte->funcexpr);
-                                       if (get_typtype(toid) == 'c')
-                                       {
-                                               /* func returns composite; same as relation case */
-                                               lfirst(i) = makeVar(vnum,
-                                                                                       InvalidAttrNumber,
-                                                                                       toid,
-                                                                                       -1,
-                                                                                       sublevels_up);
-                                       }
-                                       else
-                                       {
-                                               /* func returns scalar; use attno 1 instead */
-                                               lfirst(i) = makeVar(vnum,
-                                                                                       1,
-                                                                                       toid,
-                                                                                       -1,
-                                                                                       sublevels_up);
-                                       }
-                                       break;
-                               default:
-
-                                       /*
-                                        * RTE is a join or subselect; must fail for lack of a
-                                        * named tuple type
-                                        *
-                                        * XXX FIXME
-                                        */
-                                       if (is_column)
-                                               unknown_attribute(schemaname, relname,
-                                                                                 strVal(lfirst(funcname)));
-                                       else
-                                               ereport(ERROR,
-                                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                                errmsg("cannot pass result of subquery or join \"%s\" to a function",
-                                                                               relname)));
-                                       toid = InvalidOid;      /* keep compiler quiet */
-                                       break;
-                       }
-               }
-               else
-                       toid = exprType(arg);
-
-               actual_arg_types[argn++] = toid;
+               actual_arg_types[argn++] = exprType(arg);
        }
 
        /*
@@ -281,25 +181,9 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
                 */
                if (is_column)
                {
-                       char       *colname = strVal(lfirst(funcname));
-                       Oid                     relTypeId;
-
                        Assert(nargs == 1);
-                       if (IsA(first_arg, RangeVar))
-                               unknown_attribute(((RangeVar *) first_arg)->schemaname,
-                                                                 ((RangeVar *) first_arg)->relname,
-                                                                 colname);
-                       relTypeId = exprType(first_arg);
-                       if (!ISCOMPLEX(relTypeId))
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                                                errmsg("attribute notation .%s applied to type %s, which is not a complex type",
-                                                               colname, format_type_be(relTypeId))));
-                       else
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_UNDEFINED_COLUMN),
-                                         errmsg("attribute \"%s\" not found in data type %s",
-                                                        colname, format_type_be(relTypeId))));
+                       Assert(length(funcname) == 1);
+                       unknown_attribute(pstate, first_arg, strVal(lfirst(funcname)));
                }
 
                /*
@@ -1284,80 +1168,92 @@ setup_field_select(Node *input, char *attname, Oid relid)
  *       handles function calls with a single argument that is of complex type.
  *       If the function call is actually a column projection, return a suitably
  *       transformed expression tree.  If not, return NULL.
- *
- * NB: argument is expected to be transformed already, ie, not a RangeVar.
  */
 static Node *
-ParseComplexProjection(char *funcname, Node *first_arg)
+ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg)
 {
-       Oid                     argtype = exprType(first_arg);
+       Oid                     argtype;
        Oid                     argrelid;
        AttrNumber      attnum;
-       FieldSelect *fselect;
-
-       argrelid = typeidTypeRelid(argtype);
-       if (!argrelid)
-               return NULL;                    /* probably should not happen */
-       attnum = get_attnum(argrelid, funcname);
-       if (attnum == InvalidAttrNumber)
-               return NULL;                    /* funcname does not match any column */
 
        /*
-        * Check for special cases where we don't want to return a
-        * FieldSelect.
+        * Special case for whole-row Vars so that we can resolve (foo.*).bar
+        * even when foo is a reference to a subselect, join, or RECORD function.
+        * A bonus is that we avoid generating an unnecessary FieldSelect; our
+        * result can omit the whole-row Var and just be a Var for the selected
+        * field.
         */
-       switch (nodeTag(first_arg))
+       if (IsA(first_arg, Var) &&
+               ((Var *) first_arg)->varattno == InvalidAttrNumber)
        {
-               case T_Var:
-                       {
-                               Var                *var = (Var *) first_arg;
+               RangeTblEntry *rte;
 
-                               /*
-                                * If the Var is a whole-row tuple, we can just replace it
-                                * with a simple Var reference.
-                                */
-                               if (var->varattno == InvalidAttrNumber)
-                               {
-                                       Oid                     vartype;
-                                       int32           vartypmod;
+               rte = GetRTEByRangeTablePosn(pstate,
+                                                                        ((Var *) first_arg)->varno,
+                                                                        ((Var *) first_arg)->varlevelsup);
+               /* Return a Var if funcname matches a column, else NULL */
+               return scanRTEForColumn(pstate, rte, funcname);
+       }
 
-                                       get_atttypetypmod(argrelid, attnum,
-                                                                         &vartype, &vartypmod);
+       /*
+        * Else do it the hard way.  Note that if the arg is of RECORD type,
+        * we will never recognize a column name, and always assume the item
+        * must be a function.
+        */
+       argtype = exprType(first_arg);
+       argrelid = typeidTypeRelid(argtype);
+       if (!argrelid)
+               return NULL;                    /* can only happen if RECORD */
 
-                                       return (Node *) makeVar(var->varno,
-                                                                                       attnum,
-                                                                                       vartype,
-                                                                                       vartypmod,
-                                                                                       var->varlevelsup);
-                               }
-                               break;
-                       }
-               default:
-                       break;
-       }
+       attnum = get_attnum(argrelid, funcname);
+       if (attnum == InvalidAttrNumber)
+               return NULL;                    /* funcname does not match any column */
 
-       /* Else generate a FieldSelect expression */
-       fselect = setup_field_select(first_arg, funcname, argrelid);
-       return (Node *) fselect;
+       /* Success, so generate a FieldSelect expression */
+       return (Node *) setup_field_select(first_arg, funcname, argrelid);
 }
 
 /*
- * Simple helper routine for delivering "column does not exist" error message
+ * helper routine for delivering "column does not exist" error message
  */
 static void
-unknown_attribute(const char *schemaname, const char *relname,
-                                 const char *attname)
+unknown_attribute(ParseState *pstate, Node *relref, char *attname)
 {
-       if (schemaname)
-               ereport(ERROR,
-                               (errcode(ERRCODE_UNDEFINED_COLUMN),
-                                errmsg("column %s.%s.%s does not exist",
-                                               schemaname, relname, attname)));
-       else
+       RangeTblEntry *rte;
+
+       if (IsA(relref, Var))
+       {
+               /* Reference the RTE by alias not by actual table name */
+               rte = GetRTEByRangeTablePosn(pstate,
+                                                                        ((Var *) relref)->varno,
+                                                                        ((Var *) relref)->varlevelsup);
                ereport(ERROR,
                                (errcode(ERRCODE_UNDEFINED_COLUMN),
                                 errmsg("column %s.%s does not exist",
-                                               relname, attname)));
+                                               rte->eref->aliasname, attname)));
+       }
+       else
+       {
+               /* Have to do it by reference to the type of the expression */
+               Oid                     relTypeId = exprType(relref);
+
+               if (ISCOMPLEX(relTypeId))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_COLUMN),
+                                        errmsg("column \"%s\" not found in data type %s",
+                                                       attname, format_type_be(relTypeId))));
+               else if (relTypeId == RECORDOID)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_COLUMN),
+                                        errmsg("could not identify column \"%s\" in record data type",
+                                                       attname)));
+               else
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                        errmsg("column notation .%s applied to type %s, "
+                                                       "which is not a composite type",
+                                                       attname, format_type_be(relTypeId))));
+       }
 }
 
 /*
index 3e314be..4df92ee 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.92 2004/01/14 23:01:55 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.93 2004/04/02 19:06:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -41,8 +41,6 @@ static Node *scanNameSpaceForRelid(ParseState *pstate, Node *nsnode,
                                          Oid relid);
 static void scanNameSpaceForConflict(ParseState *pstate, Node *nsnode,
                                                 RangeTblEntry *rte1, const char *aliasname1);
-static Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
-                                char *colname);
 static bool isForUpdate(ParseState *pstate, char *refname);
 static bool get_rte_attribute_is_dropped(RangeTblEntry *rte,
                                                         AttrNumber attnum);
@@ -425,6 +423,24 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
 }
 
 /*
+ * Given an RT index and nesting depth, find the corresponding RTE.
+ * This is the inverse of RTERangeTablePosn.
+ */
+RangeTblEntry *
+GetRTEByRangeTablePosn(ParseState *pstate,
+                                          int varno,
+                                          int sublevels_up)
+{
+       while (sublevels_up-- > 0)
+       {
+               pstate = pstate->parentParseState;
+               Assert(pstate != NULL);
+       }
+       Assert(varno > 0 && varno <= length(pstate->p_rtable));
+       return rt_fetch(varno, pstate->p_rtable);
+}
+
+/*
  * scanRTEForColumn
  *       Search the column names of a single RTE for the given name.
  *       If found, return an appropriate Var node, else return NULL.
@@ -439,7 +455,7 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
  * expression can only appear in a FROM clause, and any table named in
  * FROM will be marked as requiring read access from the beginning.
  */
-static Node *
+Node *
 scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
 {
        Node       *result = NULL;
index 6bed23f..c9c44ac 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.115 2004/02/13 01:08:20 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.116 2004/04/02 19:06:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -60,14 +60,6 @@ transformTargetEntry(ParseState *pstate,
        if (expr == NULL)
                expr = transformExpr(pstate, node);
 
-       if (IsA(expr, RangeVar))
-               ereport(ERROR,
-                               (errcode(ERRCODE_SYNTAX_ERROR),
-                                errmsg("relation reference \"%s\" cannot be used as a select-list entry",
-                                               ((RangeVar *) expr)->relname),
-                                errhint("Write \"%s\".* to denote all the columns of the relation.",
-                                                ((RangeVar *) expr)->relname)));
-
        type_id = exprType(expr);
        type_mod = exprTypmod(expr);
 
@@ -243,21 +235,12 @@ markTargetListOrigins(ParseState *pstate, List *targetlist)
 static void
 markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var)
 {
-       Index           levelsup;
        RangeTblEntry *rte;
        AttrNumber      attnum;
 
        if (var == NULL || !IsA(var, Var))
                return;
-       levelsup = var->varlevelsup;
-       while (levelsup-- > 0)
-       {
-               pstate = pstate->parentParseState;
-               Assert(pstate != NULL);
-       }
-       Assert(var->varno > 0 &&
-                  (int) var->varno <= length(pstate->p_rtable));
-       rte = rt_fetch(var->varno, pstate->p_rtable);
+       rte = GetRTEByRangeTablePosn(pstate, var->varno, var->varlevelsup);
        attnum = var->varattno;
 
        switch (rte->rtekind)
index ca209c9..0fa75d0 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.42 2003/11/29 22:41:09 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.43 2004/04/02 19:07:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,6 +27,11 @@ extern void checkNameSpaceConflicts(ParseState *pstate, Node *namespace1,
 extern int RTERangeTablePosn(ParseState *pstate,
                                  RangeTblEntry *rte,
                                  int *sublevels_up);
+extern RangeTblEntry *GetRTEByRangeTablePosn(ParseState *pstate,
+                                                                                        int varno,
+                                                                                        int sublevels_up);
+extern Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
+                                                         char *colname);
 extern Node *colnameToVar(ParseState *pstate, char *colname);
 extern Node *qualifiedNameToVar(ParseState *pstate,
                                   char *schemaname,