Fix oversight in recent rowtype-handling improvements: transformTargetList
authorTom Lane
Sat, 19 Jun 2004 18:19:56 +0000 (18:19 +0000)
committerTom Lane
Sat, 19 Jun 2004 18:19:56 +0000 (18:19 +0000)
should recognize 'foo.*' when the star appears in A_Indirection, not only
in ColumnRef.  This allows 'SELECT something.*' to do what the user
expects when the something is an expression yielding a row.

src/backend/parser/parse_func.c
src/backend/parser/parse_target.c
src/backend/utils/cache/typcache.c

index d5d71b67afdbe320db81c7d87ecab5154c702afb..883ad1ca9f609630bbae9a36ec1817a29fc36388 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.171 2004/06/16 01:26:45 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.172 2004/06/19 18:19:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,7 +38,6 @@ 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(ParseState *pstate, Node *relref, char *attname);
 
 
@@ -1131,33 +1130,6 @@ make_fn_arguments(ParseState *pstate,
    }
 }
 
-/*
- * setup_field_select
- *     Build a FieldSelect node that says which attribute to project to.
- *     This routine is called by ParseFuncOrColumn() when we have found
- *     a projection on a function result or parameter.
- */
-static FieldSelect *
-setup_field_select(Node *input, char *attname, Oid relid)
-{
-   FieldSelect *fselect = makeNode(FieldSelect);
-   AttrNumber  attno;
-
-   attno = get_attnum(relid, attname);
-   if (attno == InvalidAttrNumber)
-       ereport(ERROR,
-               (errcode(ERRCODE_UNDEFINED_COLUMN),
-            errmsg("column \"%s\" of relation \"%s\" does not exist",
-                   attname, get_rel_name(relid))));
-
-   fselect->arg = (Expr *) input;
-   fselect->fieldnum = attno;
-   fselect->resulttype = get_atttype(relid, attno);
-   fselect->resulttypmod = get_atttypmod(relid, attno);
-
-   return fselect;
-}
-
 /*
  * ParseComplexProjection -
  *   handles function calls with a single argument that is of complex type.
@@ -1170,6 +1142,7 @@ ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg)
    Oid         argtype;
    Oid         argrelid;
    AttrNumber  attnum;
+   FieldSelect *fselect;
 
    /*
     * Special case for whole-row Vars so that we can resolve (foo.*).bar
@@ -1205,7 +1178,14 @@ ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg)
        return NULL;            /* funcname does not match any column */
 
    /* Success, so generate a FieldSelect expression */
-   return (Node *) setup_field_select(first_arg, funcname, argrelid);
+   fselect = makeNode(FieldSelect);
+   fselect->arg = (Expr *) first_arg;
+   fselect->fieldnum = attnum;
+   get_atttypetypmod(argrelid, attnum,
+                     &fselect->resulttype,
+                     &fselect->resulttypmod);
+
+   return (Node *) fselect;
 }
 
 /*
index e0f0e6c930e757b31dd5869183f36dab7e3a7fc4..7dcbc7b7b741d0e9a04d54ff1e3824730cd37b36 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.121 2004/06/09 19:08:17 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.122 2004/06/19 18:19:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,6 +26,7 @@
 #include "parser/parse_type.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
+#include "utils/typcache.h"
 
 
 static void markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var);
@@ -37,7 +38,9 @@ static Node *transformAssignmentIndirection(ParseState *pstate,
                               int32 targetTypMod,
                               ListCell *indirection,
                               Node *rhs);
+static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref);
 static List *ExpandAllTables(ParseState *pstate);
+static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind);
 static char *FigureColname(Node *node);
 static int FigureColnameInternal(Node *node, char **name);
 
@@ -108,103 +111,47 @@ transformTargetList(ParseState *pstate, List *targetlist)
    {
        ResTarget  *res = (ResTarget *) lfirst(o_target);
 
+       /*
+        * Check for "something.*".  Depending on the complexity of the
+        * "something", the star could appear as the last name in ColumnRef,
+        * or as the last indirection item in A_Indirection.
+        */
        if (IsA(res->val, ColumnRef))
        {
            ColumnRef  *cref = (ColumnRef *) res->val;
-           List       *fields = cref->fields;
 
-           if (strcmp(strVal(llast(fields)), "*") == 0)
-           {
-               int     numnames = list_length(fields);
-
-               if (numnames == 1)
-               {
-                   /*
-                    * Target item is a single '*', expand all tables
-                    * (e.g., SELECT * FROM emp)
-                    */
-                   p_target = list_concat(p_target,
-                                          ExpandAllTables(pstate));
-               }
-               else
-               {
-                   /*
-                    * Target item is relation.*, expand that table
-                    * (e.g., SELECT emp.*, dname FROM emp, dept)
-                    */
-                   char       *schemaname;
-                   char       *relname;
-                   RangeTblEntry *rte;
-                   int         sublevels_up;
-
-                   switch (numnames)
-                   {
-                       case 2:
-                           schemaname = NULL;
-                           relname = strVal(linitial(fields));
-                           break;
-                       case 3:
-                           schemaname = strVal(linitial(fields));
-                           relname = strVal(lsecond(fields));
-                           break;
-                       case 4:
-                       {
-                           char       *name1 = strVal(linitial(fields));
-
-                           /*
-                            * We check the catalog name and then ignore
-                            * it.
-                            */
-                           if (strcmp(name1, get_database_name(MyDatabaseId)) != 0)
-                               ereport(ERROR,
-                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                        errmsg("cross-database references are not implemented: %s",
-                                               NameListToString(fields))));
-                           schemaname = strVal(lsecond(fields));
-                           relname = strVal(lthird(fields));
-                           break;
-                       }
-                       default:
-                           ereport(ERROR,
-                                   (errcode(ERRCODE_SYNTAX_ERROR),
-                                    errmsg("improper qualified name (too many dotted names): %s",
-                                           NameListToString(fields))));
-                           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 = list_concat(p_target,
-                                          expandRelAttrs(pstate, rte));
-               }
-           }
-           else
+           if (strcmp(strVal(llast(cref->fields)), "*") == 0)
            {
-               /* Plain ColumnRef node, treat it as an expression */
-               p_target = lappend(p_target,
-                                  transformTargetEntry(pstate,
-                                                       res->val,
-                                                       NULL,
-                                                       res->name,
-                                                       false));
+               /* It is something.*, expand into multiple items */
+               p_target = list_concat(p_target,
+                                      ExpandColumnRefStar(pstate, cref));
+               continue;
            }
        }
-       else
+       else if (IsA(res->val, A_Indirection))
        {
-           /* Everything else but ColumnRef */
-           p_target = lappend(p_target,
-                              transformTargetEntry(pstate,
-                                                   res->val,
-                                                   NULL,
-                                                   res->name,
-                                                   false));
+           A_Indirection  *ind = (A_Indirection *) res->val;
+           Node    *lastitem = llast(ind->indirection);
+
+           if (IsA(lastitem, String) &&
+               strcmp(strVal(lastitem), "*") == 0)
+           {
+               /* It is something.*, expand into multiple items */
+               p_target = list_concat(p_target,
+                                      ExpandIndirectionStar(pstate, ind));
+               continue;
+           }
        }
+
+       /*
+        * Not "something.*", so transform as a single expression
+        */
+       p_target = lappend(p_target,
+                          transformTargetEntry(pstate,
+                                               res->val,
+                                               NULL,
+                                               res->name,
+                                               false));
    }
 
    return p_target;
@@ -719,8 +666,90 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
    return cols;
 }
 
