]> granicus.if.org Git - postgresql/commitdiff
Fix oversight in recent rowtype-handling improvements: transformTargetList
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 19 Jun 2004 18:19:56 +0000 (18:19 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
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