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 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* some of the executor utility code such as "ExecTypeFromTL" should be * some of the executor utility code such as "ExecTypeFromTL" should be
...@@ -554,3 +554,54 @@ BuildDescForRelation(List *schema) ...@@ -554,3 +554,54 @@ BuildDescForRelation(List *schema)
return desc; 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 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -850,11 +850,6 @@ doDeletion(const ObjectAddress *object)
* *
* rtable is the rangetable to be used to interpret Vars with varlevelsup=0. * rtable is the rangetable to be used to interpret Vars with varlevelsup=0.
* It can be NIL if no such variables are expected. * 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 void
recordDependencyOnExpr(const ObjectAddress *depender, recordDependencyOnExpr(const ObjectAddress *depender,
...@@ -975,6 +970,13 @@ recordDependencyOnSingleRelExpr(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 * 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 * scanning the query rangetable, but instead scan each individual entry
* of the alias list when we find a reference to it. * 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 static bool
find_expr_references_walker(Node *node, find_expr_references_walker(Node *node,
...@@ -1033,6 +1035,10 @@ find_expr_references_walker(Node *node, ...@@ -1033,6 +1035,10 @@ find_expr_references_walker(Node *node,
Const *con = (Const *) node; Const *con = (Const *) node;
Oid objoid; 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 * If it's a regclass or similar literal referring to an existing
* object, add a reference to that object. (Currently, only the * object, add a reference to that object. (Currently, only the
...@@ -1081,6 +1087,14 @@ find_expr_references_walker(Node *node, ...@@ -1081,6 +1087,14 @@ find_expr_references_walker(Node *node,
} }
return false; 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)) if (IsA(node, FuncExpr))
{ {
FuncExpr *funcexpr = (FuncExpr *) node; FuncExpr *funcexpr = (FuncExpr *) node;
...@@ -1134,6 +1148,29 @@ find_expr_references_walker(Node *node, ...@@ -1134,6 +1148,29 @@ find_expr_references_walker(Node *node,
/* Extra work needed here if we ever need this case */ /* Extra work needed here if we ever need this case */
elog(ERROR, "already-planned subqueries not supported"); 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)) if (IsA(node, RowCompareExpr))
{ {
RowCompareExpr *rcexpr = (RowCompareExpr *) node; RowCompareExpr *rcexpr = (RowCompareExpr *) node;
...@@ -1151,6 +1188,13 @@ find_expr_references_walker(Node *node, ...@@ -1151,6 +1188,13 @@ find_expr_references_walker(Node *node,
} }
/* fall through to examine arguments */ /* 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)) if (IsA(node, Query))
{ {
/* Recurse into RTE subquery or not-yet-planned sublink subquery */ /* Recurse into RTE subquery or not-yet-planned sublink subquery */
...@@ -1160,17 +1204,32 @@ find_expr_references_walker(Node *node, ...@@ -1160,17 +1204,32 @@ find_expr_references_walker(Node *node,
/* /*
* Add whole-relation refs for each plain relation mentioned in the * Add whole-relation refs for each plain relation mentioned in the
* subquery's rtable. (Note: query_tree_walker takes care of * subquery's rtable, as well as datatype refs for any datatypes used
* recursing into RTE_FUNCTION and RTE_SUBQUERY RTEs, so no need to do * as a RECORD function's output. (Note: query_tree_walker takes care
* that here. But keep it from looking at join alias lists.) * 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) foreach(rtable, query->rtable)
{ {
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rtable); RangeTblEntry *rte = (RangeTblEntry *) lfirst(rtable);
ListCell *ct;
if (rte->rtekind == RTE_RELATION) switch (rte->rtekind)
add_object_address(OCLASS_CLASS, rte->relid, 0, {
&context->addrs); 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 */ /* Examine substructure of query */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -202,7 +202,9 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
} }
else if (functypclass == TYPEFUNC_RECORD) else if (functypclass == TYPEFUNC_RECORD)
{ {
tupdesc = BuildDescForRelation(rte->coldeflist); tupdesc = BuildDescFromLists(rte->eref->colnames,
rte->funccoltypes,
rte->funccoltypmods);
} }
else else
{ {
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -1355,7 +1355,8 @@ _copyRangeTblEntry(RangeTblEntry *from)
COPY_SCALAR_FIELD(relid); COPY_SCALAR_FIELD(relid);
COPY_NODE_FIELD(subquery); COPY_NODE_FIELD(subquery);
COPY_NODE_FIELD(funcexpr); COPY_NODE_FIELD(funcexpr);
COPY_NODE_FIELD(coldeflist); COPY_NODE_FIELD(funccoltypes);
COPY_NODE_FIELD(funccoltypmods);
COPY_SCALAR_FIELD(jointype); COPY_SCALAR_FIELD(jointype);
COPY_NODE_FIELD(joinaliasvars); COPY_NODE_FIELD(joinaliasvars);
COPY_NODE_FIELD(alias); COPY_NODE_FIELD(alias);
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -1730,7 +1730,8 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
COMPARE_SCALAR_FIELD(relid); COMPARE_SCALAR_FIELD(relid);
COMPARE_NODE_FIELD(subquery); COMPARE_NODE_FIELD(subquery);
COMPARE_NODE_FIELD(funcexpr); COMPARE_NODE_FIELD(funcexpr);
COMPARE_NODE_FIELD(coldeflist); COMPARE_NODE_FIELD(funccoltypes);
COMPARE_NODE_FIELD(funccoltypmods);
COMPARE_SCALAR_FIELD(jointype); COMPARE_SCALAR_FIELD(jointype);
COMPARE_NODE_FIELD(joinaliasvars); COMPARE_NODE_FIELD(joinaliasvars);
COMPARE_NODE_FIELD(alias); COMPARE_NODE_FIELD(alias);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* Every node type that can appear in stored rules' parsetrees *must* * Every node type that can appear in stored rules' parsetrees *must*
...@@ -1450,7 +1450,7 @@ _outTypeName(StringInfo str, TypeName *node) ...@@ -1450,7 +1450,7 @@ _outTypeName(StringInfo str, TypeName *node)
WRITE_BOOL_FIELD(pct_type); WRITE_BOOL_FIELD(pct_type);
WRITE_INT_FIELD(typmod); WRITE_INT_FIELD(typmod);
WRITE_NODE_FIELD(arrayBounds); WRITE_NODE_FIELD(arrayBounds);
/* location is deliberately not stored */ WRITE_INT_FIELD(location);
} }
static void static void
...@@ -1580,7 +1580,8 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node) ...@@ -1580,7 +1580,8 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
break; break;
case RTE_FUNCTION: case RTE_FUNCTION:
WRITE_NODE_FIELD(funcexpr); WRITE_NODE_FIELD(funcexpr);
WRITE_NODE_FIELD(coldeflist); WRITE_NODE_FIELD(funccoltypes);
WRITE_NODE_FIELD(funccoltypmods);
break; break;
case RTE_JOIN: case RTE_JOIN:
WRITE_ENUM_FIELD(jointype, JoinType); WRITE_ENUM_FIELD(jointype, JoinType);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* Path and Plan nodes do not have any readfuncs support, because we * Path and Plan nodes do not have any readfuncs support, because we
...@@ -863,42 +863,6 @@ _readFromExpr(void) ...@@ -863,42 +863,6 @@ _readFromExpr(void)
* Stuff from parsenodes.h. * 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 * _readRangeTblEntry
*/ */
...@@ -923,7 +887,8 @@ _readRangeTblEntry(void) ...@@ -923,7 +887,8 @@ _readRangeTblEntry(void)
break; break;
case RTE_FUNCTION: case RTE_FUNCTION:
READ_NODE_FIELD(funcexpr); READ_NODE_FIELD(funcexpr);
READ_NODE_FIELD(coldeflist); READ_NODE_FIELD(funccoltypes);
READ_NODE_FIELD(funccoltypmods);
break; break;
case RTE_JOIN: case RTE_JOIN:
READ_ENUM_FIELD(jointype, JoinType); READ_ENUM_FIELD(jointype, JoinType);
...@@ -1042,10 +1007,6 @@ parseNodeString(void) ...@@ -1042,10 +1007,6 @@ parseNodeString(void)
return_value = _readJoinExpr(); return_value = _readJoinExpr();
else if (MATCH("FROMEXPR", 8)) else if (MATCH("FROMEXPR", 8))
return_value = _readFromExpr(); return_value = _readFromExpr();
else if (MATCH("COLUMNDEF", 9))
return_value = _readColumnDef();
else if (MATCH("TYPENAME", 8))
return_value = _readTypeName();
else if (MATCH("RTE", 3)) else if (MATCH("RTE", 3))
return_value = _readRangeTblEntry(); return_value = _readRangeTblEntry();
else if (MATCH("NOTIFY", 6)) else if (MATCH("NOTIFY", 6))
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -537,23 +537,27 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
} }
/* /*
* If a coldeflist is supplied, ensure it defines a legal set of names (no * OK, build an RTE for the function.
* duplicates) and datatypes (no pseudo-types, for instance). */
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) if (r->coldeflist)
{ {
TupleDesc tupdesc; TupleDesc tupdesc;
tupdesc = BuildDescForRelation(r->coldeflist); tupdesc = BuildDescFromLists(rte->eref->colnames,
rte->funccoltypes,
rte->funccoltypmods);
CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE); CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE);
} }
/*
* OK, build an RTE for the function.
*/
rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr,
r, true);
return rte; return rte;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -835,7 +835,8 @@ addRangeTableEntryForFunction(ParseState *pstate,
rte->relid = InvalidOid; rte->relid = InvalidOid;
rte->subquery = NULL; rte->subquery = NULL;
rte->funcexpr = funcexpr; rte->funcexpr = funcexpr;
rte->coldeflist = coldeflist; rte->funccoltypes = NIL;
rte->funccoltypmods = NIL;
rte->alias = alias; rte->alias = alias;
eref = makeAlias(alias ? alias->aliasname : funcname, NIL); eref = makeAlias(alias ? alias->aliasname : funcname, NIL);
...@@ -883,14 +884,28 @@ addRangeTableEntryForFunction(ParseState *pstate, ...@@ -883,14 +884,28 @@ addRangeTableEntryForFunction(ParseState *pstate,
{ {
ListCell *col; 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) foreach(col, coldeflist)
{ {
ColumnDef *n = lfirst(col); ColumnDef *n = (ColumnDef *) lfirst(col);
char *attrname; char *attrname;
Oid attrtype;
int32 attrtypmod;
attrname = pstrdup(n->colname); 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)); 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 else
...@@ -1181,36 +1196,26 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, ...@@ -1181,36 +1196,26 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
} }
else if (functypclass == TYPEFUNC_RECORD) else if (functypclass == TYPEFUNC_RECORD)
{ {
List *coldeflist = rte->coldeflist; if (colnames)
ListCell *col; *colnames = copyObject(rte->eref->colnames);
int attnum = 0; if (colvars)
foreach(col, coldeflist)
{ {
ColumnDef *colDef = lfirst(col); ListCell *l1;
ListCell *l2;
int attnum = 0;
attnum++; forboth(l1, rte->funccoltypes, l2, rte->funccoltypmods)
if (colnames)
{
char *attrname;
attrname = pstrdup(colDef->colname);
*colnames = lappend(*colnames, makeString(attrname));
}
if (colvars)
{ {
Oid attrtype = lfirst_oid(l1);
int32 attrtypmod = lfirst_int(l2);
Var *varnode; Var *varnode;
Oid atttypid;
atttypid = typenameTypeId(NULL, colDef->typename);
attnum++;
varnode = makeVar(rtindex, varnode = makeVar(rtindex,
attnum, attnum,
atttypid, attrtype,
colDef->typename->typmod, attrtypmod,
sublevels_up); sublevels_up);
*colvars = lappend(*colvars, varnode); *colvars = lappend(*colvars, varnode);
} }
} }
...@@ -1548,10 +1553,8 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, ...@@ -1548,10 +1553,8 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
} }
else if (functypclass == TYPEFUNC_RECORD) else if (functypclass == TYPEFUNC_RECORD)
{ {
ColumnDef *colDef = list_nth(rte->coldeflist, attnum - 1); *vartype = list_nth_oid(rte->funccoltypes, attnum - 1);
*vartypmod = list_nth_int(rte->funccoltypmods, attnum - 1);
*vartype = typenameTypeId(NULL, colDef->typename);
*vartypmod = colDef->typename->typmod;
} }
else else
{ {
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* ruleutils.c - Functions to convert stored expressions/querytrees * ruleutils.c - Functions to convert stored expressions/querytrees
* back to source text * 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" #include "postgres.h"
...@@ -176,7 +176,7 @@ static void get_from_clause_item(Node *jtnode, Query *query, ...@@ -176,7 +176,7 @@ static void get_from_clause_item(Node *jtnode, Query *query,
deparse_context *context); deparse_context *context);
static void get_from_clause_alias(Alias *alias, RangeTblEntry *rte, static void get_from_clause_alias(Alias *alias, RangeTblEntry *rte,
deparse_context *context); 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); deparse_context *context);
static void get_opclass_name(Oid opclass, Oid actual_datatype, static void get_opclass_name(Oid opclass, Oid actual_datatype,
StringInfo buf); StringInfo buf);
...@@ -1497,13 +1497,16 @@ deparse_context_for_subplan(const char *name, List *tlist, ...@@ -1497,13 +1497,16 @@ deparse_context_for_subplan(const char *name, List *tlist,
/* /*
* We create an RTE_SPECIAL RangeTblEntry, and store the given 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 * to get_name_for_var_field(). RTE_SPECIAL nodes shouldn't appear in
* deparse contexts otherwise. * 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->rtekind = RTE_SPECIAL;
rte->relid = InvalidOid; rte->relid = InvalidOid;
rte->coldeflist = tlist; rte->funccoltypes = tlist;
rte->eref = makeAlias(name, attrs); rte->eref = makeAlias(name, attrs);
rte->inh = false; rte->inh = false;
rte->inFromCl = true; rte->inFromCl = true;
...@@ -2678,9 +2681,9 @@ get_name_for_var_field(Var *var, int fieldno, ...@@ -2678,9 +2681,9 @@ get_name_for_var_field(Var *var, int fieldno,
* Look into the subplan's target list to get the referenced * Look into the subplan's target list to get the referenced
* expression, and then pass it to get_expr_result_type(). * 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) if (ste != NULL)
expr = (Node *) ste->expr; expr = (Node *) ste->expr;
...@@ -4205,7 +4208,6 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) ...@@ -4205,7 +4208,6 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
{ {
int varno = ((RangeTblRef *) jtnode)->rtindex; int varno = ((RangeTblRef *) jtnode)->rtindex;
RangeTblEntry *rte = rt_fetch(varno, query->rtable); RangeTblEntry *rte = rt_fetch(varno, query->rtable);
List *coldeflist = NIL;
bool gavealias = false; bool gavealias = false;
switch (rte->rtekind) switch (rte->rtekind)
...@@ -4226,8 +4228,6 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) ...@@ -4226,8 +4228,6 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
case RTE_FUNCTION: case RTE_FUNCTION:
/* Function RTE */ /* Function RTE */
get_rule_expr(rte->funcexpr, context, true); get_rule_expr(rte->funcexpr, context, true);
/* might need to emit column list for RECORD function */
coldeflist = rte->coldeflist;
break; break;
default: default:
elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind); elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
...@@ -4265,27 +4265,37 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) ...@@ -4265,27 +4265,37 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
gavealias = true; gavealias = true;
} }
if (coldeflist != NIL) if (rte->rtekind == RTE_FUNCTION)
{ {
if (!gavealias) if (rte->funccoltypes != NIL)
appendStringInfo(buf, " AS "); {
get_from_clause_coldeflist(coldeflist, context); /* 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 else
{ {
/* /*
* For a function RTE, always emit a complete column alias list; * For non-function RTEs, just report whatever the user originally
* this is to protect against possible instability of the default * gave as column aliases.
* column names (eg, from altering parameter names). Otherwise
* just report whatever the user originally gave as column
* aliases.
*/ */
if (rte->rtekind == RTE_FUNCTION) get_from_clause_alias(rte->alias, rte, context);
get_from_clause_alias(rte->eref, rte, context);
else
get_from_clause_alias(rte->alias, rte, context);
} }
} }
else if (IsA(jtnode, JoinExpr)) else if (IsA(jtnode, JoinExpr))
{ {
...@@ -4463,30 +4473,35 @@ get_from_clause_alias(Alias *alias, RangeTblEntry *rte, ...@@ -4463,30 +4473,35 @@ get_from_clause_alias(Alias *alias, RangeTblEntry *rte,
* responsible for ensuring that an alias or AS is present before it. * responsible for ensuring that an alias or AS is present before it.
*/ */
static void 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; StringInfo buf = context->buf;
ListCell *col; ListCell *l1;
ListCell *l2;
ListCell *l3;
int i = 0; int i = 0;
appendStringInfoChar(buf, '('); appendStringInfoChar(buf, '(');
foreach(col, coldeflist) l2 = list_head(types);
l3 = list_head(typmods);
foreach(l1, names)
{ {
ColumnDef *n = lfirst(col); char *attname = strVal(lfirst(l1));
char *attname; Oid atttypid;
Oid atttypeid;
int32 atttypmod; int32 atttypmod;
attname = n->colname; atttypid = lfirst_oid(l2);
atttypeid = typenameTypeId(NULL, n->typename); l2 = lnext(l2);
atttypmod = n->typename->typmod; atttypmod = lfirst_int(l3);
l3 = lnext(l3);
if (i > 0) if (i > 0)
appendStringInfo(buf, ", "); appendStringInfo(buf, ", ");
appendStringInfo(buf, "%s %s", appendStringInfo(buf, "%s %s",
quote_identifier(attname), quote_identifier(attname),
format_type_with_typemod(atttypeid, atttypmod)); format_type_with_typemod(atttypid, atttypmod));
i++; i++;
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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, ...@@ -92,4 +92,6 @@ extern void TupleDescInitEntry(TupleDesc desc,
extern TupleDesc BuildDescForRelation(List *schema); extern TupleDesc BuildDescForRelation(List *schema);
extern TupleDesc BuildDescFromLists(List *names, List *types, List *typmods);
#endif /* TUPDESC_H */ #endif /* TUPDESC_H */
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200603101 #define CATALOG_VERSION_NO 200603151
#endif #endif
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -362,8 +362,8 @@ typedef struct RangeFunction
NodeTag type; NodeTag type;
Node *funccallnode; /* untransformed function call tree */ Node *funccallnode; /* untransformed function call tree */
Alias *alias; /* table alias & optional column aliases */ Alias *alias; /* table alias & optional column aliases */
List *coldeflist; /* list of ColumnDef nodes for runtime List *coldeflist; /* list of ColumnDef nodes to describe
* assignment of RECORD TupleDesc */ * result of function returning RECORD */
} RangeFunction; } RangeFunction;
/* /*
...@@ -547,10 +547,14 @@ typedef struct RangeTblEntry ...@@ -547,10 +547,14 @@ typedef struct RangeTblEntry
/* /*
* Fields valid for a function RTE (else NULL): * 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 */ Node *funcexpr; /* expression tree for func call */
List *coldeflist; /* list of ColumnDef nodes for runtime List *funccoltypes; /* OID list of column type OIDs */
* assignment of RECORD TupleDesc */ List *funccoltypmods; /* integer list of column typmods */
/* /*
* Fields valid for a join RTE (else NULL/zero): * 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