-/* ExpandAllTables()
- * Turns '*' (in the target list) into a list of targetlist entries.
+/*
+ * ExpandColumnRefStar()
+ *     Turns foo.* (in the target list) into a list of targetlist entries.
+ *
+ * This handles the case where '*' appears as the last or only name in a
+ * ColumnRef.
+ */
+static List *
+ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref)
+{
+   List       *fields = cref->fields;
+   int         numnames = list_length(fields);
+
+   if (numnames == 1)
+   {
+       /*
+        * Target item is a bare '*', expand all tables
+        *
+        * (e.g., SELECT * FROM emp, dept)
+        */
+       return ExpandAllTables(pstate);
+   }
+   else
+   {
+       /*
+        * Target item is relation.*, expand that table
+        *
+        * (e.g., SELECT emp.*, dname FROM emp, dept)
+        */
+       char       *schemaname;
+       char       *relname;
+       RangeTblEntry *rte;
+       int         sublevels_up;
+
+       switch (numnames)
+       {
+           case 2:
+               schemaname = NULL;
+               relname = strVal(linitial(fields));
+               break;
+           case 3:
+               schemaname = strVal(linitial(fields));
+               relname = strVal(lsecond(fields));
+               break;
+           case 4:
+           {
+               char       *name1 = strVal(linitial(fields));
+
+               /*
+                * We check the catalog name and then ignore
+                * it.
+                */
+               if (strcmp(name1, get_database_name(MyDatabaseId)) != 0)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                            errmsg("cross-database references are not implemented: %s",
+                                   NameListToString(fields))));
+               schemaname = strVal(lsecond(fields));
+               relname = strVal(lthird(fields));
+               break;
+           }
+           default:
+               ereport(ERROR,
+                       (errcode(ERRCODE_SYNTAX_ERROR),
+                        errmsg("improper qualified name (too many dotted names): %s",
+                               NameListToString(fields))));
+               schemaname = NULL;      /* keep compiler quiet */
+               relname = NULL;
+               break;
+       }
+
+       rte = refnameRangeTblEntry(pstate, schemaname, relname,
+                                  &sublevels_up);
+       if (rte == NULL)
+           rte = addImplicitRTE(pstate, makeRangeVar(schemaname,
+                                                     relname));
+
+       return expandRelAttrs(pstate, rte);
+   }
+}
+
+/*
+ * 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
@@ -770,6 +799,84 @@ ExpandAllTables(ParseState *pstate)
    return target;
 }
 
+/*
+ * ExpandIndirectionStar()
+ *     Turns foo.* (in the target list) into a list of targetlist entries.
+ *
+ * This handles the case where '*' appears as the last item in A_Indirection.
+ */
+static List *
+ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind)
+{
+   Node       *expr;
+   TupleDesc   tupleDesc;
+   int         numAttrs;
+   int         i;
+   List       *te_list = NIL;
+
+   /* Strip off the '*' to create a reference to the rowtype object */
+   ind = copyObject(ind);
+   ind->indirection = list_truncate(ind->indirection,
+                                    list_length(ind->indirection) - 1);
+
+   /* And transform that */
+   expr = transformExpr(pstate, (Node *) ind);
+
+   /* Verify it's a composite type, and get the tupdesc */
+   tupleDesc = lookup_rowtype_tupdesc(exprType(expr), exprTypmod(expr));
+
+   /* Generate a list of references to the individual fields */
+   numAttrs = tupleDesc->natts;
+   for (i = 0; i < numAttrs; i++)
+   {
+       Form_pg_attribute att = tupleDesc->attrs[i];
+       Node       *fieldnode;
+       TargetEntry *te;
+
+       if (att->attisdropped)
+           continue;
+
+       /*
+        * If we got a whole-row Var from the rowtype reference, we can
+        * expand the fields as simple Vars.  Otherwise we must generate
+        * multiple copies of the rowtype reference and do FieldSelects.
+        */
+       if (IsA(expr, Var) &&
+           ((Var *) expr)->varattno == InvalidAttrNumber)
+       {
+           Var        *var = (Var *) expr;
+
+           fieldnode = (Node *) makeVar(var->varno,
+                                        i + 1,
+                                        att->atttypid,
+                                        att->atttypmod,
+                                        var->varlevelsup);
+       }
+       else
+       {
+           FieldSelect *fselect = makeNode(FieldSelect);
+
+           fselect->arg = (Expr *) copyObject(expr);
+           fselect->fieldnum = i + 1;
+           fselect->resulttype = att->atttypid;
+           fselect->resulttypmod = att->atttypmod;
+
+           fieldnode = (Node *) fselect;
+       }
+
+       te = makeNode(TargetEntry);
+       te->resdom = makeResdom((AttrNumber) pstate->p_next_resno++,
+                               att->atttypid,
+                               att->atttypmod,
+                               pstrdup(NameStr(att->attname)),
+                               false);
+       te->expr = (Expr *) fieldnode;
+       te_list = lappend(te_list, te);
+   }
+
+   return te_list;
+}
+
 /*
  * FigureColname -
  *   if the name of the resulting column is not specified in the target
index 8ad60423c78e7e686fc93ccc1e22fa0608cd1933..362480b250b50f09f9cff7c3beb96fe4304fb7be 100644 (file)
@@ -36,7 +36,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.7 2004/06/05 01:55:05 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.8 2004/06/19 18:19:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -409,8 +409,8 @@ lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError)
        if (typentry->tupDesc == NULL && !noError)
            ereport(ERROR,
                    (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                    errmsg("type %u is not composite",
-                           type_id)));
+                    errmsg("type %s is not composite",
+                           format_type_be(type_id))));
        return typentry->tupDesc;
    }
    else