Commit 23160139 authored by Tom Lane's avatar Tom Lane

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.
parent 5981b9d0
......@@ -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;
}
......@@ -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 */
......
......@@ -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
{
......
......@@ -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);
......
......@@ -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);
......
......@@ -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);
......
......@@ -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))
......
......@@ -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;
}
......
......@@ -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
{
......
......@@ -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++;
}
......
......@@ -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 */
......@@ -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
......@@ -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):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment