1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-24 09:27:52 +03:00

First pass at set-returning-functions in FROM, by Joe Conway with

some kibitzing from Tom Lane.  Not everything works yet, and there's
no documentation or regression test, but let's commit this so Joe
doesn't need to cope with tracking changes in so many files ...
This commit is contained in:
Tom Lane
2002-05-12 20:10:05 +00:00
parent 71009354c8
commit f9e4f611a1
48 changed files with 1813 additions and 329 deletions

View File

@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.233 2002/04/28 19:54:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.234 2002/05/12 20:10:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2679,7 +2679,7 @@ transformForUpdate(Query *qry, List *forUpdate)
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
++i;
if (rte->subquery)
if (rte->rtekind == RTE_SUBQUERY)
{
/* FOR UPDATE of subquery is propagated to subquery's rels */
transformForUpdate(rte->subquery, makeList1(NULL));
@@ -2707,7 +2707,7 @@ transformForUpdate(Query *qry, List *forUpdate)
++i;
if (strcmp(rte->eref->aliasname, relname) == 0)
{
if (rte->subquery)
if (rte->rtekind == RTE_SUBQUERY)
{
/* propagate to subquery */
transformForUpdate(rte->subquery, makeList1(NULL));

View File

@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.313 2002/05/06 19:47:30 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.314 2002/05/12 20:10:04 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -52,6 +52,7 @@
#include "access/htup.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "nodes/params.h"
@@ -250,7 +251,7 @@ static void doNegateFloat(Value *v);
%type <defelt> def_elem
%type <node> def_arg, columnElem, where_clause, insert_column_item,
a_expr, b_expr, c_expr, AexprConst,
in_expr, having_clause
in_expr, having_clause, func_table
%type <list> row_descriptor, row_list, in_expr_nodes
%type <node> row_expr
%type <node> case_expr, case_arg, when_clause, case_default
@@ -4074,6 +4075,19 @@ table_ref: relation_expr
$1->alias = $2;
$$ = (Node *) $1;
}
| func_table
{
RangeFunction *n = makeNode(RangeFunction);
n->funccallnode = $1;
$$ = (Node *) n;
}
| func_table alias_clause
{
RangeFunction *n = makeNode(RangeFunction);
n->funccallnode = $1;
n->alias = $2;
$$ = (Node *) n;
}
| select_with_parens
{
/*
@@ -4109,6 +4123,7 @@ table_ref: relation_expr
}
;
/*
* It may seem silly to separate joined_table from table_ref, but there is
* method in SQL92's madness: if you don't do it this way you get reduce-
@@ -4280,6 +4295,28 @@ relation_expr: qualified_name
}
;
func_table: func_name '(' ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
n->args = NIL;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
$$ = (Node *)n;
}
| func_name '(' expr_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
n->args = $3;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
$$ = (Node *)n;
}
;
where_clause: WHERE a_expr { $$ = $2; }
| /*EMPTY*/ { $$ = NULL; /* no qualifiers */ }
;
@@ -5845,26 +5882,33 @@ qualified_name_list: qualified_name
{ $$ = lappend($1, $3); }
;
qualified_name: ColId
qualified_name: relation_name
{
$$ = makeNode(RangeVar);
$$->catalogname = NULL;
$$->schemaname = NULL;
$$->relname = $1;
}
| ColId '.' ColId
| dotted_name
{
$$ = makeNode(RangeVar);
$$->catalogname = NULL;
$$->schemaname = $1;
$$->relname = $3;
}
| ColId '.' ColId '.' ColId
{
$$ = makeNode(RangeVar);
$$->catalogname = $1;
$$->schemaname = $3;
$$->relname = $5;
switch (length($1))
{
case 2:
$$->catalogname = NULL;
$$->schemaname = strVal(lfirst($1));
$$->relname = strVal(lsecond($1));
break;
case 3:
$$->catalogname = strVal(lfirst($1));
$$->schemaname = strVal(lsecond($1));
$$->relname = strVal(lfirst(lnext(lnext($1))));
break;
default:
elog(ERROR, "Improper qualified name (too many dotted names): %s",
NameListToString($1));
break;
}
}
;

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.90 2002/04/28 19:54:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.91 2002/05/12 20:10:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -17,6 +17,7 @@
#include "access/heapam.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/analyze.h"
@@ -49,6 +50,8 @@ static Node *transformJoinOnClause(ParseState *pstate, JoinExpr *j,
static RangeTblRef *transformTableEntry(ParseState *pstate, RangeVar *r);
static RangeTblRef *transformRangeSubselect(ParseState *pstate,
RangeSubselect *r);
static RangeTblRef *transformRangeFunction(ParseState *pstate,
RangeFunction *r);
static Node *transformFromClauseItem(ParseState *pstate, Node *n,
List **containedRels);
static Node *buildMergedJoinVar(JoinType jointype,
@@ -82,9 +85,9 @@ transformFromClause(ParseState *pstate, List *frmList)
/*
* The grammar will have produced a list of RangeVars,
* RangeSubselects, and/or JoinExprs. Transform each one (possibly
* adding entries to the rtable), check for duplicate refnames, and
* then add it to the joinlist and namespace.
* RangeSubselects, RangeFunctions, and/or JoinExprs. Transform each one
* (possibly adding entries to the rtable), check for duplicate refnames,
* and then add it to the joinlist and namespace.
*/
foreach(fl, frmList)
{
@@ -453,6 +456,71 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
}
/*
* transformRangeFunction --- transform a function call appearing in FROM
*/
static RangeTblRef *
transformRangeFunction(ParseState *pstate, RangeFunction *r)
{
Node *funcexpr;
char *funcname;
RangeTblEntry *rte;
RangeTblRef *rtr;
/*
* Transform the raw FuncCall node
*/
funcexpr = transformExpr(pstate, r->funccallnode);
Assert(IsA(r->funccallnode, FuncCall));
funcname = strVal(llast(((FuncCall *) r->funccallnode)->funcname));
/*
* Disallow aggregate functions and subselects in the expression.
* (Aggregates clearly make no sense; perhaps later we could support
* subselects, though.)
*/
if (contain_agg_clause(funcexpr))
elog(ERROR, "cannot use aggregate function in FROM function expression");
if (contain_subplans(funcexpr))
elog(ERROR, "cannot use subselect in FROM function expression");
/*
* Remove any Iter nodes added by parse_func.c. We oughta get rid of
* Iter completely ...
*/
while (funcexpr && IsA(funcexpr, Iter))
funcexpr = ((Iter *) funcexpr)->iterexpr;
/*
* Insist we now have a bare function call (explain.c is the only place
* that depends on this, I think). If this fails, it's probably because
* transformExpr interpreted the function notation as a type coercion.
*/
if (!funcexpr ||
!IsA(funcexpr, Expr) ||
((Expr *) funcexpr)->opType != FUNC_EXPR)
elog(ERROR, "Coercion function not allowed in FROM clause");
/*
* OK, build an RTE for the function.
*/
rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr,
r->alias, true);
/*
* We create a RangeTblRef, but we do not add it to the joinlist or
* namespace; our caller must do that if appropriate.
*/
rtr = makeNode(RangeTblRef);
/* assume new rte is at end */
rtr->rtindex = length(pstate->p_rtable);
Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
return rtr;
}
/*
* transformFromClauseItem -
* Transform a FROM-clause item, adding any required entries to the
@@ -486,6 +554,15 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
*containedRels = makeListi1(rtr->rtindex);
return (Node *) rtr;
}
else if (IsA(n, RangeFunction))
{
/* function is like a plain relation */
RangeTblRef *rtr;
rtr = transformRangeFunction(pstate, (RangeFunction *) n);
*containedRels = makeListi1(rtr->rtindex);
return (Node *) rtr;
}
else if (IsA(n, JoinExpr))
{
/* A newfangled join expression */

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.127 2002/05/03 20:15:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.128 2002/05/12 20:10:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -181,27 +181,32 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
* sizeof(Pointer) to signal that the runtime representation
* will be a pointer not an Oid.
*/
if (rte->rtekind != RTE_RELATION)
switch (rte->rtekind)
{
/*
* RTE is a join or subselect; must fail for lack of a
* named tuple type
*/
if (is_column)
elog(ERROR, "No such attribute %s.%s",
refname, strVal(lfirst(funcname)));
else
{
elog(ERROR, "Cannot pass result of sub-select or join %s to a function",
refname);
}
case RTE_RELATION:
toid = get_rel_type_id(rte->relid);
if (!OidIsValid(toid))
elog(ERROR, "Cannot find type OID for relation %u",
rte->relid);
break;
case RTE_FUNCTION:
toid = exprType(rte->funcexpr);
break;
default:
/*
* RTE is a join or subselect; must fail for lack of a
* named tuple type
*/
if (is_column)
elog(ERROR, "No such attribute %s.%s",
refname, strVal(lfirst(funcname)));
else
elog(ERROR, "Cannot pass result of sub-select or join %s to a function",
refname);
toid = InvalidOid; /* keep compiler quiet */
break;
}
toid = get_rel_type_id(rte->relid);
if (!OidIsValid(toid))
elog(ERROR, "Cannot find type OID for relation %u",
rte->relid);
/* replace RangeVar in the arg list */
lfirst(i) = makeVar(vnum,
InvalidAttrNumber,

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.68 2002/04/28 19:54:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.69 2002/05/12 20:10:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -672,6 +672,117 @@ addRangeTableEntryForSubquery(ParseState *pstate,
return rte;
}
/*
* Add an entry for a function to the pstate's range table (p_rtable).
*
* This is just like addRangeTableEntry() except that it makes a function RTE.
*/
RangeTblEntry *
addRangeTableEntryForFunction(ParseState *pstate,
char *funcname,
Node *funcexpr,
Alias *alias,
bool inFromCl)
{
RangeTblEntry *rte = makeNode(RangeTblEntry);
Oid funcrettype = exprType(funcexpr);
Oid funcrelid;
Alias *eref;
int numaliases;
int varattno;
rte->rtekind = RTE_FUNCTION;
rte->relid = InvalidOid;
rte->subquery = NULL;
rte->funcexpr = funcexpr;
rte->alias = alias;
eref = alias ? (Alias *) copyObject(alias) : makeAlias(funcname, NIL);
rte->eref = eref;
numaliases = length(eref->colnames);
/*
* Now determine if the function returns a simple or composite type,
* and check/add column aliases.
*/
funcrelid = typeidTypeRelid(funcrettype);
if (OidIsValid(funcrelid))
{
/*
* Composite data type, i.e. a table's row type
*
* Get the rel's relcache entry. This access ensures that we have an
* up-to-date relcache entry for the rel.
*/
Relation rel;
int maxattrs;
rel = heap_open(funcrelid, AccessShareLock);
/*
* Since the rel is open anyway, let's check that the number of column
* aliases is reasonable.
*/
maxattrs = RelationGetNumberOfAttributes(rel);
if (maxattrs < numaliases)
elog(ERROR, "Table \"%s\" has %d columns available but %d columns specified",
RelationGetRelationName(rel), maxattrs, numaliases);
/* fill in alias columns using actual column names */
for (varattno = numaliases; varattno < maxattrs; varattno++)
{
char *attrname;
attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
eref->colnames = lappend(eref->colnames, makeString(attrname));
}
/*
* Drop the rel refcount, but keep the access lock till end of
* transaction so that the table can't be deleted or have its schema
* modified underneath us.
*/
heap_close(rel, NoLock);
}
else
{
/*
* Must be a base data type, i.e. scalar.
* Just add one alias column named for the function.
*/
if (numaliases > 1)
elog(ERROR, "Too many column aliases specified for function %s",
funcname);
if (numaliases == 0)
eref->colnames = makeList1(makeString(funcname));
}
/*----------
* Flags:
* - this RTE should be expanded to include descendant tables,
* - this RTE is in the FROM clause,
* - this RTE should be checked for read/write access rights.
*----------
*/
rte->inh = false; /* never true for functions */
rte->inFromCl = inFromCl;
rte->checkForRead = true;
rte->checkForWrite = false;
rte->checkAsUser = InvalidOid;
/*
* Add completed RTE to pstate's range table list, but not to join
* list nor namespace --- caller must do that if appropriate.
*/
if (pstate != NULL)
pstate->p_rtable = lappend(pstate->p_rtable, rte);
return rte;
}
/*
* Add an entry for a join to the pstate's range table (p_rtable).
*
@@ -834,124 +945,201 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
/* Need the RT index of the entry for creating Vars */
rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
if (rte->rtekind == RTE_RELATION)
switch (rte->rtekind)
{
/* Ordinary relation RTE */
Relation rel;
int maxattrs;
int numaliases;
rel = heap_open(rte->relid, AccessShareLock);
maxattrs = RelationGetNumberOfAttributes(rel);
numaliases = length(rte->eref->colnames);
for (varattno = 0; varattno < maxattrs; varattno++)
{
Form_pg_attribute attr = rel->rd_att->attrs[varattno];
if (colnames)
case RTE_RELATION:
{
char *label;
/* Ordinary relation RTE */
Relation rel;
int maxattrs;
int numaliases;
if (varattno < numaliases)
label = strVal(nth(varattno, rte->eref->colnames));
rel = heap_open(rte->relid, AccessShareLock);
maxattrs = RelationGetNumberOfAttributes(rel);
numaliases = length(rte->eref->colnames);
for (varattno = 0; varattno < maxattrs; varattno++)
{
Form_pg_attribute attr = rel->rd_att->attrs[varattno];
if (colnames)
{
char *label;
if (varattno < numaliases)
label = strVal(nth(varattno, rte->eref->colnames));
else
label = NameStr(attr->attname);
*colnames = lappend(*colnames, makeString(pstrdup(label)));
}
if (colvars)
{
Var *varnode;
varnode = makeVar(rtindex, attr->attnum,
attr->atttypid, attr->atttypmod,
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
}
heap_close(rel, AccessShareLock);
}
break;
case RTE_SUBQUERY:
{
/* Subquery RTE */
List *aliasp = rte->eref->colnames;
List *tlistitem;
varattno = 0;
foreach(tlistitem, rte->subquery->targetList)
{
TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
if (te->resdom->resjunk)
continue;
varattno++;
Assert(varattno == te->resdom->resno);
if (colnames)
{
/* Assume there is one alias per target item */
char *label = strVal(lfirst(aliasp));
*colnames = lappend(*colnames, makeString(pstrdup(label)));
aliasp = lnext(aliasp);
}
if (colvars)
{
Var *varnode;
varnode = makeVar(rtindex, varattno,
te->resdom->restype,
te->resdom->restypmod,
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
}
}
break;
case RTE_FUNCTION:
{
/* Function RTE */
Oid funcrettype = exprType(rte->funcexpr);
Oid funcrelid = typeidTypeRelid(funcrettype);
if (OidIsValid(funcrelid))
{
/*
* Composite data type, i.e. a table's row type
* Same as ordinary relation RTE
*/
Relation rel;
int maxattrs;
int numaliases;
rel = heap_open(funcrelid, AccessShareLock);
maxattrs = RelationGetNumberOfAttributes(rel);
numaliases = length(rte->eref->colnames);
for (varattno = 0; varattno < maxattrs; varattno++)
{
Form_pg_attribute attr = rel->rd_att->attrs[varattno];
if (colnames)
{
char *label;
if (varattno < numaliases)
label = strVal(nth(varattno, rte->eref->colnames));
else
label = NameStr(attr->attname);
*colnames = lappend(*colnames, makeString(pstrdup(label)));
}
if (colvars)
{
Var *varnode;
varnode = makeVar(rtindex, attr->attnum,
attr->atttypid, attr->atttypmod,
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
}
heap_close(rel, AccessShareLock);
}
else
label = NameStr(attr->attname);
*colnames = lappend(*colnames, makeString(pstrdup(label)));
}
{
/*
* Must be a base data type, i.e. scalar
*/
if (colnames)
*colnames = lappend(*colnames,
lfirst(rte->eref->colnames));
if (colvars)
if (colvars)
{
Var *varnode;
varnode = makeVar(rtindex, 1,
funcrettype, -1,
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
}
}
break;
case RTE_JOIN:
{
Var *varnode;
/* Join RTE */
List *aliasp = rte->eref->colnames;
List *aliasvars = rte->joinaliasvars;
varnode = makeVar(rtindex, attr->attnum,
attr->atttypid, attr->atttypmod,
sublevels_up);
varattno = 0;
while (aliasp)
{
Assert(aliasvars);
varattno++;
*colvars = lappend(*colvars, varnode);
if (colnames)
{
char *label = strVal(lfirst(aliasp));
*colnames = lappend(*colnames, makeString(pstrdup(label)));
}
if (colvars)
{
Node *aliasvar = (Node *) lfirst(aliasvars);
Var *varnode;
varnode = makeVar(rtindex, varattno,
exprType(aliasvar),
exprTypmod(aliasvar),
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
aliasp = lnext(aliasp);
aliasvars = lnext(aliasvars);
}
Assert(aliasvars == NIL);
}
}
heap_close(rel, AccessShareLock);
break;
default:
elog(ERROR, "expandRTE: unsupported RTE kind %d",
(int) rte->rtekind);
}
else if (rte->rtekind == RTE_SUBQUERY)
{
/* Subquery RTE */
List *aliasp = rte->eref->colnames;
List *tlistitem;
varattno = 0;
foreach(tlistitem, rte->subquery->targetList)
{
TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
if (te->resdom->resjunk)
continue;
varattno++;
Assert(varattno == te->resdom->resno);
if (colnames)
{
/* Assume there is one alias per target item */
char *label = strVal(lfirst(aliasp));
*colnames = lappend(*colnames, makeString(pstrdup(label)));
aliasp = lnext(aliasp);
}
if (colvars)
{
Var *varnode;
varnode = makeVar(rtindex, varattno,
te->resdom->restype,
te->resdom->restypmod,
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
}
}
else if (rte->rtekind == RTE_JOIN)
{
/* Join RTE */
List *aliasp = rte->eref->colnames;
List *aliasvars = rte->joinaliasvars;
varattno = 0;
while (aliasp)
{
Assert(aliasvars);
varattno++;
if (colnames)
{
char *label = strVal(lfirst(aliasp));
*colnames = lappend(*colnames, makeString(pstrdup(label)));
}
if (colvars)
{
Node *aliasvar = (Node *) lfirst(aliasvars);
Var *varnode;
varnode = makeVar(rtindex, varattno,
exprType(aliasvar),
exprTypmod(aliasvar),
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
aliasp = lnext(aliasp);
aliasvars = lnext(aliasvars);
}
Assert(aliasvars == NIL);
}
else
elog(ERROR, "expandRTE: unsupported RTE kind %d",
(int) rte->rtekind);
}
/*
@@ -1044,57 +1232,101 @@ void
get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
Oid *vartype, int32 *vartypmod)
{
if (rte->rtekind == RTE_RELATION)
switch (rte->rtekind)
{
/* Plain relation RTE --- get the attribute's type info */
HeapTuple tp;
Form_pg_attribute att_tup;
case RTE_RELATION:
{
/* Plain relation RTE --- get the attribute's type info */
HeapTuple tp;
Form_pg_attribute att_tup;
tp = SearchSysCache(ATTNUM,
ObjectIdGetDatum(rte->relid),
Int16GetDatum(attnum),
0, 0);
/* this shouldn't happen... */
if (!HeapTupleIsValid(tp))
elog(ERROR, "Relation %s does not have attribute %d",
get_rel_name(rte->relid), attnum);
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
*vartype = att_tup->atttypid;
*vartypmod = att_tup->atttypmod;
ReleaseSysCache(tp);
tp = SearchSysCache(ATTNUM,
ObjectIdGetDatum(rte->relid),
Int16GetDatum(attnum),
0, 0);
/* this shouldn't happen... */
if (!HeapTupleIsValid(tp))
elog(ERROR, "Relation %s does not have attribute %d",
get_rel_name(rte->relid), attnum);
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
*vartype = att_tup->atttypid;
*vartypmod = att_tup->atttypmod;
ReleaseSysCache(tp);
}
break;
case RTE_SUBQUERY:
{
/* Subselect RTE --- get type info from subselect's tlist */
List *tlistitem;
foreach(tlistitem, rte->subquery->targetList)
{
TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
if (te->resdom->resjunk || te->resdom->resno != attnum)
continue;
*vartype = te->resdom->restype;
*vartypmod = te->resdom->restypmod;
return;
}
/* falling off end of list shouldn't happen... */
elog(ERROR, "Subquery %s does not have attribute %d",
rte->eref->aliasname, attnum);
}
break;
case RTE_FUNCTION:
{
/* Function RTE */
Oid funcrettype = exprType(rte->funcexpr);
Oid funcrelid = typeidTypeRelid(funcrettype);
if (OidIsValid(funcrelid))
{
/*
* Composite data type, i.e. a table's row type
* Same as ordinary relation RTE
*/
HeapTuple tp;
Form_pg_attribute att_tup;
tp = SearchSysCache(ATTNUM,
ObjectIdGetDatum(funcrelid),
Int16GetDatum(attnum),
0, 0);
/* this shouldn't happen... */
if (!HeapTupleIsValid(tp))
elog(ERROR, "Relation %s does not have attribute %d",
get_rel_name(funcrelid), attnum);
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
*vartype = att_tup->atttypid;
*vartypmod = att_tup->atttypmod;
ReleaseSysCache(tp);
}
else
{
/*
* Must be a base data type, i.e. scalar
*/
*vartype = funcrettype;
*vartypmod = -1;
}
}
break;
case RTE_JOIN:
{
/* Join RTE --- get type info from join RTE's alias variable */
Node *aliasvar;
Assert(attnum > 0 && attnum <= length(rte->joinaliasvars));
aliasvar = (Node *) nth(attnum-1, rte->joinaliasvars);
*vartype = exprType(aliasvar);
*vartypmod = exprTypmod(aliasvar);
}
break;
default:
elog(ERROR, "get_rte_attribute_type: unsupported RTE kind %d",
(int) rte->rtekind);
}
else if (rte->rtekind == RTE_SUBQUERY)
{
/* Subselect RTE --- get type info from subselect's tlist */
List *tlistitem;
foreach(tlistitem, rte->subquery->targetList)
{
TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
if (te->resdom->resjunk || te->resdom->resno != attnum)
continue;
*vartype = te->resdom->restype;
*vartypmod = te->resdom->restypmod;
return;
}
/* falling off end of list shouldn't happen... */
elog(ERROR, "Subquery %s does not have attribute %d",
rte->eref->aliasname, attnum);
}
else if (rte->rtekind == RTE_JOIN)
{
/* Join RTE --- get type info from join RTE's alias variable */
Node *aliasvar;
Assert(attnum > 0 && attnum <= length(rte->joinaliasvars));
aliasvar = (Node *) nth(attnum-1, rte->joinaliasvars);
*vartype = exprType(aliasvar);
*vartypmod = exprTypmod(aliasvar);
}
else
elog(ERROR, "get_rte_attribute_type: unsupported RTE kind %d",
(int) rte->rtekind);
}
/*

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.40 2002/04/20 21:56:14 petere Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.41 2002/05/12 20:10:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -56,7 +56,8 @@ LookupTypeName(const TypeName *typename)
switch (length(typename->names))
{
case 1:
elog(ERROR, "Improper %%TYPE reference (too few dotted names)");
elog(ERROR, "Improper %%TYPE reference (too few dotted names): %s",
NameListToString(typename->names));
break;
case 2:
rel->relname = strVal(lfirst(typename->names));
@@ -74,7 +75,8 @@ LookupTypeName(const TypeName *typename)
field = strVal(lfirst(lnext(lnext(lnext(typename->names)))));
break;
default:
elog(ERROR, "Improper %%TYPE reference (too many dotted names)");
elog(ERROR, "Improper %%TYPE reference (too many dotted names): %s",
NameListToString(typename->names));
break;
}
@@ -121,7 +123,8 @@ LookupTypeName(const TypeName *typename)
elog(ERROR, "Cross-database references are not implemented");
break;
default:
elog(ERROR, "Improper type name (too many dotted names)");
elog(ERROR, "Improper type name (too many dotted names): %s",
NameListToString(typename->names));
break;
}