summaryrefslogtreecommitdiff
path: root/src/backend/parser
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser')
-rw-r--r--src/backend/parser/analyze.c44
-rw-r--r--src/backend/parser/parse_clause.c162
-rw-r--r--src/backend/parser/parse_expr.c4
-rw-r--r--src/backend/parser/parse_func.c55
-rw-r--r--src/backend/parser/parse_node.c53
-rw-r--r--src/backend/parser/parse_relation.c357
-rw-r--r--src/backend/parser/parse_target.c66
7 files changed, 386 insertions, 355 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 5fb298e1c2..98f5030f78 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.219 2002/03/10 06:02:23 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.220 2002/03/12 00:51:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2045,10 +2045,12 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
Node *node;
List *lefttl,
*dtlist,
- *targetvars,
+ *colMods,
*targetnames,
- *sv_namespace;
- JoinExpr *jnode;
+ *sv_namespace,
+ *sv_rtable;
+ RangeTblEntry *jrte;
+ RangeTblRef *jrtr;
int tllen;
qry->commandType = CMD_SELECT;
@@ -2115,12 +2117,14 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
/*
* Generate dummy targetlist for outer query using column names of
* leftmost select and common datatypes of topmost set operation. Also
- * make lists of the dummy vars and their names for use in parsing
- * ORDER BY.
+ * make a list of the column names for use in parsing ORDER BY.
+ *
+ * XXX colMods is a hack to provide a dummy typmod list below. We
+ * should probably keep track of common typmod instead.
*/
qry->targetList = NIL;
- targetvars = NIL;
targetnames = NIL;
+ colMods = NIL;
lefttl = leftmostQuery->targetList;
foreach(dtlist, sostmt->colTypes)
{
@@ -2135,15 +2139,15 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
-1,
colName,
false);
- expr = (Node *) makeVar(leftmostRTI,
+ expr = (Node *) makeVar(1,
leftResdom->resno,
colType,
-1,
0);
qry->targetList = lappend(qry->targetList,
makeTargetEntry(resdom, expr));
- targetvars = lappend(targetvars, expr);
targetnames = lappend(targetnames, makeString(colName));
+ colMods = lappendi(colMods, -1);
lefttl = lnext(lefttl);
}
@@ -2190,7 +2194,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
/*
* As a first step towards supporting sort clauses that are
* expressions using the output columns, generate a namespace entry
- * that makes the output columns visible. A JoinExpr node is handy
+ * that makes the output columns visible. A Join RTE node is handy
* for this, since we can easily control the Vars generated upon
* matches.
*
@@ -2198,12 +2202,23 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
* "ORDER BY upper(foo)" will draw the right error message rather than
* "foo not found".
*/
- jnode = makeNode(JoinExpr);
- jnode->colnames = targetnames;
- jnode->colvars = targetvars;
+ jrte = addRangeTableEntryForJoin(NULL,
+ targetnames,
+ JOIN_INNER,
+ sostmt->colTypes,
+ colMods,
+ NIL,
+ NIL,
+ NULL,
+ true);
+ jrtr = makeNode(RangeTblRef);
+ jrtr->rtindex = 1;
+
+ sv_rtable = pstate->p_rtable;
+ pstate->p_rtable = makeList1(jrte);
sv_namespace = pstate->p_namespace;
- pstate->p_namespace = makeList1(jnode);
+ pstate->p_namespace = makeList1(jrtr);
/*
* For now, we don't support resjunk sort clauses on the output of a
@@ -2218,6 +2233,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
qry->targetList);
pstate->p_namespace = sv_namespace;
+ pstate->p_rtable = sv_rtable;
if (tllen != length(qry->targetList))
elog(ERROR, "ORDER BY on a UNION/INTERSECT/EXCEPT result must be on one of the result columns");
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 61904439e7..2f1eda4a9b 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.83 2001/10/25 05:49:37 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.84 2002/03/12 00:51:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -503,7 +503,13 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
*res_colnames,
*l_colvars,
*r_colvars,
- *res_colvars;
+ *coltypes,
+ *coltypmods,
+ *leftcolnos,
+ *rightcolnos;
+ Index leftrti,
+ rightrti;
+ RangeTblEntry *rte;
/*
* Recursively process the left and right subtrees
@@ -525,39 +531,32 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
/*
* Extract column name and var lists from both subtrees
+ *
+ * Note: expandRTE returns new lists, safe for me to modify
*/
- if (IsA(j->larg, JoinExpr))
- {
- /* Make a copy of the subtree's lists so we can modify! */
- l_colnames = copyObject(((JoinExpr *) j->larg)->colnames);
- l_colvars = copyObject(((JoinExpr *) j->larg)->colvars);
- }
+ if (IsA(j->larg, RangeTblRef))
+ leftrti = ((RangeTblRef *) j->larg)->rtindex;
+ else if (IsA(j->larg, JoinExpr))
+ leftrti = ((JoinExpr *) j->larg)->rtindex;
else
{
- RangeTblEntry *rte;
-
- Assert(IsA(j->larg, RangeTblRef));
- rte = rt_fetch(((RangeTblRef *) j->larg)->rtindex,
- pstate->p_rtable);
- expandRTE(pstate, rte, &l_colnames, &l_colvars);
- /* expandRTE returns new lists, so no need for copyObject */
- }
- if (IsA(j->rarg, JoinExpr))
- {
- /* Make a copy of the subtree's lists so we can modify! */
- r_colnames = copyObject(((JoinExpr *) j->rarg)->colnames);
- r_colvars = copyObject(((JoinExpr *) j->rarg)->colvars);
+ elog(ERROR, "transformFromClauseItem: unexpected subtree type");
+ leftrti = 0; /* keep compiler quiet */
}
+ rte = rt_fetch(leftrti, pstate->p_rtable);
+ expandRTE(pstate, rte, &l_colnames, &l_colvars);
+
+ if (IsA(j->rarg, RangeTblRef))
+ rightrti = ((RangeTblRef *) j->rarg)->rtindex;
+ else if (IsA(j->rarg, JoinExpr))
+ rightrti = ((JoinExpr *) j->rarg)->rtindex;
else
{
- RangeTblEntry *rte;
-
- Assert(IsA(j->rarg, RangeTblRef));
- rte = rt_fetch(((RangeTblRef *) j->rarg)->rtindex,
- pstate->p_rtable);
- expandRTE(pstate, rte, &r_colnames, &r_colvars);
- /* expandRTE returns new lists, so no need for copyObject */
+ elog(ERROR, "transformFromClauseItem: unexpected subtree type");
+ rightrti = 0; /* keep compiler quiet */
}
+ rte = rt_fetch(rightrti, pstate->p_rtable);
+ expandRTE(pstate, rte, &r_colnames, &r_colvars);
/*
* Natural join does not explicitly specify columns; must generate
@@ -604,7 +603,10 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
* Now transform the join qualifications, if any.
*/
res_colnames = NIL;
- res_colvars = NIL;
+ coltypes = NIL;
+ coltypmods = NIL;
+ leftcolnos = NIL;
+ rightcolnos = NIL;
if (j->using)
{
@@ -624,9 +626,10 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
{
char *u_colname = strVal(lfirst(ucol));
List *col;
- Node *l_colvar,
- *r_colvar,
- *colvar;
+ Var *l_colvar,
+ *r_colvar;
+ Oid outcoltype;
+ int32 outcoltypmod;
int ndx;
int l_index = -1;
int r_index = -1;
@@ -672,34 +675,28 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
res_colnames = lappend(res_colnames,
nth(l_index, l_colnames));
- switch (j->jointype)
+ /*
+ * Choose output type if input types are dissimilar.
+ */
+ outcoltype = l_colvar->vartype;
+ outcoltypmod = l_colvar->vartypmod;
+ if (outcoltype != r_colvar->vartype)
{
- case JOIN_INNER:
- case JOIN_LEFT:
- colvar = l_colvar;
- break;
- case JOIN_RIGHT:
- colvar = r_colvar;
- break;
- default:
- {
- /* Need COALESCE(l_colvar, r_colvar) */
- CaseExpr *c = makeNode(CaseExpr);
- CaseWhen *w = makeNode(CaseWhen);
- NullTest *n = makeNode(NullTest);
-
- n->arg = l_colvar;
- n->nulltesttype = IS_NOT_NULL;
- w->expr = (Node *) n;
- w->result = l_colvar;
- c->args = makeList1(w);
- c->defresult = r_colvar;
- colvar = transformExpr(pstate, (Node *) c,
- EXPR_COLUMN_FIRST);
- break;
- }
+ outcoltype =
+ select_common_type(makeListi2(l_colvar->vartype,
+ r_colvar->vartype),
+ "JOIN/USING");
+ outcoltypmod = -1; /* ie, unknown */
}
- res_colvars = lappend(res_colvars, colvar);
+ else if (outcoltypmod != r_colvar->vartypmod)
+ {
+ /* same type, but not same typmod */
+ outcoltypmod = -1; /* ie, unknown */
+ }
+ coltypes = lappendi(coltypes, outcoltype);
+ coltypmods = lappendi(coltypmods, outcoltypmod);
+ leftcolnos = lappendi(leftcolnos, l_index+1);
+ rightcolnos = lappendi(rightcolnos, r_index+1);
}
j->quals = transformJoinUsingClause(pstate,
@@ -724,30 +721,53 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
r_colnames, r_colvars,
&r_colnames, &r_colvars);
res_colnames = nconc(res_colnames, l_colnames);
- res_colvars = nconc(res_colvars, l_colvars);
+ while (l_colvars)
+ {
+ Var *l_var = (Var *) lfirst(l_colvars);
+
+ coltypes = lappendi(coltypes, l_var->vartype);
+ coltypmods = lappendi(coltypmods, l_var->vartypmod);
+ leftcolnos = lappendi(leftcolnos, l_var->varattno);
+ rightcolnos = lappendi(rightcolnos, 0);
+ l_colvars = lnext(l_colvars);
+ }
res_colnames = nconc(res_colnames, r_colnames);
- res_colvars = nconc(res_colvars, r_colvars);
+ while (r_colvars)
+ {
+ Var *r_var = (Var *) lfirst(r_colvars);
+
+ coltypes = lappendi(coltypes, r_var->vartype);
+ coltypmods = lappendi(coltypmods, r_var->vartypmod);
+ leftcolnos = lappendi(leftcolnos, 0);
+ rightcolnos = lappendi(rightcolnos, r_var->varattno);
+ r_colvars = lnext(r_colvars);
+ }
/*
- * Process alias (AS clause), if any.
+ * Check alias (AS clause), if any.
*/
if (j->alias)
{
- /*
- * If a column alias list is specified, substitute the alias
- * names into my output-column list
- */
if (j->alias->attrs != NIL)
{
- if (length(j->alias->attrs) != length(res_colnames))
- elog(ERROR, "Column alias list for \"%s\" has wrong number of entries (need %d)",
- j->alias->relname, length(res_colnames));
- res_colnames = j->alias->attrs;
+ if (length(j->alias->attrs) > length(res_colnames))
+ elog(ERROR, "Column alias list for \"%s\" has too many entries",
+ j->alias->relname);
}
}
- j->colnames = res_colnames;
- j->colvars = res_colvars;
+ /*
+ * Now build an RTE for the result of the join
+ */
+ rte = addRangeTableEntryForJoin(pstate, res_colnames,
+ j->jointype,
+ coltypes, coltypmods,
+ leftcolnos, rightcolnos,
+ j->alias, true);
+
+ /* assume new rte is at end */
+ j->rtindex = length(pstate->p_rtable);
+ Assert(rte == rt_fetch(j->rtindex, pstate->p_rtable));
return (Node *) j;
}
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index f740d632cc..9c32fac231 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.107 2002/03/07 16:35:36 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.108 2002/03/12 00:51:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -646,7 +646,7 @@ transformIdent(ParseState *pstate, Ident *ident, int precedence)
* appear
*/
if (ident->indirection == NIL &&
- refnameRangeOrJoinEntry(pstate, ident->name, &sublevels_up) != NULL)
+ refnameRangeTblEntry(pstate, ident->name, &sublevels_up) != NULL)
{
ident->isRel = TRUE;
result = (Node *) ident;
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 766a5daad5..ed39d6c103 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.116 2002/02/19 20:11:15 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.117 2002/03/12 00:51:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -316,7 +316,6 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
{
RangeTblEntry *rte;
int vnum;
- Node *rteorjoin;
int sublevels_up;
/*
@@ -324,49 +323,11 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
*/
refname = ((Ident *) arg)->name;
- rteorjoin = refnameRangeOrJoinEntry(pstate, refname,
- &sublevels_up);
+ rte = refnameRangeTblEntry(pstate, refname,
+ &sublevels_up);
- if (rteorjoin == NULL)
+ if (rte == NULL)
rte = addImplicitRTE(pstate, refname);
- else if (IsA(rteorjoin, RangeTblEntry))
- rte = (RangeTblEntry *) rteorjoin;
- else if (IsA(rteorjoin, JoinExpr))
- {
- /*
- * The relation name refers to a join. We can't support
- * functions on join tuples (since we don't have a named
- * type for the join tuples), so error out.
- */
- if (nargs == 1)
- {
- /*
- * We have f(x) or more likely x.f where x is a join
- * and f is not one of the attribute names of the join
- * (else we'd have recognized it above). Give an
- * appropriately vague error message. Would be nicer
- * to know which syntax was used...
- */
- elog(ERROR, "No such attribute or function %s.%s",
- refname, funcname);
- }
- else
- {
- /*
- * There are multiple arguments, so it must be a
- * function call.
- */
- elog(ERROR, "Cannot pass result of join %s to a function",
- refname);
- }
- rte = NULL; /* keep compiler quiet */
- }
- else
- {
- elog(ERROR, "ParseFuncOrColumn: unexpected node type %d",
- nodeTag(rteorjoin));
- rte = NULL; /* keep compiler quiet */
- }
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
@@ -379,11 +340,11 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
* sizeof(Pointer) to signal that the runtime representation
* will be a pointer not an Oid.
*/
- if (rte->relname == NULL)
+ if (rte->rtekind != RTE_RELATION)
{
/*
- * RTE is a subselect; must fail for lack of a specific
- * type
+ * RTE is a join or subselect; must fail for lack of a
+ * named tuple type
*/
if (nargs == 1)
{
@@ -397,7 +358,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
}
else
{
- elog(ERROR, "Cannot pass result of sub-select %s to a function",
+ elog(ERROR, "Cannot pass result of sub-select or join %s to a function",
refname);
}
}
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index a43dcb13af..be825c26f9 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -8,21 +8,22 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.58 2002/03/06 06:09:54 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.59 2002/03/12 00:51:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
+#include "postgres.h"
+
#include <ctype.h>
#include <errno.h>
#include <float.h>
-#include "postgres.h"
-
#include "access/heapam.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
#include "fmgr.h"
#include "nodes/makefuncs.h"
+#include "parser/parsetree.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parse_node.h"
@@ -165,51 +166,11 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno)
{
int vnum,
sublevels_up;
- Oid vartypeid = 0;
- int32 type_mod = 0;
+ Oid vartypeid;
+ int32 type_mod;
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
-
- if (rte->relid != InvalidOid)
- {
- /* Plain relation RTE --- get the attribute's type info */
- HeapTuple tp;
- Form_pg_attribute att_tup;
-
- tp = SearchSysCache(ATTNUM,
- ObjectIdGetDatum(rte->relid),
- Int16GetDatum(attrno),
- 0, 0);
- /* this shouldn't happen... */
- if (!HeapTupleIsValid(tp))
- elog(ERROR, "Relation %s does not have attribute %d",
- rte->relname, attrno);
- att_tup = (Form_pg_attribute) GETSTRUCT(tp);
- vartypeid = att_tup->atttypid;
- type_mod = att_tup->atttypmod;
- ReleaseSysCache(tp);
- }
- else
- {
- /* 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 != attrno)
- continue;
- vartypeid = te->resdom->restype;
- type_mod = te->resdom->restypmod;
- break;
- }
- /* falling off end of list shouldn't happen... */
- if (tlistitem == NIL)
- elog(ERROR, "Subquery %s does not have attribute %d",
- rte->eref->relname, attrno);
- }
-
+ get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod);
return makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up);
}
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 9440914a77..1609c89ce0 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.62 2002/03/06 06:09:55 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.63 2002/03/12 00:51:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -36,38 +36,41 @@ static Node *scanNameSpaceForRefname(ParseState *pstate, Node *nsnode,
char *refname);
static Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
char *colname);
-static Node *scanJoinForColumn(JoinExpr *join, char *colname,
- int sublevels_up);
static bool isForUpdate(ParseState *pstate, char *relname);
-static List *expandNamesVars(ParseState *pstate, List *names, List *vars);
static int specialAttNum(char *a);
static void warnAutoRange(ParseState *pstate, char *refname);
/*
- * refnameRangeOrJoinEntry
- * Given a refname, look to see if it matches any RTE or join table.
- * If so, return a pointer to the RangeTblEntry or JoinExpr.
+ * refnameRangeTblEntry
+ * Given a refname, look to see if it matches any RTE.
+ * If so, return a pointer to the RangeTblEntry.
* Optionally get its nesting depth (0 = current). If sublevels_up
* is NULL, only consider items at the current nesting level.
*/
-Node *
-refnameRangeOrJoinEntry(ParseState *pstate,
- char *refname,
- int *sublevels_up)
+RangeTblEntry *
+refnameRangeTblEntry(ParseState *pstate,
+ char *refname,
+ int *sublevels_up)
{
if (sublevels_up)
*sublevels_up = 0;
while (pstate != NULL)
{
- Node *rte;
+ Node *nsnode;
- rte = scanNameSpaceForRefname(pstate,
- (Node *) pstate->p_namespace,
- refname);
- if (rte)
- return rte;
+ nsnode = scanNameSpaceForRefname(pstate,
+ (Node *) pstate->p_namespace,
+ refname);
+ if (nsnode)
+ {
+ /* should get an RTE or JoinExpr */
+ if (IsA(nsnode, RangeTblEntry))
+ return (RangeTblEntry *) nsnode;
+ Assert(IsA(nsnode, JoinExpr));
+ return rt_fetch(((JoinExpr *) nsnode)->rtindex, pstate->p_rtable);
+ }
pstate = pstate->parentParseState;
if (sublevels_up)
@@ -247,6 +250,12 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
*
* Side effect: if we find a match, mark the RTE as requiring read access.
* See comments in setTargetTable().
+ *
+ * NOTE: if the RTE is for a join, marking it as requiring read access does
+ * nothing. It might seem that we need to propagate the mark to all the
+ * contained RTEs, but that is not necessary. This is so because a join
+ * expression can only appear in a FROM clause, and any table named in
+ * FROM will be marked checkForRead from the beginning.
*/
static Node *
scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
@@ -279,10 +288,9 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
return result;
/*
- * If the RTE represents a table (not a sub-select), consider system
- * column names.
+ * If the RTE represents a real table, consider system column names.
*/
- if (rte->relid != InvalidOid)
+ if (rte->rtekind == RTE_RELATION)
{
/* quick check to see if name could be a system column */
attnum = specialAttNum(colname);
@@ -304,44 +312,6 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
}
/*
- * scanJoinForColumn
- * Search the column names of a single join table for the given name.
- * If found, return an appropriate Var node or expression, else return NULL.
- * If the name proves ambiguous within this jointable, raise error.
- *
- * NOTE: unlike scanRTEForColumn, there's no need to worry about forcing
- * checkForRead true for the referenced tables. This is so because a join
- * expression can only appear in a FROM clause, and any table named in
- * FROM will be marked checkForRead from the beginning.
- */
-static Node *
-scanJoinForColumn(JoinExpr *join, char *colname, int sublevels_up)
-{
- Node *result = NULL;
- int attnum = 0;
- List *c;
-
- foreach(c, join->colnames)
- {
- attnum++;
- if (strcmp(strVal(lfirst(c)), colname) == 0)
- {
- if (result)
- elog(ERROR, "Column reference \"%s\" is ambiguous", colname);
- result = copyObject(nth(attnum - 1, join->colvars));
-
- /*
- * If referencing an uplevel join item, we must adjust
- * sublevels settings in the copied expression.
- */
- if (sublevels_up > 0)
- IncrementVarSublevelsUp(result, sublevels_up, 0);
- }
- }
- return result;
-}
-
-/*
* colnameToVar
* Search for an unqualified column name.
* If found, return the appropriate Var node (or expression).
@@ -382,9 +352,13 @@ colnameToVar(ParseState *pstate, char *colname)
}
else if (IsA(nsnode, JoinExpr))
{
- JoinExpr *j = (JoinExpr *) nsnode;
+ int varno = ((JoinExpr *) nsnode)->rtindex;
+ RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
- newresult = scanJoinForColumn(j, colname, levels_up);
+ /* joins are always inFromCl, so no need to check */
+
+ /* use orig_pstate here to get the right sublevels_up */
+ newresult = scanRTEForColumn(orig_pstate, rte, colname);
}
else
elog(ERROR, "colnameToVar: unexpected node type %d",
@@ -412,41 +386,26 @@ colnameToVar(ParseState *pstate, char *colname)
/*
* qualifiedNameToVar
* Search for a qualified column name (refname + column name).
- * If found, return the appropriate Var node (or expression).
+ * If found, return the appropriate Var node.
* If not found, return NULL. If the name proves ambiguous, raise error.
*/
Node *
qualifiedNameToVar(ParseState *pstate, char *refname, char *colname,
bool implicitRTEOK)
{
- Node *result;
- Node *rteorjoin;
+ RangeTblEntry *rte;
int sublevels_up;
- rteorjoin = refnameRangeOrJoinEntry(pstate, refname, &sublevels_up);
+ rte = refnameRangeTblEntry(pstate, refname, &sublevels_up);
- if (rteorjoin == NULL)
+ if (rte == NULL)
{
if (!implicitRTEOK)
return NULL;
- rteorjoin = (Node *) addImplicitRTE(pstate, refname);
- sublevels_up = 0;
+ rte = addImplicitRTE(pstate, refname);
}
- if (IsA(rteorjoin, RangeTblEntry))
- result = scanRTEForColumn(pstate, (RangeTblEntry *) rteorjoin,
- colname);
- else if (IsA(rteorjoin, JoinExpr))
- result = scanJoinForColumn((JoinExpr *) rteorjoin,
- colname, sublevels_up);
- else
- {
- elog(ERROR, "qualifiedNameToVar: unexpected node type %d",
- nodeTag(rteorjoin));
- result = NULL; /* keep compiler quiet */
- }
-
- return result;
+ return scanRTEForColumn(pstate, rte, colname);
}
/*
@@ -474,9 +433,9 @@ addRangeTableEntry(ParseState *pstate,
int numaliases;
int varattno;
+ rte->rtekind = RTE_RELATION;
rte->relname = relname;
rte->alias = alias;
- rte->subquery = NULL;
/*
* Get the rel's OID. This access also ensures that we have an
@@ -563,6 +522,7 @@ addRangeTableEntryForSubquery(ParseState *pstate,
int varattno;
List *tlistitem;
+ rte->rtekind = RTE_SUBQUERY;
rte->relname = NULL;
rte->relid = InvalidOid;
rte->subquery = subquery;
@@ -622,6 +582,76 @@ addRangeTableEntryForSubquery(ParseState *pstate,
}
/*
+ * 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.
+ */
+RangeTblEntry *
+addRangeTableEntryForJoin(ParseState *pstate,
+ List *colnames,
+ JoinType jointype,
+ List *coltypes,
+ List *coltypmods,
+ List *leftcols,
+ List *rightcols,
+ Attr *alias,
+ bool inFromCl)
+{
+ RangeTblEntry *rte = makeNode(RangeTblEntry);
+ Attr *eref;
+ int numaliases;
+
+ rte->rtekind = RTE_JOIN;
+ rte->relname = NULL;
+ rte->relid = InvalidOid;
+ rte->subquery = NULL;
+ rte->jointype = jointype;
+ rte->joincoltypes = coltypes;
+ rte->joincoltypmods = coltypmods;
+ rte->joinleftcols = leftcols;
+ rte->joinrightcols = rightcols;
+ rte->alias = alias;
+
+ eref = alias ? (Attr *) copyObject(alias) : makeAttr("unnamed_join", NULL);
+ numaliases = length(eref->attrs);
+
+ /* fill in any unspecified alias columns */
+ if (numaliases < length(colnames))
+ {
+ while (numaliases-- > 0)
+ colnames = lnext(colnames);
+ eref->attrs = nconc(eref->attrs, colnames);
+ }
+
+ rte->eref = eref;
+
+ /*----------
+ * 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.
+ *
+ * Joins are never checked for access rights.
+ *----------
+ */
+ rte->inh = false; /* never true for joins */
+ rte->inFromCl = inFromCl;
+ rte->checkForRead = false;
+ 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;
+}
+
+/*
* Has the specified relname been selected FOR UPDATE?
*/
static bool
@@ -720,15 +750,16 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
/* Need the RT index of the entry for creating Vars */
rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
- if (rte->relname)
+ if (rte->rtekind == RTE_RELATION)
{
/* Ordinary relation RTE */
Relation rel;
int maxattrs;
+ int numaliases;
rel = heap_openr(rte->relname, AccessShareLock);
-
maxattrs = RelationGetNumberOfAttributes(rel);
+ numaliases = length(rte->eref->attrs);
for (varattno = 0; varattno < maxattrs; varattno++)
{
@@ -743,7 +774,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
{
char *label;
- if (varattno < length(rte->eref->attrs))
+ if (varattno < numaliases)
label = strVal(nth(varattno, rte->eref->attrs));
else
label = NameStr(attr->attname);
@@ -764,7 +795,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
heap_close(rel, AccessShareLock);
}
- else
+ else if (rte->rtekind == RTE_SUBQUERY)
{
/* Subquery RTE */
List *aliasp = rte->eref->attrs;
@@ -802,56 +833,63 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
}
}
}
-}
+ else if (rte->rtekind == RTE_JOIN)
+ {
+ /* Join RTE */
+ List *aliasp = rte->eref->attrs;
+ List *coltypes = rte->joincoltypes;
+ List *coltypmods = rte->joincoltypmods;
-/*
- * expandRelAttrs -
- * makes a list of TargetEntry nodes for the attributes of the rel
- */
-List *
-expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
-{
- List *name_list,
- *var_list;
+ varattno = 0;
+ while (aliasp)
+ {
+ Assert(coltypes && coltypmods);
+ varattno++;
- expandRTE(pstate, rte, &name_list, &var_list);
+ if (colnames)
+ {
+ char *label = strVal(lfirst(aliasp));
- return expandNamesVars(pstate, name_list, var_list);
-}
+ *colnames = lappend(*colnames, makeString(pstrdup(label)));
+ }
-/*
- * expandJoinAttrs -
- * makes a list of TargetEntry nodes for the attributes of the join
- */
-List *
-expandJoinAttrs(ParseState *pstate, JoinExpr *join, int sublevels_up)
-{
- List *vars;
+ if (colvars)
+ {
+ Var *varnode;
- vars = copyObject(join->colvars);
+ varnode = makeVar(rtindex, varattno,
+ (Oid) lfirsti(coltypes),
+ (int32) lfirsti(coltypmods),
+ sublevels_up);
- /*
- * If referencing an uplevel join item, we must adjust sublevels
- * settings in the copied expression.
- */
- if (sublevels_up > 0)
- IncrementVarSublevelsUp((Node *) vars, sublevels_up, 0);
+ *colvars = lappend(*colvars, varnode);
+ }
- return expandNamesVars(pstate,
- copyObject(join->colnames),
- vars);
+ aliasp = lnext(aliasp);
+ coltypes = lnext(coltypes);
+ coltypmods = lnext(coltypmods);
+ }
+ Assert(coltypes == NIL && coltypmods == NIL);
+ }
+ else
+ elog(ERROR, "expandRTE: unsupported RTE kind %d",
+ (int) rte->rtekind);
}
/*
- * expandNamesVars -
- * Workhorse for "*" expansion: produce a list of targetentries
- * given lists of column names (as String nodes) and var references.
+ * expandRelAttrs -
+ * Workhorse for "*" expansion: produce a list of targetentries
+ * for the attributes of the rte
*/
-static List *
-expandNamesVars(ParseState *pstate, List *names, List *vars)
+List *
+expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
{
+ List *names,
+ *vars;
List *te_list = NIL;
+ expandRTE(pstate, rte, &names, &vars);
+
while (names)
{
char *label = strVal(lfirst(names));
@@ -875,22 +913,16 @@ expandNamesVars(ParseState *pstate, List *names, List *vars)
return te_list;
}
-/* ----------
+/*
* get_rte_attribute_name
* Get an attribute name from a RangeTblEntry
*
* This is unlike get_attname() because we use aliases if available.
- * In particular, it will work on an RTE for a subselect, whereas
+ * In particular, it will work on an RTE for a subselect or join, whereas
* get_attname() only works on real relations.
*
* "*" is returned if the given attnum is InvalidAttrNumber --- this case
* occurs when a Var represents a whole tuple of a relation.
- *
- * XXX Actually, this is completely bogus, because refnames of RTEs are
- * not guaranteed unique, and may not even have scope across the whole
- * query. Cleanest fix would be to add refname/attname to Var nodes and
- * just print those, rather than indulging in this hack.
- * ----------
*/
char *
get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
@@ -901,7 +933,8 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
return "*";
/*
- * If there is an alias, use it
+ * If there is an alias, use it. (This path should always be taken
+ * for non-relation RTEs.)
*/
if (attnum > 0 && attnum <= length(rte->eref->attrs))
return strVal(nth(attnum - 1, rte->eref->attrs));
@@ -909,9 +942,9 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
/*
* Can get here for a system attribute (which never has an alias), or
* if alias name list is too short (which probably can't happen
- * anymore). Neither of these cases is valid for a subselect RTE.
+ * anymore). Neither of these cases is valid for a non-relation RTE.
*/
- if (rte->relid == InvalidOid)
+ if (rte->rtekind != RTE_RELATION)
elog(ERROR, "Invalid attnum %d for rangetable entry %s",
attnum, rte->eref->relname);
@@ -926,6 +959,64 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
}
/*
+ * get_rte_attribute_type
+ * Get attribute type information from a RangeTblEntry
+ */
+void
+get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
+ Oid *vartype, int32 *vartypmod)
+{
+ if (rte->rtekind == 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",
+ rte->relname, 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;
+
+ 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->relname, attnum);
+ }
+ else if (rte->rtekind == RTE_JOIN)
+ {
+ /* Join RTE --- get type info directly from join RTE */
+ Assert(attnum > 0 && attnum <= length(rte->joincoltypes));
+ *vartype = (Oid) nthi(attnum-1, rte->joincoltypes);
+ *vartypmod = nthi(attnum-1, rte->joincoltypmods);
+ }
+ else
+ elog(ERROR, "get_rte_attribute_type: unsupported RTE kind %d",
+ (int) rte->rtekind);
+}
+
+/*
* given relation and att name, return id of variable
*
* This should only be used if the relation is already
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index bb398a7068..f5791298f3 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -8,12 +8,13 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.76 2001/11/05 17:46:26 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.77 2002/03/12 00:51:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+
#include "nodes/makefuncs.h"
#include "parser/parsetree.h"
#include "parser/parse_coerce.h"
@@ -118,30 +119,16 @@ transformTargetList(ParseState *pstate, List *targetlist)
* Target item is relation.*, expand that table (eg.
* SELECT emp.*, dname FROM emp, dept)
*/
- Node *rteorjoin;
+ RangeTblEntry *rte;
int sublevels_up;
- rteorjoin = refnameRangeOrJoinEntry(pstate, att->relname,
- &sublevels_up);
-
- if (rteorjoin == NULL)
- {
- rteorjoin = (Node *) addImplicitRTE(pstate, att->relname);
- sublevels_up = 0;
- }
+ rte = refnameRangeTblEntry(pstate, att->relname,
+ &sublevels_up);
+ if (rte == NULL)
+ rte = addImplicitRTE(pstate, att->relname);
- if (IsA(rteorjoin, RangeTblEntry))
- p_target = nconc(p_target,
- expandRelAttrs(pstate,
- (RangeTblEntry *) rteorjoin));
- else if (IsA(rteorjoin, JoinExpr))
- p_target = nconc(p_target,
- expandJoinAttrs(pstate,
- (JoinExpr *) rteorjoin,
- sublevels_up));
- else
- elog(ERROR, "transformTargetList: unexpected node type %d",
- nodeTag(rteorjoin));
+ p_target = nconc(p_target,
+ expandRelAttrs(pstate, rte));
}
else
{
@@ -405,34 +392,29 @@ ExpandAllTables(ParseState *pstate)
foreach(ns, pstate->p_namespace)
{
Node *n = (Node *) lfirst(ns);
+ RangeTblEntry *rte;
if (IsA(n, RangeTblRef))
- {
- RangeTblEntry *rte;
-
rte = rt_fetch(((RangeTblRef *) n)->rtindex,
pstate->p_rtable);
-
- /*
- * Ignore added-on relations that were not listed in the FROM
- * clause.
- */
- if (!rte->inFromCl)
- continue;
-
- target = nconc(target, expandRelAttrs(pstate, rte));
- }
else if (IsA(n, JoinExpr))
- {
- /* A newfangled join expression */
- JoinExpr *j = (JoinExpr *) n;
-
- /* Currently, a join expr could only have come from FROM. */
- target = nconc(target, expandJoinAttrs(pstate, j, 0));
- }
+ rte = rt_fetch(((JoinExpr *) n)->rtindex,
+ pstate->p_rtable);
else
+ {
elog(ERROR, "ExpandAllTables: unexpected node (internal error)"
"\n\t%s", nodeToString(n));
+ rte = NULL; /* keep compiler quiet */
+ }
+
+ /*
+ * Ignore added-on relations that were not listed in the FROM
+ * clause.
+ */
+ if (!rte->inFromCl)
+ continue;
+
+ target = nconc(target, expandRelAttrs(pstate, rte));
}
/* Check for SELECT *; */