Clean up representation of function RTEs for functions returning RECORD.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 16 Mar 2006 00:31:55 +0000 (00:31 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 16 Mar 2006 00:31:55 +0000 (00:31 +0000)
The original coding stored the raw parser output (ColumnDef and TypeName
nodes) which was ugly, bulky, and wrong because it failed to create any
dependency on the referenced datatype --- and in fact would not track type
renamings and suchlike.  Instead store a list of column type OIDs in the
RTE.

Also fix up general failure of recordDependencyOnExpr to do anything sane
about recording dependencies on datatypes.  While there are many cases where
there will be an indirect dependency (eg if an operator returns a datatype,
the dependency on the operator is enough), we do have to record the datatype
as a separate dependency in examples like CoerceToDomain.

initdb forced because of change of stored rules.

13 files changed:
src/backend/access/common/tupdesc.c
src/backend/catalog/dependency.c
src/backend/executor/nodeFunctionscan.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/parser/parse_clause.c
src/backend/parser/parse_relation.c
src/backend/utils/adt/ruleutils.c
src/include/access/tupdesc.h
src/include/catalog/catversion.h
src/include/nodes/parsenodes.h

index 8726797524b12aa25dbb3d193998d53d6356bc34..020011966f010a69f5002928c4661b87ddc0f6e0 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.115 2006/03/14 22:48:18 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.116 2006/03/16 00:31:54 tgl Exp $
  *
  * NOTES
  *   some of the executor utility code such as "ExecTypeFromTL" should be
@@ -554,3 +554,54 @@ BuildDescForRelation(List *schema)
 
    return desc;
 }
+
+/*
+ * BuildDescFromLists
+ *
+ * Build a TupleDesc given lists of column names (as String nodes),
+ * column type OIDs, and column typmods.  No constraints are generated.
+ *
+ * This is essentially a cut-down version of BuildDescForRelation for use
+ * with functions returning RECORD.
+ */
+TupleDesc
+BuildDescFromLists(List *names, List *types, List *typmods)
+{
+   int         natts;
+   AttrNumber  attnum;
+   ListCell   *l1;
+   ListCell   *l2;
+   ListCell   *l3;
+   TupleDesc   desc;
+
+   natts = list_length(names);
+   Assert(natts == list_length(types));
+   Assert(natts == list_length(typmods));
+
+   /*
+    * allocate a new tuple descriptor
+    */
+   desc = CreateTemplateTupleDesc(natts, false);
+
+   attnum = 0;
+
+   l2 = list_head(types);
+   l3 = list_head(typmods);
+   foreach(l1, names)
+   {
+       char       *attname = strVal(lfirst(l1));
+       Oid         atttypid;
+       int32       atttypmod;
+
+       atttypid = lfirst_oid(l2);
+       l2 = lnext(l2);
+       atttypmod = lfirst_int(l3);
+       l3 = lnext(l3);
+
+       attnum++;
+
+       TupleDescInitEntry(desc, attnum, attname, atttypid, atttypmod, 0);
+   }
+
+   return desc;
+}
index 4ed850f1a5bc580538d5e867b270946e9df26fdf..160647aed72311c8175adb2c20fb1feee31d153b 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.50 2006/03/05 15:58:22 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.51 2006/03/16 00:31:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -850,11 +850,6 @@ doDeletion(const ObjectAddress *object)
  *
  * rtable is the rangetable to be used to interpret Vars with varlevelsup=0.
  * It can be NIL if no such variables are expected.
