summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2006-03-16 00:31:55 +0000
committerTom Lane2006-03-16 00:31:55 +0000
commit41c3b40d151b96ce3989febceee95186e710b423 (patch)
treeaed27eb7883cf8477c92de3f18d477dcf9b085c8
parentb666a033d89483cbd11243fbfc5dda616e775d99 (diff)
Clean up representation of function RTEs for functions returning RECORD.
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.
-rw-r--r--src/backend/access/common/tupdesc.c51
-rw-r--r--src/backend/catalog/dependency.c81
-rw-r--r--src/backend/executor/nodeFunctionscan.c4
-rw-r--r--src/backend/nodes/copyfuncs.c3
-rw-r--r--src/backend/nodes/equalfuncs.c3
-rw-r--r--src/backend/nodes/outfuncs.c5
-rw-r--r--src/backend/nodes/readfuncs.c43
-rw-r--r--src/backend/parser/parse_clause.c22
-rw-r--r--src/backend/parser/parse_relation.c61
-rw-r--r--src/backend/utils/adt/ruleutils.c79
-rw-r--r--src/include/access/tupdesc.h2
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/nodes/parsenodes.h12
13 files changed, 236 insertions, 132 deletions
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 3f022da86a..7d21bc4d82 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -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;
+}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index cc9030acad..553d6dc01b 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -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 */
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index e6d1420fa4..c7077782a4 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -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
{
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 9cf1b22b1b..2ff2933590 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -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);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 0913890cb3..786016c4e5 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -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);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 538798b921..13dcf5311e 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -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);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 4b2f2bd906..9073a0b650 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -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))
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index f317a80857..51d8b6fbee 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -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;
}
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index c5de93f13c..a6382c6f07 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -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
{
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 406517fe13..54c2a1a7c9 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -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++;
}
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index 2b2eb9a0af..33837e877b 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -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 */
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 2b09ca3f47..3167f0246a 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200603101
+#define CATALOG_VERSION_NO 200603151
#endif
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index a9cc663874..c07e34055c 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -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):