summaryrefslogtreecommitdiff
path: root/src/backend/parser
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2002-05-12 20:10:05 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2002-05-12 20:10:05 +0000
commitf9e4f611a18f64fd9106a72ec9af9e2220075780 (patch)
treebfbc1d3d9fb5a008d8fe3405dce3366659c7e7cc /src/backend/parser
parent71009354c848964e657e540e24dac6b4c9a81570 (diff)
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 ...
Diffstat (limited to 'src/backend/parser')
-rw-r--r--src/backend/parser/analyze.c6
-rw-r--r--src/backend/parser/gram.y72
-rw-r--r--src/backend/parser/parse_clause.c85
-rw-r--r--src/backend/parser/parse_func.c43
-rw-r--r--src/backend/parser/parse_relation.c510
-rw-r--r--src/backend/parser/parse_type.c11
6 files changed, 544 insertions, 183 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index a3acf294534..72a0c8be513 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -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));
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 258a11c1183..6b7469f2bd7 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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
- {
- $$ = makeNode(RangeVar);
- $$->catalogname = NULL;
- $$->schemaname = $1;
- $$->relname = $3;
- }
- | ColId '.' ColId '.' ColId
+ | dotted_name
{
$$ = 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;
+ }
}
;
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 6df4a4fd7dc..19aa688ff94 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -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)
{
@@ -454,6 +457,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
* range table list being built in the ParseState, and return 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 */
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index f74f5be2f7b..339577f39ca 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -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,
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index b822a2378ba..857adf05691 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -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 $
*
*-------------------------------------------------------------------------
*/
@@ -673,6 +673,117 @@ addRangeTableEntryForSubquery(ParseState *pstate,
}
/*
+ * 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).
*
* This is much like addRangeTableEntry() except that it makes a join RTE.
@@ -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;
+ case RTE_RELATION:
+ {
+ /* Ordinary relation RTE */
+ Relation rel;
+ int maxattrs;
+ int numaliases;
- rel = heap_open(rte->relid, AccessShareLock);
- maxattrs = RelationGetNumberOfAttributes(rel);
- numaliases = length(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];
+ for (varattno = 0; varattno < maxattrs; varattno++)
+ {
+ Form_pg_attribute attr = rel->rd_att->attrs[varattno];
- if (colnames)
- {
- char *label;
+ 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 (varattno < numaliases)
+ label = strVal(nth(varattno, rte->eref->colnames));
+ else
+ label = NameStr(attr->attname);
+ *colnames = lappend(*colnames, makeString(pstrdup(label)));
+ }
- if (colvars)
- {
- Var *varnode;
+ if (colvars)
+ {
+ Var *varnode;
- varnode = makeVar(rtindex, attr->attnum,
- attr->atttypid, attr->atttypmod,
- sublevels_up);
+ varnode = makeVar(rtindex, attr->attnum,
+ attr->atttypid, attr->atttypmod,
+ sublevels_up);
+
+ *colvars = lappend(*colvars, varnode);
+ }
+ }
- *colvars = lappend(*colvars, varnode);
+ heap_close(rel, AccessShareLock);
}
- }
+ break;
+ case RTE_SUBQUERY:
+ {
+ /* Subquery RTE */
+ List *aliasp = rte->eref->colnames;
+ List *tlistitem;
- heap_close(rel, AccessShareLock);
- }
- 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);
- varattno = 0;
- foreach(tlistitem, rte->subquery->targetList)
- {
- TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
+ if (te->resdom->resjunk)
+ continue;
+ varattno++;
+ Assert(varattno == te->resdom->resno);
- 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));
- if (colnames)
- {
- /* Assume there is one alias per target item */
- char *label = strVal(lfirst(aliasp));
+ *colnames = lappend(*colnames, makeString(pstrdup(label)));
+ aliasp = lnext(aliasp);
+ }
- *colnames = lappend(*colnames, makeString(pstrdup(label)));
- aliasp = lnext(aliasp);
- }
+ if (colvars)
+ {
+ Var *varnode;
- if (colvars)
- {
- Var *varnode;
+ varnode = makeVar(rtindex, varattno,
+ te->resdom->restype,
+ te->resdom->restypmod,
+ sublevels_up);
- 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);
- *colvars = lappend(*colvars, varnode);
+ 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
+ {
+ /*
+ * Must be a base data type, i.e. scalar
+ */
+ if (colnames)
+ *colnames = lappend(*colnames,
+ lfirst(rte->eref->colnames));
+
+ if (colvars)
+ {
+ Var *varnode;
+
+ varnode = makeVar(rtindex, 1,
+ funcrettype, -1,
+ sublevels_up);
+
+ *colvars = lappend(*colvars, varnode);
+ }
+ }
}
- }
- }
- else if (rte->rtekind == RTE_JOIN)
- {
- /* Join RTE */
- List *aliasp = rte->eref->colnames;
- List *aliasvars = rte->joinaliasvars;
+ break;
+ case RTE_JOIN:
+ {
+ /* Join RTE */
+ List *aliasp = rte->eref->colnames;
+ List *aliasvars = rte->joinaliasvars;
- varattno = 0;
- while (aliasp)
- {
- Assert(aliasvars);
- varattno++;
+ varattno = 0;
+ while (aliasp)
+ {
+ Assert(aliasvars);
+ varattno++;
- if (colnames)
- {
- char *label = strVal(lfirst(aliasp));
+ if (colnames)
+ {
+ char *label = strVal(lfirst(aliasp));
- *colnames = lappend(*colnames, makeString(pstrdup(label)));
- }
+ *colnames = lappend(*colnames, makeString(pstrdup(label)));
+ }
- if (colvars)
- {
- Node *aliasvar = (Node *) lfirst(aliasvars);
- Var *varnode;
+ if (colvars)
+ {
+ Node *aliasvar = (Node *) lfirst(aliasvars);
+ Var *varnode;
- varnode = makeVar(rtindex, varattno,
- exprType(aliasvar),
- exprTypmod(aliasvar),
- sublevels_up);
+ varnode = makeVar(rtindex, varattno,
+ exprType(aliasvar),
+ exprTypmod(aliasvar),
+ sublevels_up);
- *colvars = lappend(*colvars, varnode);
- }
+ *colvars = lappend(*colvars, varnode);
+ }
- aliasp = lnext(aliasp);
- aliasvars = lnext(aliasvars);
- }
- Assert(aliasvars == NIL);
+ aliasp = lnext(aliasp);
+ aliasvars = lnext(aliasvars);
+ }
+ Assert(aliasvars == NIL);
+ }
+ break;
+ default:
+ elog(ERROR, "expandRTE: unsupported RTE kind %d",
+ (int) rte->rtekind);
}
- 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;
-
- 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);
- }
- else if (rte->rtekind == RTE_SUBQUERY)
- {
- /* Subselect RTE --- get type info from subselect's tlist */
- List *tlistitem;
+ 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);
+ }
+ 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);
+ 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;
+ 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);
+ 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
- elog(ERROR, "get_rte_attribute_type: unsupported RTE kind %d",
- (int) rte->rtekind);
}
/*
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 0901164389d..a8a466ac3ba 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -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;
}