- *
- * XXX is it important to create dependencies on the datatypes mentioned in
- * the expression? In most cases this would be redundant (eg, a ref to an
- * operator indirectly references its input and output datatypes), but I'm
- * not quite convinced there are no cases where we need it.
  */
 void
 recordDependencyOnExpr(const ObjectAddress *depender,
@@ -975,6 +970,13 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
  * To do so, we do not scan the joinaliasvars list of a join RTE while
  * scanning the query rangetable, but instead scan each individual entry
  * of the alias list when we find a reference to it.
+ *
+ * Note: in many cases we do not need to create dependencies on the datatypes
+ * involved in an expression, because we'll have an indirect dependency via
+ * some other object.  For instance Var nodes depend on a column which depends
+ * on the datatype, and OpExpr nodes depend on the operator which depends on
+ * the datatype.  However we do need a type dependency if there is no such
+ * indirect dependency, as for example in Const and CoerceToDomain nodes.
  */
 static bool
 find_expr_references_walker(Node *node,
@@ -1033,6 +1035,10 @@ find_expr_references_walker(Node *node,
        Const      *con = (Const *) node;
        Oid         objoid;
 
+       /* A constant must depend on the constant's datatype */
+       add_object_address(OCLASS_TYPE, con->consttype, 0,
+                          &context->addrs);
+
        /*
         * If it's a regclass or similar literal referring to an existing
         * object, add a reference to that object.  (Currently, only the
@@ -1081,6 +1087,14 @@ find_expr_references_walker(Node *node,
        }
        return false;
    }
+   if (IsA(node, Param))
+   {
+       Param      *param = (Param *) node;
+
+       /* A parameter must depend on the parameter's datatype */
+       add_object_address(OCLASS_TYPE, param->paramtype, 0,
+                          &context->addrs);
+   }
    if (IsA(node, FuncExpr))
    {
        FuncExpr   *funcexpr = (FuncExpr *) node;
@@ -1134,6 +1148,29 @@ find_expr_references_walker(Node *node,
        /* Extra work needed here if we ever need this case */
        elog(ERROR, "already-planned subqueries not supported");
    }
+   if (IsA(node, RelabelType))
+   {
+       RelabelType    *relab = (RelabelType *) node;
+
+       /* since there is no function dependency, need to depend on type */
+       add_object_address(OCLASS_TYPE, relab->resulttype, 0,
+                          &context->addrs);
+   }
+   if (IsA(node, ConvertRowtypeExpr))
+   {
+       ConvertRowtypeExpr *cvt = (ConvertRowtypeExpr *) node;
+
+       /* since there is no function dependency, need to depend on type */
+       add_object_address(OCLASS_TYPE, cvt->resulttype, 0,
+                          &context->addrs);
+   }
+   if (IsA(node, RowExpr))
+   {
+       RowExpr    *rowexpr = (RowExpr *) node;
+
+       add_object_address(OCLASS_TYPE, rowexpr->row_typeid, 0,
+                          &context->addrs);
+   }
    if (IsA(node, RowCompareExpr))
    {
        RowCompareExpr *rcexpr = (RowCompareExpr *) node;
@@ -1151,6 +1188,13 @@ find_expr_references_walker(Node *node,
        }
        /* fall through to examine arguments */
    }
+   if (IsA(node, CoerceToDomain))
+   {
+       CoerceToDomain *cd = (CoerceToDomain *) node;
+
+       add_object_address(OCLASS_TYPE, cd->resulttype, 0,
+                          &context->addrs);
+   }
    if (IsA(node, Query))
    {
        /* Recurse into RTE subquery or not-yet-planned sublink subquery */
@@ -1160,17 +1204,32 @@ find_expr_references_walker(Node *node,
 
        /*
         * Add whole-relation refs for each plain relation mentioned in the
-        * subquery's rtable.  (Note: query_tree_walker takes care of
-        * recursing into RTE_FUNCTION and RTE_SUBQUERY RTEs, so no need to do
-        * that here.  But keep it from looking at join alias lists.)
+        * subquery's rtable, as well as datatype refs for any datatypes used
+        * as a RECORD function's output.  (Note: query_tree_walker takes care
+        * of recursing into RTE_FUNCTION and RTE_SUBQUERY RTEs, so no need to
+        * do that here.  But keep it from looking at join alias lists.)
         */
        foreach(rtable, query->rtable)
        {
            RangeTblEntry *rte = (RangeTblEntry *) lfirst(rtable);
+           ListCell   *ct;
 
-           if (rte->rtekind == RTE_RELATION)
-               add_object_address(OCLASS_CLASS, rte->relid, 0,
-                                  &context->addrs);
+           switch (rte->rtekind)
+           {
+               case RTE_RELATION:
+                   add_object_address(OCLASS_CLASS, rte->relid, 0,
+                                      &context->addrs);
+                   break;
+               case RTE_FUNCTION:
+                   foreach(ct, rte->funccoltypes)
+                   {
+                       add_object_address(OCLASS_TYPE, lfirst_oid(ct), 0,
+                                          &context->addrs);
+                   }
+                   break;
+               default:
+                   break;
+           }
        }
 
        /* Examine substructure of query */
index 45cbc6c1fcb6fe2e9ef8d2cad44066597aa490c9..494ac209a926492dbdb202bfe7afa5da60b8b6dc 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.37 2006/03/05 15:58:26 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.38 2006/03/16 00:31:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -202,7 +202,9 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
    }
    else if (functypclass == TYPEFUNC_RECORD)
    {
-       tupdesc = BuildDescForRelation(rte->coldeflist);
+       tupdesc = BuildDescFromLists(rte->eref->colnames,
+                                    rte->funccoltypes,
+                                    rte->funccoltypmods);
    }
    else
    {
index b38efcdec8b581c7f60e3bd6a5ff7b8008d06061..0a0e5f40b0bb79ebe2a7b758a251d568e34f96bb 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.330 2006/03/14 22:48:19 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.331 2006/03/16 00:31:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1355,7 +1355,8 @@ _copyRangeTblEntry(RangeTblEntry *from)
    COPY_SCALAR_FIELD(relid);
    COPY_NODE_FIELD(subquery);
    COPY_NODE_FIELD(funcexpr);
-   COPY_NODE_FIELD(coldeflist);
+   COPY_NODE_FIELD(funccoltypes);
+   COPY_NODE_FIELD(funccoltypmods);
    COPY_SCALAR_FIELD(jointype);
    COPY_NODE_FIELD(joinaliasvars);
    COPY_NODE_FIELD(alias);
index 0275b01c28e5f99a6f8903bddae9d2f676fae71f..9696925c2f32c6263370184aec8de603fbc70e14 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.266 2006/03/14 22:48:19 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.267 2006/03/16 00:31:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1730,7 +1730,8 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
    COMPARE_SCALAR_FIELD(relid);
    COMPARE_NODE_FIELD(subquery);
    COMPARE_NODE_FIELD(funcexpr);
-   COMPARE_NODE_FIELD(coldeflist);
+   COMPARE_NODE_FIELD(funccoltypes);
+   COMPARE_NODE_FIELD(funccoltypmods);
    COMPARE_SCALAR_FIELD(jointype);
    COMPARE_NODE_FIELD(joinaliasvars);
    COMPARE_NODE_FIELD(alias);
index ef87fedbc216ab9c376693ba0769d029635d3ff3..70e48c071b31c13489080e49260af38bfc0bc1ad 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.270 2006/03/14 22:48:19 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.271 2006/03/16 00:31:54 tgl Exp $
  *
  * NOTES
  *   Every node type that can appear in stored rules' parsetrees *must*
@@ -1450,7 +1450,7 @@ _outTypeName(StringInfo str, TypeName *node)
    WRITE_BOOL_FIELD(pct_type);
    WRITE_INT_FIELD(typmod);
    WRITE_NODE_FIELD(arrayBounds);
-   /* location is deliberately not stored */
+   WRITE_INT_FIELD(location);
 }
 
 static void
@@ -1580,7 +1580,8 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
            break;
        case RTE_FUNCTION:
            WRITE_NODE_FIELD(funcexpr);
-           WRITE_NODE_FIELD(coldeflist);
+           WRITE_NODE_FIELD(funccoltypes);
+           WRITE_NODE_FIELD(funccoltypmods);
            break;
        case RTE_JOIN:
            WRITE_ENUM_FIELD(jointype, JoinType);
index c7490a9567ccc59e8df2f8be6f6713a3c03052fb..4e762d3a2ecbcff27208ccc75d3dd44e9a6b54ac 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.186 2006/03/14 22:48:19 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.187 2006/03/16 00:31:55 tgl Exp $
  *
  * NOTES
  *   Path and Plan nodes do not have any readfuncs support, because we
@@ -863,42 +863,6 @@ _readFromExpr(void)
  * Stuff from parsenodes.h.
  */
 
-static ColumnDef *
-_readColumnDef(void)
-{
-   READ_LOCALS(ColumnDef);
-
-   READ_STRING_FIELD(colname);
-   READ_NODE_FIELD(typename);
-   READ_INT_FIELD(inhcount);
-   READ_BOOL_FIELD(is_local);
-   READ_BOOL_FIELD(is_not_null);
-   READ_NODE_FIELD(raw_default);
-   READ_STRING_FIELD(cooked_default);
-   READ_NODE_FIELD(constraints);
-   READ_NODE_FIELD(support);
-
-   READ_DONE();
-}
-
-static TypeName *
-_readTypeName(void)
-{
-   READ_LOCALS(TypeName);
-
-   READ_NODE_FIELD(names);
-   READ_OID_FIELD(typeid);
-   READ_BOOL_FIELD(timezone);
-   READ_BOOL_FIELD(setof);
-   READ_BOOL_FIELD(pct_type);
-   READ_INT_FIELD(typmod);
-   READ_NODE_FIELD(arrayBounds);
-   /* location is deliberately not stored */
-   local_node->location = -1;
-
-   READ_DONE();
-}
-
 /*
  * _readRangeTblEntry
  */
@@ -923,7 +887,8 @@ _readRangeTblEntry(void)
            break;
        case RTE_FUNCTION:
            READ_NODE_FIELD(funcexpr);
-           READ_NODE_FIELD(coldeflist);
+           READ_NODE_FIELD(funccoltypes);
+           READ_NODE_FIELD(funccoltypmods);
            break;
        case RTE_JOIN:
            READ_ENUM_FIELD(jointype, JoinType);
@@ -1042,10 +1007,6 @@ parseNodeString(void)
        return_value = _readJoinExpr();
    else if (MATCH("FROMEXPR", 8))
        return_value = _readFromExpr();
-   else if (MATCH("COLUMNDEF", 9))
-       return_value = _readColumnDef();
-   else if (MATCH("TYPENAME", 8))
-       return_value = _readTypeName();
    else if (MATCH("RTE", 3))
        return_value = _readRangeTblEntry();
    else if (MATCH("NOTIFY", 6))
index 4384a4eaab83fce99cd21736fc8e62f293a4cf0d..9bdb91b47445b9972be2433f50e11e81cdeeb8a2 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.148 2006/03/14 22:48:20 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.149 2006/03/16 00:31:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -537,23 +537,27 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
    }
 
    /*
-    * If a coldeflist is supplied, ensure it defines a legal set of names (no
-    * duplicates) and datatypes (no pseudo-types, for instance).
+    * OK, build an RTE for the function.
+    */
+   rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr,
+                                       r, true);
+
+   /*
+    * If a coldeflist was supplied, ensure it defines a legal set of names
+    * (no duplicates) and datatypes (no pseudo-types, for instance).
+    * addRangeTableEntryForFunction looked up the type names but didn't
+    * check them further than that.
     */
    if (r->coldeflist)
    {
        TupleDesc   tupdesc;
 
-       tupdesc = BuildDescForRelation(r->coldeflist);
+       tupdesc = BuildDescFromLists(rte->eref->colnames,
+                                    rte->funccoltypes,
+                                    rte->funccoltypmods);
        CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE);
    }
 
-   /*
-    * OK, build an RTE for the function.
-    */
-   rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr,
-                                       r, true);
-
    return rte;
 }
 
index d0f78b119cdf0e65b88b5b494a3f024e854233ae..e8e11ad167ad758df685e94ea61dd14496b715a7 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.120 2006/03/14 22:48:21 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.121 2006/03/16 00:31:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -835,7 +835,8 @@ addRangeTableEntryForFunction(ParseState *pstate,
    rte->relid = InvalidOid;
    rte->subquery = NULL;
    rte->funcexpr = funcexpr;
-   rte->coldeflist = coldeflist;
+   rte->funccoltypes = NIL;
+   rte->funccoltypmods = NIL;
    rte->alias = alias;
 
    eref = makeAlias(alias ? alias->aliasname : funcname, NIL);
@@ -883,14 +884,28 @@ addRangeTableEntryForFunction(ParseState *pstate,
    {
        ListCell   *col;
 
-       /* Use the column definition list to form the alias list */
+       /*
+        * Use the column definition list to form the alias list and
+        * funccoltypes/funccoltypmods lists.
+        */
        foreach(col, coldeflist)
        {
-           ColumnDef  *n = lfirst(col);
+           ColumnDef  *n = (ColumnDef *) lfirst(col);
            char       *attrname;
+           Oid         attrtype;
+           int32       attrtypmod;
 
            attrname = pstrdup(n->colname);
+           if (n->typename->setof)
+               ereport(ERROR,
+                       (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                        errmsg("column \"%s\" cannot be declared SETOF",
+                               attrname)));
            eref->colnames = lappend(eref->colnames, makeString(attrname));
+           attrtype = typenameTypeId(pstate, n->typename);
+           attrtypmod = n->typename->typmod;
+           rte->funccoltypes = lappend_oid(rte->funccoltypes, attrtype);
+           rte->funccoltypmods = lappend_int(rte->funccoltypmods, attrtypmod);
        }
    }
    else
@@ -1181,36 +1196,26 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
                }
                else if (functypclass == TYPEFUNC_RECORD)
                {
-                   List       *coldeflist = rte->coldeflist;
-                   ListCell   *col;
-                   int         attnum = 0;
-
-                   foreach(col, coldeflist)
+                   if (colnames)
+                       *colnames = copyObject(rte->eref->colnames);
+                   if (colvars)
                    {
-                       ColumnDef  *colDef = lfirst(col);
+                       ListCell   *l1;
+                       ListCell   *l2;
+                       int         attnum = 0;
 
-                       attnum++;
-                       if (colnames)
-                       {
-                           char       *attrname;
-
-                           attrname = pstrdup(colDef->colname);
-                           *colnames = lappend(*colnames, makeString(attrname));
-                       }
-
-                       if (colvars)
+                       forboth(l1, rte->funccoltypes, l2, rte->funccoltypmods)
                        {
+                           Oid         attrtype = lfirst_oid(l1);
+                           int32       attrtypmod = lfirst_int(l2);
                            Var        *varnode;
-                           Oid         atttypid;
-
-                           atttypid = typenameTypeId(NULL, colDef->typename);
 
+                           attnum++;
                            varnode = makeVar(rtindex,
                                              attnum,
-                                             atttypid,
-                                             colDef->typename->typmod,
+                                             attrtype,
+                                             attrtypmod,
                                              sublevels_up);
-
                            *colvars = lappend(*colvars, varnode);
                        }
                    }
@@ -1548,10 +1553,8 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
                }
                else if (functypclass == TYPEFUNC_RECORD)
                {
-                   ColumnDef  *colDef = list_nth(rte->coldeflist, attnum - 1);
-
-                   *vartype = typenameTypeId(NULL, colDef->typename);
-                   *vartypmod = colDef->typename->typmod;
+                   *vartype = list_nth_oid(rte->funccoltypes, attnum - 1);
+                   *vartypmod = list_nth_int(rte->funccoltypmods, attnum - 1);
                }
                else
                {
index 9942bcc291b86989f658e4d74d641d7af9d96671..714140e06179d315ad64b36802612a90d46418bf 100644 (file)
@@ -2,7 +2,7 @@
  * ruleutils.c - Functions to convert stored expressions/querytrees
  *             back to source text
  *
- *   $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.216 2006/03/14 22:48:22 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.217 2006/03/16 00:31:55 tgl Exp $
  **********************************************************************/
 
 #include "postgres.h"
@@ -176,7 +176,7 @@ static void get_from_clause_item(Node *jtnode, Query *query,
                     deparse_context *context);
 static void get_from_clause_alias(Alias *alias, RangeTblEntry *rte,
                      deparse_context *context);
-static void get_from_clause_coldeflist(List *coldeflist,
+static void get_from_clause_coldeflist(List *names, List *types, List *typmods,
                           deparse_context *context);
 static void get_opclass_name(Oid opclass, Oid actual_datatype,
                 StringInfo buf);
@@ -1497,13 +1497,16 @@ deparse_context_for_subplan(const char *name, List *tlist,
 
    /*
     * We create an RTE_SPECIAL RangeTblEntry, and store the given tlist
-    * in its coldeflist field.  This is a hack to make the tlist available
+    * in its funccoltypes field.  This is a hack to make the tlist available
     * to get_name_for_var_field().  RTE_SPECIAL nodes shouldn't appear in
     * deparse contexts otherwise.
+    *
+    * XXX this desperately needs redesign, as it fails to handle cases where
+    * we need to drill down through multiple tlists.
     */
    rte->rtekind = RTE_SPECIAL;
    rte->relid = InvalidOid;
-   rte->coldeflist = tlist;
+   rte->funccoltypes = tlist;
    rte->eref = makeAlias(name, attrs);
    rte->inh = false;
    rte->inFromCl = true;
@@ -2678,9 +2681,9 @@ get_name_for_var_field(Var *var, int fieldno,
             * Look into the subplan's target list to get the referenced
             * expression, and then pass it to get_expr_result_type().
             */
-           if (rte->coldeflist)
+           if (rte->funccoltypes)
            {
-               TargetEntry *ste = get_tle_by_resno(rte->coldeflist, attnum);
+               TargetEntry *ste = get_tle_by_resno(rte->funccoltypes, attnum);
 
                if (ste != NULL)
                    expr = (Node *) ste->expr;
@@ -4205,7 +4208,6 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
    {
        int         varno = ((RangeTblRef *) jtnode)->rtindex;
        RangeTblEntry *rte = rt_fetch(varno, query->rtable);
-       List       *coldeflist = NIL;
        bool        gavealias = false;
 
        switch (rte->rtekind)
@@ -4226,8 +4228,6 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
            case RTE_FUNCTION:
                /* Function RTE */
                get_rule_expr(rte->funcexpr, context, true);
-               /* might need to emit column list for RECORD function */
-               coldeflist = rte->coldeflist;
                break;
            default:
                elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
@@ -4265,27 +4265,37 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
            gavealias = true;
        }
 
-       if (coldeflist != NIL)
+       if (rte->rtekind == RTE_FUNCTION)
        {
-           if (!gavealias)
-               appendStringInfo(buf, " AS ");
-           get_from_clause_coldeflist(coldeflist, context);
+           if (rte->funccoltypes != NIL)
+           {
+               /* Function returning RECORD, reconstruct the columndefs */
+               if (!gavealias)
+                   appendStringInfo(buf, " AS ");
+               get_from_clause_coldeflist(rte->eref->colnames,
+                                          rte->funccoltypes,
+                                          rte->funccoltypmods,
+                                          context);
+           }
+           else
+           {
+               /*
+                * For a function RTE, always emit a complete column alias
+                * list; this is to protect against possible instability of
+                * the default column names (eg, from altering parameter
+                * names).
+                */
+               get_from_clause_alias(rte->eref, rte, context);
+           }
        }
        else
        {
            /*
-            * For a function RTE, always emit a complete column alias list;
-            * this is to protect against possible instability of the default
-            * column names (eg, from altering parameter names).  Otherwise
-            * just report whatever the user originally gave as column
-            * aliases.
+            * For non-function RTEs, just report whatever the user originally
+            * gave as column aliases.
             */
-           if (rte->rtekind == RTE_FUNCTION)
-               get_from_clause_alias(rte->eref, rte, context);
-           else
-               get_from_clause_alias(rte->alias, rte, context);
+           get_from_clause_alias(rte->alias, rte, context);
        }
-
    }
    else if (IsA(jtnode, JoinExpr))
    {
@@ -4463,30 +4473,35 @@ get_from_clause_alias(Alias *alias, RangeTblEntry *rte,
  * responsible for ensuring that an alias or AS is present before it.
  */
 static void
-get_from_clause_coldeflist(List *coldeflist, deparse_context *context)
+get_from_clause_coldeflist(List *names, List *types, List *typmods,
+                          deparse_context *context)
 {
    StringInfo  buf = context->buf;
-   ListCell   *col;
+   ListCell   *l1;
+   ListCell   *l2;
+   ListCell   *l3;
    int         i = 0;
 
    appendStringInfoChar(buf, '(');
 
-   foreach(col, coldeflist)
+   l2 = list_head(types);
+   l3 = list_head(typmods);
+   foreach(l1, names)
    {
-       ColumnDef  *n = lfirst(col);
-       char       *attname;
-       Oid         atttypeid;
+       char       *attname = strVal(lfirst(l1));
+       Oid         atttypid;
        int32       atttypmod;
 
-       attname = n->colname;
-       atttypeid = typenameTypeId(NULL, n->typename);
-       atttypmod = n->typename->typmod;
+       atttypid = lfirst_oid(l2);
+       l2 = lnext(l2);
+       atttypmod = lfirst_int(l3);
+       l3 = lnext(l3);
 
        if (i > 0)
            appendStringInfo(buf, ", ");
        appendStringInfo(buf, "%s %s",
                         quote_identifier(attname),
-                        format_type_with_typemod(atttypeid, atttypmod));
+                        format_type_with_typemod(atttypid, atttypmod));
        i++;
    }
 
index 1a5e23e1d1be110a7c347ace0aecf8b587bcf804..670ef5c92e0e7434d522816f401ea386d5f01416 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/tupdesc.h,v 1.48 2006/03/05 15:58:53 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/access/tupdesc.h,v 1.49 2006/03/16 00:31:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -92,4 +92,6 @@ extern void TupleDescInitEntry(TupleDesc desc,
 
 extern TupleDesc BuildDescForRelation(List *schema);
 
+extern TupleDesc BuildDescFromLists(List *names, List *types, List *typmods);
+
 #endif   /* TUPDESC_H */
index 4b541531f8404cd96a4dbdada472aa90952e9280..2ae52c723fce01494ecff641dde23f09b03c33f2 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.320 2006/03/10 20:15:26 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.321 2006/03/16 00:31:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 200603101
+#define CATALOG_VERSION_NO 200603151
 
 #endif
index 0e7d5bf9f51859e87503dc8102dd5f0e6cfa2621..aea32d236d9d7b2bfada58cdd69c5156e603ad2a 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.304 2006/03/14 22:48:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.305 2006/03/16 00:31:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -362,8 +362,8 @@ typedef struct RangeFunction
    NodeTag     type;
    Node       *funccallnode;   /* untransformed function call tree */
    Alias      *alias;          /* table alias & optional column aliases */
-   List       *coldeflist;     /* list of ColumnDef nodes for runtime
-                                * assignment of RECORD TupleDesc */
+   List       *coldeflist;     /* list of ColumnDef nodes to describe
+                                * result of function returning RECORD */
 } RangeFunction;
 
 /*
@@ -547,10 +547,14 @@ typedef struct RangeTblEntry
 
    /*
     * Fields valid for a function RTE (else NULL):
+    *
+    * If the function returns RECORD, funccoltypes lists the column types
+    * declared in the RTE's column type specification, and funccoltypmods
+    * lists their declared typmods.  Otherwise, both fields are NIL.
     */
    Node       *funcexpr;       /* expression tree for func call */
-   List       *coldeflist;     /* list of ColumnDef nodes for runtime
-                                * assignment of RECORD TupleDesc */
+   List       *funccoltypes;   /* OID list of column type OIDs */
+   List       *funccoltypmods; /* integer list of column typmods */
 
    /*
     * Fields valid for a join RTE (else NULL/zero):