diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 89c20ce5fa..2840e40633 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.120 2000/01/31 04:35:48 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.121 2000/02/15 03:36:34 thomas Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -53,6 +53,7 @@
 #include "optimizer/planmain.h"
 #include "optimizer/tlist.h"
 #include "optimizer/var.h"
+#include "nodes/makefuncs.h"
 #include "parser/parse_clause.h"
 #include "parser/parse_expr.h"
 #include "parser/parse_relation.h"
@@ -1719,7 +1720,8 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin,
 	 */
 	rte = makeNode(RangeTblEntry);
 	rte->relname = RelationGetRelationName(rel);
-	rte->refname = RelationGetRelationName(rel);
+	rte->ref = makeNode(Attr);
+	rte->ref->relname = RelationGetRelationName(rel);
 	rte->relid = RelationGetRelid(rel);
 	rte->inh = false;
 	rte->inFromCl = true;
@@ -1798,7 +1800,8 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
 	 */
 	rte = makeNode(RangeTblEntry);
 	rte->relname = RelationGetRelationName(rel);
-	rte->refname = RelationGetRelationName(rel);
+	rte->ref = makeNode(Attr);
+	rte->ref->relname = RelationGetRelationName(rel);
 	rte->relid = RelationGetRelid(rel);
 	rte->inh = false;
 	rte->inFromCl = true;
@@ -1919,8 +1922,8 @@ AddRelationRawConstraints(Relation rel,
 	 * its sole rangetable entry.  We need a ParseState for transformExpr.
 	 */
 	pstate = make_parsestate(NULL);
-	makeRangeTable(pstate, NULL, NULL);
-	addRangeTableEntry(pstate, relname, relname, false, true, true);
+	makeRangeTable(pstate, NULL);
+	addRangeTableEntry(pstate, relname, makeAttr(relname, NULL), false, true, true);
 
 	/*
 	 * Process column default expressions.
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 8406416b76..2b152b2fe5 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
- *	  $Id: explain.c,v 1.52 2000/01/26 05:56:13 momjian Exp $
+ *	  $Id: explain.c,v 1.53 2000/02/15 03:36:39 thomas Exp $
  *
  */
 
@@ -230,12 +230,12 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
 				RangeTblEntry *rte = nth(((Scan *) plan)->scanrelid - 1, es->rtable);
 
 				appendStringInfo(str, " on ");
-				if (strcmp(rte->refname, rte->relname) != 0)
+				if (strcmp(rte->ref->relname, rte->relname) != 0)
 				{
 					appendStringInfo(str, "%s ",
 									 stringStringInfo(rte->relname));
 				}
-				appendStringInfo(str, stringStringInfo(rte->refname));
+				appendStringInfo(str, stringStringInfo(rte->ref->relname));
 			}
 			break;
 		case T_TidScan:
@@ -244,12 +244,12 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
 				RangeTblEntry *rte = nth(((TidScan *) plan)->scan.scanrelid - 1, es->rtable);
 
 				appendStringInfo(str, " on ");
-				if (strcmp(rte->refname, rte->relname) != 0)
+				if (strcmp(rte->ref->relname, rte->relname) != 0)
 				{
 					appendStringInfo(str, "%s ",
 									 stringStringInfo(rte->relname));
 				}
-				appendStringInfo(str, stringStringInfo(rte->refname));
+				appendStringInfo(str, stringStringInfo(rte->ref->relname));
 			}
 			break;
 		default:
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 8742b895df..f4bb18762b 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$Id: view.c,v 1.41 2000/01/26 05:56:14 momjian Exp $
+ *	$Id: view.c,v 1.42 2000/02/15 03:36:39 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,6 +17,7 @@
 #include "catalog/heap.h"
 #include "commands/creatinh.h"
 #include "commands/view.h"
+#include "nodes/makefuncs.h"
 #include "parser/parse_relation.h"
 #include "parser/parse_type.h"
 #include "rewrite/rewriteDefine.h"
@@ -225,9 +226,11 @@ UpdateRangeTableOfViewParse(char *viewName, Query *viewParse)
 	 * create the 2 new range table entries and form the new range
 	 * table... CURRENT first, then NEW....
 	 */
-	rt_entry1 = addRangeTableEntry(NULL, (char *) viewName, "*CURRENT*",
+	rt_entry1 = addRangeTableEntry(NULL, (char *) viewName,
+								   makeAttr("*CURRENT*", NULL),
 								   FALSE, FALSE, FALSE);
-	rt_entry2 = addRangeTableEntry(NULL, (char *) viewName, "*NEW*",
+	rt_entry2 = addRangeTableEntry(NULL, (char *) viewName,
+								   makeAttr("*NEW*", NULL),
 								   FALSE, FALSE, FALSE);
 	new_rt = lcons(rt_entry2, old_rt);
 	new_rt = lcons(rt_entry1, new_rt);
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 899885dc31..e3d1862f4e 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -27,7 +27,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.108 2000/02/03 00:02:58 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.109 2000/02/15 03:36:49 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1508,7 +1508,8 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate)
 	slot->ttc_buffer = InvalidBuffer;
 	slot->ttc_whichplan = -1;
 	rte->relname = RelationGetRelationName(rel);
-	rte->refname = rte->relname;
+	rte->ref = makeNode(Attr);
+	rte->ref->relname = rte->relname;
 	rte->relid = RelationGetRelid(rel);
 	/* inh, inFromCl, inJoinSet, skipAcl won't be used, leave them zero */
 	rtlist = lcons(rte, NIL);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c5c9cb0782..adf0c7f198 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.104 2000/02/07 04:40:56 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.105 2000/02/15 03:37:08 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -688,6 +688,18 @@ _copyVar(Var *from)
 	return newnode;
 }
 
+static Attr *
+_copyAttr(Attr *from)
+{
+	Attr	   *newnode = makeNode(Attr);
+
+	if (from->relname)
+		newnode->relname = pstrdup(from->relname);
+	Node_Copy(from, newnode, attrs);
+
+	return newnode;
+}
+
 /* ----------------
  *		_copyOper
  * ----------------
@@ -1327,8 +1339,8 @@ _copyRangeTblEntry(RangeTblEntry *from)
 
 	if (from->relname)
 		newnode->relname = pstrdup(from->relname);
-	if (from->refname)
-		newnode->refname = pstrdup(from->refname);
+	if (from->ref)
+		Node_Copy(from, newnode, ref);
 	newnode->relid = from->relid;
 	newnode->inh = from->inh;
 	newnode->inFromCl = from->inFromCl;
@@ -1571,6 +1583,9 @@ copyObject(void *from)
 		case T_Var:
 			retval = _copyVar(from);
 			break;
+		case T_Attr:
+			retval = _copyAttr(from);
+			break;
 		case T_Oper:
 			retval = _copyOper(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 5888f49515..3ddc8d6c98 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.59 2000/02/07 04:40:57 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.60 2000/02/15 03:37:08 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -95,6 +95,17 @@ _equalExpr(Expr *a, Expr *b)
 	return true;
 }
 
+static bool
+_equalAttr(Attr *a, Attr *b)
+{
+	if (!strcmp(a->relname, b->relname))
+		return false;
+	if (length(a->attrs) != length(b->attrs))
+		return false;
+
+	return equal(a->attrs, b->attrs);
+}
+
 static bool
 _equalVar(Var *a, Var *b)
 {
@@ -633,14 +644,14 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
 		if (a->relname != b->relname)
 			return false;
 	}
-	if (a->refname && b->refname)
+	if (a->ref && b->ref)
 	{
-		if (strcmp(a->refname, b->refname) != 0)
+		if (! equal(a->ref, b->ref))
 			return false;
 	}
 	else
 	{
-		if (a->refname != b->refname)
+		if (a->ref != b->ref)
 			return false;
 	}
 	if (a->relid != b->relid)
@@ -845,6 +856,9 @@ equal(void *a, void *b)
 		case T_EState:
 			retval = _equalEState(a, b);
 			break;
+		case T_Attr:
+			retval = _equalAttr(a, b);
+			break;
 		case T_Integer:
 		case T_String:
 		case T_Float:
diff --git a/src/backend/nodes/freefuncs.c b/src/backend/nodes/freefuncs.c
index ab308fe310..690da02de8 100644
--- a/src/backend/nodes/freefuncs.c
+++ b/src/backend/nodes/freefuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.34 2000/02/07 04:40:57 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.35 2000/02/15 03:37:08 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1013,8 +1013,19 @@ _freeRangeTblEntry(RangeTblEntry *node)
 {
 	if (node->relname)
 		pfree(node->relname);
-	if (node->refname)
-		pfree(node->refname);
+	if (node->ref)
+		freeObject(node->ref);
+
+	pfree(node);
+}
+
+static void
+_freeAttr(Attr *node)
+{
+	if (node->relname)
+		pfree(node->relname);
+	if (node->attrs)
+		freeObject(node->attrs);
 
 	pfree(node);
 }
@@ -1308,6 +1319,9 @@ freeObject(void *node)
 		case T_TypeCast:
 			_freeTypeCast(node);
 			break;
+		case T_Attr:
+			_freeAttr(node);
+			break;
 
 			/*
 			 * VALUE NODES
@@ -1332,3 +1346,10 @@ freeObject(void *node)
 			break;
 	}
 }
+
+
+
+
+
+
+
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 221a83d713..e42930ef87 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.19 2000/01/26 05:56:31 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.20 2000/02/15 03:37:09 thomas Exp $
  *
  * NOTES
  *	  Creator functions in POSTGRES 4.2 are generated automatically. Most of
@@ -141,3 +141,26 @@ makeConst(Oid consttype,
 	cnst->constiscast = constiscast;
 	return cnst;
 }
+
+/*
+ * makeAttr -
+ *	  creates an Attr node
+ */
+Attr *
+makeAttr(char *relname, char *attname)
+{
+	Attr	   *a = makeNode(Attr);
+
+	a->relname = pstrdup(relname);
+	a->paramNo = NULL;
+	if (attname != NULL)
+		a->attrs = lcons(makeString(pstrdup(attname)), NIL);
+	a->indirection = NULL;
+
+	return a;
+}
+
+
+
+
+
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 8923510e1b..e4c35cc277 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.106 2000/02/07 04:40:57 tgl Exp $
+ *	$Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.107 2000/02/15 03:37:09 thomas Exp $
  *
  * NOTES
  *	  Every (plan) node in POSTGRES has an associated "out" routine which
@@ -954,8 +954,8 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
 {
 	appendStringInfo(str, " RTE :relname ");
 	_outToken(str, node->relname);
-	appendStringInfo(str, " :refname ");
-	_outToken(str, node->refname);
+	appendStringInfo(str, " :ref ");
+	_outNode(str, node->ref);
 	appendStringInfo(str,
 					 " :relid %u :inh %s :inFromCl %s :inJoinSet %s :skipAcl %s",
 					 node->relid,
@@ -1273,18 +1273,10 @@ _outIdent(StringInfo str, Ident *node)
 static void
 _outAttr(StringInfo str, Attr *node)
 {
-	List	   *l;
-
-	appendStringInfo(str, " ATTR ");
+	appendStringInfo(str, " ATTR :relname ");
 	_outToken(str, node->relname);
-	appendStringInfo(str, " (");
-	foreach(l, node->attrs)
-	{
-		_outNode(str, lfirst(l));
-		if (lnext(l))
-			appendStringInfo(str, " ");
-	}
-	appendStringInfo(str, ")");
+	appendStringInfo(str, " :attrs ");
+	_outNode(str, node->attrs);
 }
 
 static void
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index ffba95949a..a84b829950 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.35 2000/01/26 05:56:32 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.36 2000/02/15 03:37:09 thomas Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -133,7 +133,7 @@ print_rt(List *rtable)
 		RangeTblEntry *rte = lfirst(l);
 
 		printf("%d\t%s(%s)\t%u\t%d\t%s\n",
-			   i, rte->relname, rte->refname, rte->relid,
+			   i, rte->relname, rte->ref->relname, rte->relid,
 			   rte->inFromCl,
 			   (rte->inh ? "inh" : ""));
 		i++;
@@ -175,8 +175,9 @@ print_expr(Node *expr, List *rtable)
 				{
 					rt = rt_fetch(var->varno, rtable);
 					relname = rt->relname;
-					if (rt->refname)
-						relname = rt->refname;	/* table renamed */
+					if (rt->ref)
+						if (rt->ref->relname)
+						relname = rt->relname;	/* table renamed */
 					attname = get_attname(rt->relid, var->varattno);
 				}
 				break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 38db7def0b..7d56b603b8 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.82 2000/02/07 04:40:57 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.83 2000/02/15 03:37:09 thomas Exp $
  *
  * NOTES
  *	  Most of the read functions for plan nodes are tested. (In fact, they
@@ -1322,6 +1322,51 @@ _readTargetEntry()
 	return local_node;
 }
 
+static List *
+_readList()
+{
+	List	   *local_node = NULL;
+	char	   *token;
+	int			length;
+
+	token = lsptok(NULL, &length);		/* eat "(" */
+	token = lsptok(NULL, &length);		/* get "{" */
+	while (strncmp(token, "{", length) == 0)
+	{
+		nconc(local_node, nodeRead(true));
+
+		token = lsptok(NULL, &length);		/* eat ")" */
+		if (strncmp(token, "}", length) != 0)
+			elog(ERROR, "badly formatted attribute list"
+				 " in planstring \"%.10s\"...\n", token);
+		token = lsptok(NULL, &length);		/* "{" or ")" */
+	}
+
+	return local_node;
+}
+
+static Attr *
+_readAttr()
+{
+	Attr	   *local_node;
+	char	   *token;
+	int			length;
+
+	local_node = makeNode(Attr);
+
+	token = lsptok(NULL, &length);		/* eat :relname */
+	token = lsptok(NULL, &length);		/* get relname */
+	if (length == 0)
+		local_node->relname = pstrdup("");
+	else
+		local_node->relname = debackslash(token, length);
+
+	token = lsptok(NULL, &length);		/* eat :attrs */
+	local_node->attrs = _readList();
+
+	return local_node;
+}
+
 /* ----------------
  *		_readRangeTblEntry
  * ----------------
@@ -1342,12 +1387,8 @@ _readRangeTblEntry()
 	else
 		local_node->relname = debackslash(token, length);
 
-	token = lsptok(NULL, &length);		/* eat :refname */
-	token = lsptok(NULL, &length);		/* get :refname */
-	if (length == 0)
-		local_node->refname = NULL;
-	else
-		local_node->refname = debackslash(token, length);
+	token = lsptok(NULL, &length);		/* eat :ref */
+	local_node->ref = nodeRead(true);
 
 	token = lsptok(NULL, &length);		/* eat :relid */
 	token = lsptok(NULL, &length);		/* get :relid */
@@ -1795,6 +1836,8 @@ parsePlanString(void)
 		return_value = _readArray();
 	else if (length == 3 && strncmp(token, "VAR", length) == 0)
 		return_value = _readVar();
+	else if (length == 4 && strncmp(token, "ATTR", length) == 0)
+		return_value = _readAttr();
 	else if (length == 5 && strncmp(token, "CONST", length) == 0)
 		return_value = _readConst();
 	else if (length == 4 && strncmp(token, "FUNC", length) == 0)
@@ -1843,6 +1886,14 @@ parsePlanString(void)
 		return_value = _readCaseWhen();
 	else if (length == 7 && strncmp(token, "ROWMARK", length) == 0)
 		return_value = _readRowMark();
+#if 0
+	else if (length == 1 && strncmp(token, "{", length) == 0)
+	{
+		/* raw list (of strings?) found in Attr structure - thomas 2000-02-09 */
+		return_value = nodeRead(true);
+		token = lsptok(NULL, &length);	/* eat trailing brace */
+	}
+#endif
 	else
 		elog(ERROR, "badly formatted planstring \"%.10s\"...\n", token);
 
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 95005f166f..9a86cb2348 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.43 2000/02/03 06:12:19 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.44 2000/02/15 03:37:26 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -430,9 +430,9 @@ new_rangetable_entry(Oid new_relid, RangeTblEntry *old_entry)
 	RangeTblEntry *new_entry = copyObject(old_entry);
 
 	/* ??? someone tell me what the following is doing! - ay 11/94 */
-	if (!strcmp(new_entry->refname, "*CURRENT*") ||
-		!strcmp(new_entry->refname, "*NEW*"))
-		new_entry->refname = get_rel_name(new_relid);
+	if (!strcmp(new_entry->ref->relname, "*CURRENT*") ||
+		!strcmp(new_entry->ref->relname, "*NEW*"))
+		new_entry->ref->relname = get_rel_name(new_relid);
 	else
 		new_entry->relname = get_rel_name(new_relid);
 
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 53b4eec2cb..664a0dfaaa 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.58 2000/01/26 05:56:40 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.59 2000/02/15 03:37:36 thomas Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -556,7 +556,7 @@ check_subplans_for_ungrouped_vars_walker(Node *node,
 					elog(ERROR, "cache lookup of attribute %d in relation %u failed",
 						 var->varattno, rte->relid);
 				elog(ERROR, "Sub-SELECT uses un-GROUPed attribute %s.%s from outer query",
-					 rte->refname, attname);
+					 rte->ref->relname, attname);
 			}
 		}
 	}
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index a9dda032cd..5f7369af00 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$Id: analyze.c,v 1.136 2000/02/05 00:20:38 wieck Exp $
+ *	$Id: analyze.c,v 1.137 2000/02/15 03:37:47 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -239,13 +239,13 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
 	qry->commandType = CMD_DELETE;
 
 	/* set up a range table */
-	makeRangeTable(pstate, NULL, NULL);
+	makeRangeTable(pstate, NULL);
 	setTargetTable(pstate, stmt->relname);
 
 	qry->distinctClause = NIL;
 
 	/* fix where clause */
-	qry->qual = transformWhereClause(pstate, stmt->whereClause, NULL);
+	qry->qual = transformWhereClause(pstate, stmt->whereClause);
 
 	qry->rtable = pstate->p_rtable;
 	qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
@@ -266,7 +266,6 @@ static Query *
 transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 {
 	Query	   *qry = makeNode(Query);
-	Node	   *fromQual;
 	List	   *icolumns;
 	List	   *attrnos;
 	List	   *attnos;
@@ -289,16 +288,16 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 	 */
 
 	/* set up a range table --- note INSERT target is not in it yet */
-	makeRangeTable(pstate, stmt->fromClause, &fromQual);
+	makeRangeTable(pstate, stmt->fromClause);
 
 	qry->targetList = transformTargetList(pstate, stmt->targetList);
 
-	qry->qual = transformWhereClause(pstate, stmt->whereClause, fromQual);
+	qry->qual = transformWhereClause(pstate, stmt->whereClause);
 
 	/* Initial processing of HAVING clause is just like WHERE clause.
 	 * Additional work will be done in optimizer/plan/planner.c.
 	 */
-	qry->havingQual = transformWhereClause(pstate, stmt->havingClause, NULL);
+	qry->havingQual = transformWhereClause(pstate, stmt->havingClause);
 
 	qry->groupClause = transformGroupClause(pstate,
 											stmt->groupClause,
@@ -974,6 +973,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
 			 *
 			 */
 			if (fkconstraint->fk_attrs != NIL && fkconstraint->pk_attrs == NIL)
+			{
 				if (strcmp(fkconstraint->pktable_name, stmt->relname) != 0)
 					transformFkeyGetPrimaryKey(fkconstraint);
 				else if (pkey != NULL) 
@@ -997,6 +997,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
 					elog(ERROR, "PRIMARY KEY for referenced table \"%s\" not found",
 						fkconstraint->pktable_name);
 				}
+			}
 
 			/*
 			 * Build a CREATE CONSTRAINT TRIGGER statement for the CHECK
@@ -1207,7 +1208,8 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
 	qry->commandType = CMD_UTILITY;
 
 	/* take care of the where clause */
-	stmt->whereClause = transformWhereClause(pstate, stmt->whereClause, NULL);
+	stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
+
 	qry->hasSubLinks = pstate->p_hasSubLinks;
 
 	stmt->rangetable = pstate->p_rtable;
@@ -1231,7 +1233,8 @@ transformExtendStmt(ParseState *pstate, ExtendStmt *stmt)
 	qry->commandType = CMD_UTILITY;
 
 	/* take care of the where clause */
-	stmt->whereClause = transformWhereClause(pstate, stmt->whereClause, NULL);
+	stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
+
 	qry->hasSubLinks = pstate->p_hasSubLinks;
 
 	stmt->rangetable = pstate->p_rtable;
@@ -1268,9 +1271,11 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
 
 		nothing_qry->commandType = CMD_NOTHING;
 
-		addRangeTableEntry(pstate, stmt->object->relname, "*CURRENT*",
+		addRangeTableEntry(pstate, stmt->object->relname,
+						   makeAttr("*CURRENT*", NULL),
 						   FALSE, FALSE, FALSE);
-		addRangeTableEntry(pstate, stmt->object->relname, "*NEW*",
+		addRangeTableEntry(pstate, stmt->object->relname,
+						   makeAttr("*NEW*", NULL),
 						   FALSE, FALSE, FALSE);
 
 		nothing_qry->rtable = pstate->p_rtable;
@@ -1290,9 +1295,11 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
 		 * NOTE: 'CURRENT' must always have a varno equal to 1 and 'NEW'
 		 * equal to 2.
 		 */
-		addRangeTableEntry(pstate, stmt->object->relname, "*CURRENT*",
+		addRangeTableEntry(pstate, stmt->object->relname,
+						   makeAttr("*CURRENT*", NULL),
 						   FALSE, FALSE, FALSE);
-		addRangeTableEntry(pstate, stmt->object->relname, "*NEW*",
+		addRangeTableEntry(pstate, stmt->object->relname,
+						   makeAttr("*NEW*", NULL),
 						   FALSE, FALSE, FALSE);
 
 		pstate->p_last_resno = 1;
@@ -1306,7 +1313,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
 	}
 
 	/* take care of the where clause */
-	stmt->whereClause = transformWhereClause(pstate, stmt->whereClause, NULL);
+	stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
+
 	qry->hasSubLinks = pstate->p_hasSubLinks;
 
 	qry->utilityStmt = (Node *) stmt;
@@ -1323,12 +1331,11 @@ static Query *
 transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 {
 	Query	   *qry = makeNode(Query);
-	Node	   *fromQual;
 
 	qry->commandType = CMD_SELECT;
 
 	/* set up a range table */
-	makeRangeTable(pstate, stmt->fromClause, &fromQual);
+	makeRangeTable(pstate, stmt->fromClause);
 
 	qry->into = stmt->into;
 	qry->isTemp = stmt->istemp;
@@ -1336,12 +1343,12 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 
 	qry->targetList = transformTargetList(pstate, stmt->targetList);
 
-	qry->qual = transformWhereClause(pstate, stmt->whereClause, fromQual);
+	qry->qual = transformWhereClause(pstate, stmt->whereClause);
 
 	/* Initial processing of HAVING clause is just like WHERE clause.
 	 * Additional work will be done in optimizer/plan/planner.c.
 	 */
-	qry->havingQual = transformWhereClause(pstate, stmt->havingClause, NULL);
+	qry->havingQual = transformWhereClause(pstate, stmt->havingClause);
 
 	qry->groupClause = transformGroupClause(pstate,
 											stmt->groupClause,
@@ -1401,12 +1408,12 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 	 * the FROM clause is non-standard SQL syntax. We used to be able to
 	 * do this with REPLACE in POSTQUEL so we keep the feature.
 	 */
-	makeRangeTable(pstate, stmt->fromClause, NULL);
+	makeRangeTable(pstate, stmt->fromClause);
 	setTargetTable(pstate, stmt->relname);
 
 	qry->targetList = transformTargetList(pstate, stmt->targetList);
 
-	qry->qual = transformWhereClause(pstate, stmt->whereClause, NULL);
+	qry->qual = transformWhereClause(pstate, stmt->whereClause);
 
 	qry->hasSubLinks = pstate->p_hasSubLinks;
 
@@ -1866,7 +1873,7 @@ transformForUpdate(Query *qry, List *forUpdate)
 		i = 1;
 		foreach(l2, qry->rtable)
 		{
-			if (strcmp(((RangeTblEntry *) lfirst(l2))->refname, relname) == 0)
+			if (strcmp(((RangeTblEntry *) lfirst(l2))->ref->relname, relname) == 0)
 			{
 				List	   *l3;
 
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 3c23a29246..afaa55e54f 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.33 2000/01/26 05:56:42 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.34 2000/02/15 03:37:47 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -111,7 +111,7 @@ check_ungrouped_columns_walker(Node *node,
 			elog(ERROR, "cache lookup of attribute %d in relation %u failed",
 				 var->varattno, rte->relid);
 		elog(ERROR, "Attribute %s.%s must be GROUPed or used in an aggregate function",
-			 rte->refname, attname);
+			 rte->ref->relname, attname);
 	}
 	/* Otherwise, recurse. */
 	return expression_tree_walker(node, check_ungrouped_columns_walker,
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index b22691fa3c..d0a3eb1edb 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,16 +8,17 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.51 2000/01/27 18:11:35 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.52 2000/02/15 03:37:47 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
-
 #include "access/heapam.h"
+#include "miscadmin.h"
 #include "optimizer/tlist.h"
 #include "parse.h"
+#include "nodes/makefuncs.h"
 #include "parser/parse_clause.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_expr.h"
@@ -25,7 +26,6 @@
 #include "parser/parse_relation.h"
 #include "parser/parse_target.h"
 
-
 #define ORDER_CLAUSE 0
 #define GROUP_CLAUSE 1
 #define DISTINCT_ON_CLAUSE 2
@@ -34,28 +34,26 @@ static char *clauseText[] = {"ORDER BY", "GROUP BY", "DISTINCT ON"};
 
 static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node,
 										List *tlist, int clause);
-static void parseFromClause(ParseState *pstate, List *frmList, Node **qual);
-static char	*transformTableEntry(ParseState *pstate, RangeVar *r);
+static void parseFromClause(ParseState *pstate, List *frmList);
+RangeTblEntry *transformTableEntry(ParseState *pstate, RangeVar *r);
 static List *addTargetToSortList(TargetEntry *tle, List *sortlist,
 								 List *targetlist, char *opname);
 static bool exprIsInSortList(Node *expr, List *sortList, List *targetList);
 
-#ifdef ENABLE_OUTER_JOINS
-static Node *transformUsingClause(ParseState *pstate, List *onList,
-								  char *lname, char *rname);
+#ifndef DISABLE_OUTER_JOINS
+static Node *transformUsingClause(ParseState *pstate, List *using, List *left, List *right);
 #endif
 
 
-
 /*
  * makeRangeTable -
  *	  Build the initial range table from the FROM clause.
  */
 void
-makeRangeTable(ParseState *pstate, List *frmList, Node **qual)
+makeRangeTable(ParseState *pstate, List *frmList)
 {
 	/* Currently, nothing to do except this: */
-	parseFromClause(pstate, frmList, qual);
+	parseFromClause(pstate, frmList);
 }
 
 /*
@@ -80,7 +78,8 @@ setTargetTable(ParseState *pstate, char *relname)
 
 	if ((refnameRangeTablePosn(pstate, relname, &sublevels_up) == 0)
 		|| (sublevels_up != 0))
-		rte = addRangeTableEntry(pstate, relname, relname,
+		rte = addRangeTableEntry(pstate, relname,
+								 makeAttr(relname, NULL),
 								 FALSE, FALSE, FALSE);
 	else
 		rte = refnameRangeTableEntry(pstate, relname);
@@ -94,40 +93,52 @@ setTargetTable(ParseState *pstate, char *relname)
 	/* will close relation later, see analyze.c */
 }
 
-/*
- * transformWhereClause -
- *	  transforms the qualification and make sure it is of type Boolean
- *
- * Now accept an additional argument, which is a qualification derived
- * from the JOIN/ON or JOIN/USING syntax.
- * - thomas 1998-12-16
- */
+
 Node *
-transformWhereClause(ParseState *pstate, Node *a_expr, Node *o_expr)
+mergeInnerJoinQuals(ParseState *pstate, Node *clause);
+
+Node *
+mergeInnerJoinQuals(ParseState *pstate, Node *clause)
 {
-	A_Expr	   *expr;
-	Node	   *qual;
+	A_Expr	   *expr = (A_Expr *) pstate->p_join_quals;
 
-	if ((a_expr == NULL) && (o_expr == NULL))
-		return NULL;			/* no qualifiers */
+	if (expr == NULL)
+		return clause;
 
-	if ((a_expr != NULL) && (o_expr != NULL))
+	if (clause != NULL)
 	{
 		A_Expr	   *a = makeNode(A_Expr);
 
 		a->oper = AND;
 		a->opname = NULL;
-		a->lexpr = o_expr;
-		a->rexpr = a_expr;
+		a->lexpr = (Node *) expr;
+		a->rexpr = clause;
 		expr = a;
 	}
-	else if (o_expr != NULL)
-		expr = (A_Expr *) o_expr;
-	else
-		expr = (A_Expr *) a_expr;
+
+	/* Make sure that we don't do this twice... */
+	pstate->p_join_quals = NULL;
+
+	return (Node *) expr;
+} /* mergeInnerJoinQuals() */
+
+/*
+ * transformWhereClause -
+ *	  transforms the qualification and make sure it is of type Boolean
+ */
+Node *
+transformWhereClause(ParseState *pstate, Node *clause)
+{
+	Node	   *qual;
+
+	if (pstate->p_join_quals != NULL)
+		clause = mergeInnerJoinQuals(pstate, clause);
+
+	if (clause == NULL)
+		return NULL;
 
 	pstate->p_in_where_clause = true;
-	qual = transformExpr(pstate, (Node *) expr, EXPR_COLUMN_FIRST);
+	qual = transformExpr(pstate, clause, EXPR_COLUMN_FIRST);
 	pstate->p_in_where_clause = false;
 
 	if (exprType(qual) != BOOLOID)
@@ -138,98 +149,259 @@ transformWhereClause(ParseState *pstate, Node *a_expr, Node *o_expr)
 	return qual;
 }
 
-#ifdef ENABLE_OUTER_JOINS
-static Attr *
-makeAttr(char *relname, char *attname)
+#ifndef DISABLE_JOIN_SYNTAX
+char *
+AttrString(Attr *attr);
+
+char *
+AttrString(Attr *attr)
 {
-	Attr	   *a = makeNode(Attr);
+	Value *val;
 
-	a->relname = relname;
-	a->paramNo = NULL;
-	a->attrs = lcons(makeString(attname), NIL);
-	a->indirection = NULL;
+	Assert(length(attr->attrs) == 1);
 
-	return a;
+	val = lfirst(attr->attrs);
+
+	Assert(IsA(val, String));
+
+	return strVal(val);
+}
+
+List *
+ListTableAsAttrs(ParseState *pstate, char *table);
+List *
+ListTableAsAttrs(ParseState *pstate, char *table)
+{
+	List *rlist = NULL;
+	List *col;
+
+	Attr *attr = expandTable(pstate, table, TRUE);
+	foreach(col, attr->attrs)
+	{
+		Attr *a;
+		a = makeAttr(table, strVal((Value *) col));
+		rlist = lappend(rlist, a);
+	}
+
+	return rlist;
+}
+
+List *
+makeUniqueAttrList(List *candidates, List *idents);
+List *
+makeUniqueAttrList(List *attrs, List *filter)
+{
+	List *result = NULL;
+	List *candidate;
+
+	foreach(candidate, attrs)
+	{
+		List *fmember;
+		bool match = FALSE;
+//		char *field;
+		Attr *cattr = lfirst(candidate);
+
+		Assert(IsA(cattr, Attr));
+		Assert(length(cattr->attrs) == 1);
+
+//		field = strVal(lfirst(ccol));
+//		bool match = FALSE;
+
+		foreach(fmember, filter)
+		{
+			Attr *fattr = lfirst(fmember);
+			Assert(IsA(fattr, Attr));
+			Assert(length(fattr->attrs) == 1);
+
+			if (strcmp(strVal(lfirst(cattr->attrs)), strVal(lfirst(fattr->attrs))) == 0)
+			{
+				match = TRUE;
+				break;
+			}
+		}
+
+		if (!match)
+			result = lappend(result, cattr);
+	}
+
+	return result;
+}
+
+List *
+makeAttrList(Attr *attr);
+
+List *
+makeAttrList(Attr *attr)
+{
+	List *result = NULL;
+
+	char *name = attr->relname;
+	List *col;
+
+	foreach (col, attr->attrs)
+	{
+		Attr *newattr = makeAttr(name, strVal((Value *) lfirst(col)));
+
+		result = lappend(result, newattr);
+	}
+
+	return result;
+}
+
+/* ExpandAttrs()
+ * Take an existing attribute node and return a list of attribute nodes
+ * with one attribute name per node.
+ */
+List *
+ExpandAttrs(Attr *attr);
+List *
+ExpandAttrs(Attr *attr)
+{
+	List *col;
+	char *relname = attr->relname;
+	List *rlist = NULL;
+
+	Assert(attr != NULL);
+
+	if ((attr->attrs == NULL) || (length(attr->attrs) <= 1))
+		return lcons(attr, NIL);
+
+	foreach(col, attr->attrs)
+	{
+		Attr *attr = lfirst(col);
+
+		rlist = lappend(rlist, makeAttr(relname, AttrString(attr)));
+	}
+
+	return rlist;
 }
-#endif
 
-#ifdef ENABLE_OUTER_JOINS
 /* transformUsingClause()
  * Take an ON or USING clause from a join expression and expand if necessary.
  */
 static Node *
-transformUsingClause(ParseState *pstate, List *onList, char *lname, char *rname)
+transformUsingClause(ParseState *pstate, List *usingList, List *leftList, List *rightList)
 {
 	A_Expr	   *expr = NULL;
-	List	   *on;
-	Node	   *qual;
+	List	   *using;
 
-	foreach(on, onList)
+	foreach(using, usingList)
 	{
-		qual = lfirst(on);
+		List *col;
+		A_Expr *e;
 
-		/*
-		 * Ident node means it is just a column name from a real USING
-		 * clause...
+		Attr *uattr = lfirst(using);
+		Attr *lattr = NULL, *rattr = NULL;
+
+		/* find the first instances of this column in the shape list
+		 * and the last table in the shape list...
 		 */
-		if (IsA(qual, Ident))
+		foreach (col, leftList)
 		{
-			Ident	   *i = (Ident *) qual;
-			Attr	   *lattr = makeAttr(lname, i->name);
-			Attr	   *rattr = makeAttr(rname, i->name);
-			A_Expr	   *e = makeNode(A_Expr);
+			Attr *attr = lfirst(col);
 
-			e->oper = OP;
-			e->opname = "=";
-			e->lexpr = (Node *) lattr;
-			e->rexpr = (Node *) rattr;
-
-			if (expr != NULL)
+			if (strcmp(AttrString(attr), AttrString(uattr)) == 0)
 			{
-				A_Expr	   *a = makeNode(A_Expr);
-
-				a->oper = AND;
-				a->opname = NULL;
-				a->lexpr = (Node *) expr;
-				a->rexpr = (Node *) e;
-				expr = a;
+				lattr = attr;
+				break;
+			}
+		}
+		foreach (col, rightList)
+		{
+			Attr *attr = lfirst(col);
+
+			if (strcmp(AttrString(attr), AttrString(uattr)) == 0)
+			{
+				rattr = attr;
+				break;
 			}
-			else
-				expr = e;
 		}
 
-		/* otherwise, we have an expression from an ON clause... */
+		Assert((lattr != NULL) && (rattr != NULL));
+
+		e = makeNode(A_Expr);
+		e->oper = OP;
+		e->opname = "=";
+		e->lexpr = (Node *) lattr;
+		e->rexpr = (Node *) rattr;
+
+		if (expr != NULL)
+		{
+			A_Expr	   *a = makeNode(A_Expr);
+
+			a->oper = AND;
+			a->opname = NULL;
+			a->lexpr = (Node *) expr;
+			a->rexpr = (Node *) e;
+			expr = a;
+		}
 		else
-		{
-			if (expr != NULL)
-			{
-				A_Expr	   *a = makeNode(A_Expr);
-
-				a->oper = AND;
-				a->opname = NULL;
-				a->lexpr = (Node *) expr;
-				a->rexpr = (Node *) qual;
-				expr = a;
-			}
-			else
-				expr = (A_Expr *) qual;
-		}
+			expr = e;
 	}
-	return ((Node *) transformExpr(pstate, (Node *) expr, EXPR_COLUMN_FIRST));
-}
 
+	return ((Node *) transformExpr(pstate, (Node *) expr, EXPR_COLUMN_FIRST));
+} /* transformUsiongClause() */
 #endif
 
-static char *
+
+RangeTblEntry *
 transformTableEntry(ParseState *pstate, RangeVar *r)
 {
 	RelExpr    *baserel = r->relExpr;
 	char	   *relname = baserel->relname;
-	char	   *refname = r->name;
+#if 0
+	char	   *refname;
+	List	   *columns;
+#endif
 	RangeTblEntry *rte;
 
-	if (refname == NULL)
+#if 0
+	if (r->name != NULL)
+		refname = r->name->relname;
+	else
+		refname = NULL;
+
+	columns = ListTableAsAttrs(pstate, relname);
+
+	/* alias might be specified... */
+	if (r->name != NULL)
+	{
+#ifndef DISABLE_JOIN_SYNTAX
+		if (length(columns) > 0)
+		{
+			if (length(r->name->attrs) > 0)
+			{
+				if (length(columns) != length(r->name->attrs))
+					elog(ERROR, "'%s' has %d columns but %d %s specified",
+						 relname, length(columns), length(r->name->attrs),
+						 ((length(r->name->attrs) != 1)? "aliases": "alias"));
+
+				aliasList = nconc(aliasList, r->name->attrs);
+			}
+			else
+			{
+				r->name->attrs = columns;
+
+				aliasList = nconc(aliasList, r->name->attrs);
+			}
+		}
+		else
+		{
+			elog(NOTICE, "transformTableEntry: column aliases not handled (internal error)");
+		}
+#else
+		elog(ERROR, "Column aliases not yet supported");
+#endif
+	}
+	else
+	{
 		refname = relname;
+		aliasList = nconc(aliasList, columns);
+	}
+#endif
+
+	if (r->name == NULL)
+		r->name = makeAttr(relname, NULL);
 
 	/*
 	 * marks this entry to indicate it comes from the FROM clause. In SQL,
@@ -242,11 +414,12 @@ transformTableEntry(ParseState *pstate, RangeVar *r)
 	 * we expand * to foo.x.
 	 */
 
-	rte = addRangeTableEntry(pstate, relname, refname,
+	rte = addRangeTableEntry(pstate, relname, r->name,
 							 baserel->inh, TRUE, TRUE);
 
-	return refname;
-}
+	return rte;
+} /* transformTableEntry() */
+
 
 /*
  * parseFromClause -
@@ -263,12 +436,13 @@ transformTableEntry(ParseState *pstate, RangeVar *r)
  * - thomas 1998-12-16
  */
 static void
-parseFromClause(ParseState *pstate, List *frmList, Node **qual)
+parseFromClause(ParseState *pstate, List *frmList)
 {
-	List	   *fl;
+//	List *shape, *alias;
+//	Node **qual;
+//	char *lname, *rname;
 
-	if (qual != NULL)
-		*qual = NULL;
+	List *fl;
 
 	foreach(fl, frmList)
 	{
@@ -285,60 +459,258 @@ parseFromClause(ParseState *pstate, List *frmList, Node **qual)
 		 * eg. select * from foo f where f.x = 1; will generate wrong answer
 		 * if we expand * to foo.x.
 		 */
+
+		/* Plain vanilla inner join, just like we've always had? */
 		if (IsA(n, RangeVar))
+		{
 			transformTableEntry(pstate, (RangeVar *) n);
+		}
+
+		/* A newfangled join expression? */
 		else if (IsA(n, JoinExpr))
 		{
-			JoinExpr   *j = (JoinExpr *) n;
+#ifndef DISABLE_JOIN_SYNTAX
+//			char			   *lname, *rname;
+			RangeTblEntry	   *l_rte, *r_rte;
+			Attr			   *l_name, *r_name;
+			JoinExpr *j = (JoinExpr *) n;
 
-#ifdef ENABLE_OUTER_JOINS
-			char	   *lname = transformTableEntry(pstate, (RangeVar *) j->larg);
+			if (j->alias != NULL)
+				elog(ERROR, "JOIN table aliases are not supported");
 
-#endif
-			char	   *rname;
-
-			if (IsA((Node *) j->rarg, RangeVar))
-				rname = transformTableEntry(pstate, (RangeVar *) j->rarg);
+			/* nested join? then handle the left one first... */
+			if (IsA(j->larg, JoinExpr))
+			{
+				parseFromClause(pstate, lcons(j->larg, NIL));
+				l_name = ((JoinExpr *)j->larg)->alias;
+			}
 			else
-				elog(ERROR, "Nested JOINs are not yet supported");
+			{
+				Assert(IsA(j->larg, RangeVar));
+				l_rte = transformTableEntry(pstate, (RangeVar *) j->larg);
+				l_name = expandTable(pstate, l_rte->ref->relname, TRUE);
+			}
+
+			if (IsA(j->rarg, JoinExpr))
+			{
+//				elog(ERROR, "Nested JOINs are not yet supported");
+				parseFromClause(pstate, lcons(j->rarg, NIL));
+				l_name = ((JoinExpr *)j->larg)->alias;
+			}
+			else
+			{
+				Assert(IsA(j->rarg, RangeVar));
+				r_rte = transformTableEntry(pstate, (RangeVar *) j->rarg);
+				r_name = expandTable(pstate, r_rte->ref->relname, TRUE);
+			}
+
+			/* Natural join does not explicitly specify columns; must generate columns to join.
+			 * Need to run through the list of columns from each table or join result
+			 * and match up the column names. Use the first table, and check every
+			 * column in the second table for a match.
+			 */
+			if (j->isNatural)
+			{
+				List *lx, *rx;
+				List *rlist = NULL;
+
+				foreach(lx, l_name->attrs)
+				{
+					Ident *id = NULL;
+					Value *l_col = lfirst(lx);
+					Assert(IsA(l_col, String));
+
+					foreach(rx, r_name->attrs)
+					{
+						Value *r_col = lfirst(rx);
+						Assert(IsA(r_col, String));
+
+//						if (equal(l_col, r_col))
+						if (strcmp(strVal(l_col), strVal(r_col)) == 0)
+						{
+							id = (Ident *) makeNode(Ident);
+							id->name = strVal(l_col);
+							break;
+						}
+					}
+
+					/* right column matched? then keep as join column... */
+					if (id != NULL)
+						rlist = lappend(rlist, id);
+				}
+				j->quals = rlist;
+
+				printf("NATURAL JOIN columns are %s\n", nodeToString(rlist));
+			}
 
-#ifdef ENABLE_OUTER_JOINS
 			if (j->jointype == INNER_P)
 			{
+				/* CROSS JOIN */
+				if (j->quals == NULL)
+				{
+					printf("CROSS JOIN...\n");
+				}
 
-				/*
+				/* JOIN/USING
 				 * This is an inner join, so rip apart the join node and
 				 * transform into a traditional FROM list. NATURAL JOIN
-				 * and USING clauses both change the shape of the result.
+				 * and JOIN USING both change the shape of the result.
 				 * Need to generate a list of result columns to use for
-				 * target list expansion and validation. Not doing this
-				 * yet though!
+				 * target list expansion and validation.
 				 */
-				if (IsA(j->quals, List))
-					j->quals = lcons(transformUsingClause(pstate, (List *) j->quals, lname, rname), NIL);
+				else if (IsA(j->quals, List))
+				{
+					/*
+					 * List of Ident nodes means column names from a real USING
+					 * clause. Determine the shape of the joined table.
+					 */
+//					List *ltable, *rtable;
+					List *ucols, *ucol;
+					List *shape = NULL;
+					List *alias = NULL;
+					List *l_shape, *r_shape;
 
+					List *l_cols = makeAttrList(l_name);
+					List *r_cols = makeAttrList(r_name);
+
+					printf("USING input tables are:\n %s\n %s\n",
+						   nodeToString(l_name), nodeToString(r_name));
+
+					printf("USING expanded tables are:\n %s\n %s\n",
+						   nodeToString(l_cols), nodeToString(r_cols));
+
+					/* Columns from the USING clause... */
+					ucols = (List *)j->quals;
+					foreach(ucol, ucols)
+					{
+						List *col;
+						Attr *l_attr = NULL, *r_attr = NULL;
+						Ident *id = lfirst(ucol);
+
+						Attr *attr = makeAttr("", id->name);
+
+						foreach(col, l_cols)
+						{
+							attr = lfirst(col);
+							if (strcmp(AttrString(attr), id->name) == 0)
+							{
+								l_attr = attr;
+								break;
+							}
+						}
+
+						foreach(col, r_cols)
+						{
+							attr = lfirst(col);
+							if (strcmp(AttrString(attr), id->name) == 0)
+							{
+								r_attr = attr;
+								break;
+							}
+						}
+
+						if (l_attr == NULL)
+							elog(ERROR, "USING column '%s' not found in table '%s'",
+								 id->name, l_name->relname);
+						if (r_attr == NULL)
+							elog(ERROR, "USING column '%s' not found in table '%s'",
+								 id->name, r_name->relname);
+
+						shape = lappend(shape, l_attr);
+						alias = lappend(alias, makeAttr("", AttrString(l_attr)));
+					}
+					printf("JOIN/USING join columns are %s\n", nodeToString(shape));
+
+					/* Remaining columns from the left side... */
+					l_shape = makeUniqueAttrList(makeAttrList(l_name), shape);
+
+					printf("JOIN/USING left columns are %s\n", nodeToString(l_shape));
+
+					r_shape = makeUniqueAttrList(makeAttrList(r_name), shape);
+
+					printf("JOIN/USING right columns are %s\n", nodeToString(r_shape));
+
+					printf("JOIN/USING input quals are %s\n", nodeToString(j->quals));
+
+					j->quals = (List *) transformUsingClause(pstate, shape, l_cols, r_cols);
+
+					printf("JOIN/USING transformed quals are %s\n", nodeToString(j->quals));
+
+					alias = nconc(nconc(alias, listCopy(l_shape)), listCopy(r_shape));
+					shape = nconc(nconc(shape, l_shape), r_shape);
+
+					printf("JOIN/USING shaped table is %s\n", nodeToString(shape));
+					printf("JOIN/USING alias list is %s\n", nodeToString(alias));
+
+					pstate->p_shape = shape;
+					pstate->p_alias = alias;
+				}
+
+				/* otherwise, must be an expression from an ON clause... */
+				else
+				{
+					j->quals = (List *) lcons(j->quals, NIL);
+				}
+
+				pstate->p_join_quals = (Node *) j->quals;
+
+#if 0
 				if (qual == NULL)
 					elog(ERROR, "JOIN/ON not supported in this context");
 
+				printf("Table aliases are %s\n", nodeToString(*aliasList));
+#endif
+
+#if 0
 				if (*qual == NULL)
-					*qual = lfirst(j->quals);
+				{
+#endif
+
+#if 0
+					/* merge qualified join clauses... */
+				if (j->quals != NULL)
+				{
+					if (*qual != NULL)
+					{
+						A_Expr	   *a = makeNode(A_Expr);
+
+						a->oper = AND;
+						a->opname = NULL;
+						a->lexpr = (Node *) *qual;
+						a->rexpr = (Node *) j->quals;
+
+						*qual = (Node *)a;
+					}
+					else
+					{
+						*qual = (Node *)j->quals;
+					}
+				}
+#endif
+
+#if 0
+				}
 				else
+				{
 					elog(ERROR, "Multiple JOIN/ON clauses not handled (internal error)");
+					*qual = lappend(*qual, j->quals);
+				}
+#endif
 
 				/*
 				 * if we are transforming this node back into a FROM list,
 				 * then we will need to replace the node with two nodes.
 				 * Will need access to the previous list item to change
 				 * the link pointer to reference these new nodes. Try
-				 * accumulating and returning a new list. - thomas
-				 * 1999-01-08 Not doing this yet though!
+				 * accumulating and returning a new list.
+				 * - thomas 1999-01-08 Not doing this yet though!
 				 */
 
 			}
 			else if ((j->jointype == LEFT)
 					 || (j->jointype == RIGHT)
 					 || (j->jointype == FULL))
-				elog(ERROR, "OUTER JOIN is not implemented");
+				elog(ERROR, "OUTER JOIN is not yet supported");
 			else
 				elog(ERROR, "Unrecognized JOIN clause; tag is %d (internal error)",
 					 j->jointype);
@@ -350,7 +722,7 @@ parseFromClause(ParseState *pstate, List *frmList, Node **qual)
 			elog(ERROR, "parseFromClause: unexpected FROM clause node (internal error)"
 				 "\n\t%s", nodeToString(n));
 	}
-}
+} /* parseFromClause() */
 
 
 /*
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 76e28fbfec..365378f407 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.67 2000/01/26 05:56:42 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.68 2000/02/15 03:37:47 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -144,12 +144,12 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 							Node	   *rexpr = transformExpr(pstate, a->rexpr, precedence);
 
 							if (exprType(lexpr) != BOOLOID)
-								elog(ERROR, "left-hand side of AND is type '%s', not bool",
-									 typeidTypeName(exprType(lexpr)));
+								elog(ERROR, "left-hand side of AND is type '%s', not '%s'",
+									 typeidTypeName(exprType(lexpr)), typeidTypeName(BOOLOID));
 
 							if (exprType(rexpr) != BOOLOID)
-								elog(ERROR, "right-hand side of AND is type '%s', not bool",
-									 typeidTypeName(exprType(rexpr)));
+								elog(ERROR, "right-hand side of AND is type '%s', not '%s'",
+									 typeidTypeName(exprType(rexpr)), typeidTypeName(BOOLOID));
 
 							expr->typeOid = BOOLOID;
 							expr->opType = AND_EXPR;
@@ -164,11 +164,11 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 							Node	   *rexpr = transformExpr(pstate, a->rexpr, precedence);
 
 							if (exprType(lexpr) != BOOLOID)
-								elog(ERROR, "left-hand side of OR is type '%s', not bool",
-									 typeidTypeName(exprType(lexpr)));
+								elog(ERROR, "left-hand side of OR is type '%s', not '%s'",
+									 typeidTypeName(exprType(lexpr)), typeidTypeName(BOOLOID));
 							if (exprType(rexpr) != BOOLOID)
-								elog(ERROR, "right-hand side of OR is type '%s', not bool",
-									 typeidTypeName(exprType(rexpr)));
+								elog(ERROR, "right-hand side of OR is type '%s', not '%s'",
+									 typeidTypeName(exprType(rexpr)), typeidTypeName(BOOLOID));
 							expr->typeOid = BOOLOID;
 							expr->opType = OR_EXPR;
 							expr->args = makeList(lexpr, rexpr, -1);
@@ -181,8 +181,8 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 							Node	   *rexpr = transformExpr(pstate, a->rexpr, precedence);
 
 							if (exprType(rexpr) != BOOLOID)
-								elog(ERROR, "argument to NOT is type '%s', not bool",
-									 typeidTypeName(exprType(rexpr)));
+								elog(ERROR, "argument to NOT is type '%s', not '%s'",
+									 typeidTypeName(exprType(rexpr)), typeidTypeName(BOOLOID));
 							expr->typeOid = BOOLOID;
 							expr->opType = NOT_EXPR;
 							expr->args = makeList(rexpr, -1);
@@ -223,11 +223,11 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 				pstate->p_hasSubLinks = true;
 				qtrees = parse_analyze(lcons(sublink->subselect, NIL), pstate);
 				if (length(qtrees) != 1)
-					elog(ERROR, "parser: bad query in subselect");
+					elog(ERROR, "Bad query in subselect");
 				qtree = (Query *) lfirst(qtrees);
 				if (qtree->commandType != CMD_SELECT ||
 					qtree->resultRelation != 0)
-					elog(ERROR, "parser: bad query in subselect");
+					elog(ERROR, "Bad query in subselect");
 				sublink->subselect = (Node *) qtree;
 
 				if (sublink->subLinkType == EXISTS_SUBLINK)
@@ -247,11 +247,11 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 					 */
 					if (tlist == NIL ||
 						((TargetEntry *) lfirst(tlist))->resdom->resjunk)
-						elog(ERROR, "parser: subselect must have a field");
+						elog(ERROR, "Subselect must have a field");
 					while ((tlist = lnext(tlist)) != NIL)
 					{
 						if (! ((TargetEntry *) lfirst(tlist))->resdom->resjunk)
-							elog(ERROR, "parser: subselect must have only one field");
+							elog(ERROR, "Subselect must have only one field");
 					}
 					/* EXPR needs no lefthand or combining operator.
 					 * These fields should be NIL already, but make sure.
@@ -274,7 +274,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 					/* Combining operators other than =/<> is dubious... */
 					if (length(left_list) != 1 &&
 						strcmp(op, "=") != 0 && strcmp(op, "<>") != 0)
-						elog(ERROR, "parser: '%s' is not usable for row comparison",
+						elog(ERROR, "Row comparison cannot use '%s'",
 							 op);
 
 					sublink->oper = NIL;
@@ -297,7 +297,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 							continue;
 
 						if (left_list == NIL)
-							elog(ERROR, "parser: Subselect has too many fields.");
+							elog(ERROR, "Subselect has too many fields");
 						lexpr = lfirst(left_list);
 						left_list = lnext(left_list);
 
@@ -308,7 +308,10 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 						opform = (Form_pg_operator) GETSTRUCT(optup);
 
 						if (opform->oprresult != BOOLOID)
-							elog(ERROR, "parser: '%s' must return 'bool' to be used with quantified predicate subquery", op);
+							elog(ERROR, "'%s' result type of '%s' must return '%s'"
+								 " to be used with quantified predicate subquery",
+								 op, typeidTypeName(opform->oprresult),
+								 typeidTypeName(BOOLOID));
 
 						newop = makeOper(oprid(optup),/* opno */
 										 InvalidOid, /* opid */
@@ -318,7 +321,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 						sublink->oper = lappend(sublink->oper, newop);
 					}
 					if (left_list != NIL)
-						elog(ERROR, "parser: Subselect has too few fields.");
+						elog(ERROR, "Subselect has too few fields");
 				}
 				result = (Node *) expr;
 				break;
@@ -430,7 +433,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 					}
 					else
 					{
-						elog(ERROR, "CASE/ELSE unable to convert to type %s",
+						elog(ERROR, "CASE/ELSE unable to convert to type '%s'",
 							 typeidTypeName(ptype));
 					}
 				}
@@ -457,7 +460,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 						}
 						else
 						{
-							elog(ERROR, "CASE/WHEN unable to convert to type %s",
+							elog(ERROR, "CASE/WHEN unable to convert to type '%s'",
 								 typeidTypeName(ptype));
 						}
 					}
@@ -519,8 +522,8 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 			}
 		default:
 			/* should not reach here */
-			elog(ERROR, "transformExpr: does not know how to transform node %d",
-				 nodeTag(expr));
+			elog(ERROR, "transformExpr: does not know how to transform node %d"
+				 " (internal error)", nodeTag(expr));
 			break;
 	}
 
@@ -566,18 +569,22 @@ transformIdent(ParseState *pstate, Ident *ident, int precedence)
 		if ((rte = colnameRangeTableEntry(pstate, ident->name)) != NULL)
 		{
 			/* Convert it to a fully qualified Attr, and transform that */
+#ifndef DISABLE_JOIN_SYNTAX
+			Attr	   *att = makeAttr(rte->ref->relname, ident->name);
+#else
 			Attr	   *att = makeNode(Attr);
 
 			att->relname = rte->refname;
 			att->paramNo = NULL;
 			att->attrs = lcons(makeString(ident->name), NIL);
+#endif
 			att->indirection = ident->indirection;
 			return transformAttr(pstate, att, precedence);
 		}
 	}
 
 	if (result == NULL)
-		elog(ERROR, "attribute '%s' not found", ident->name);
+		elog(ERROR, "Attribute '%s' not found", ident->name);
 
 	return result;
 }
@@ -631,7 +638,7 @@ exprType(Node *expr)
 					TargetEntry *tent;
 
 					if (! qtree || ! IsA(qtree, Query))
-						elog(ERROR, "exprType: can't get type for untransformed sublink");
+						elog(ERROR, "Cannot get type for untransformed sublink");
 					tent = (TargetEntry *) lfirst(qtree->targetList);
 					type = tent->resdom->restype;
 				}
@@ -653,7 +660,7 @@ exprType(Node *expr)
 			type = UNKNOWNOID;
 			break;
 		default:
-			elog(ERROR, "exprType: don't know how to get type for %d node",
+			elog(ERROR, "Do not know how to get type for %d node",
 				 nodeTag(expr));
 			break;
 	}
@@ -728,7 +735,7 @@ parser_typecast_constant(Value *expr, TypeName *typename)
 			break;
 		default:
 			elog(ERROR,
-				 "parser_typecast_constant: cannot cast this expression to type '%s'",
+				 "Cannot cast this expression to type '%s'",
 				 typename->name);
 	}
 
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 9686317944..06afc86790 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.68 2000/01/26 05:56:42 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.69 2000/02/15 03:37:47 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -283,6 +283,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
 		if (IsA(first_arg, Ident) && ((Ident *) first_arg)->isRel)
 		{
 			RangeTblEntry *rte;
+			AttrNumber attnum;
 			Ident	   *ident = (Ident *) first_arg;
 
 			/*
@@ -293,7 +294,8 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
 			rte = refnameRangeTableEntry(pstate, refname);
 			if (rte == NULL)
 			{
-				rte = addRangeTableEntry(pstate, refname, refname,
+				rte = addRangeTableEntry(pstate, refname,
+										 makeAttr(refname, NULL),
 										 FALSE, FALSE, TRUE);
 #ifdef WARN_FROM
 				elog(NOTICE,"Adding missing FROM-clause entry%s for table %s",
@@ -304,12 +306,53 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
 
 			relname = rte->relname;
 			relid = rte->relid;
+			attnum = InvalidAttrNumber;
 
 			/*
 			 * If the attr isn't a set, just make a var for it.  If it is
 			 * a set, treat it like a function and drop through.
+			 * Look through the explicit column list first, since we
+			 * now allow column aliases.
+			 * - thomas 2000-02-07
 			 */
-			if (get_attnum(relid, funcname) != InvalidAttrNumber)
+			if (rte->ref->attrs != NULL)
+			{
+				List   *c;
+				/* start counting attributes/columns from one.
+				 * zero is reserved for InvalidAttrNumber.
+				 * - thomas 2000-01-27
+				 */
+				int		i = 1;
+				foreach (c, rte->ref->attrs)
+				{
+					char *colname = strVal(lfirst(c));
+					/* found a match? */
+					if (strcmp(colname, funcname) == 0)
+					{
+						char *basename = get_attname(relid, i);
+
+						if (basename != NULL)
+						{
+							funcname = basename;
+							attnum = i;
+						}
+						/* attnum was initialized to InvalidAttrNumber
+						 * earlier, so no need to reset it if the
+						 * above test fails. - thomas 2000-02-07
+						 */
+						break;
+					}
+					i++;
+				}
+				if (attnum == InvalidAttrNumber)
+					attnum = specialAttNum(funcname);
+			}
+			else
+			{
+				attnum = get_attnum(relid, funcname);
+			}
+
+			if (attnum != InvalidAttrNumber)
 			{
 				return (Node *) make_var(pstate,
 										 relid,
@@ -474,7 +517,8 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
 			rte = refnameRangeTableEntry(pstate, refname);
 			if (rte == NULL)
 			{
-				rte = addRangeTableEntry(pstate, refname, refname,
+				rte = addRangeTableEntry(pstate, refname,
+										 makeAttr(refname, NULL),
 										 FALSE, FALSE, TRUE);
 #ifdef WARN_FROM
 				elog(NOTICE,"Adding missing FROM-clause entry%s for table %s",
@@ -485,7 +529,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
 
 			relname = rte->relname;
 
-			vnum = refnameRangeTablePosn(pstate, rte->refname, NULL);
+			vnum = refnameRangeTablePosn(pstate, rte->ref->relname, NULL);
 
 			/*
 			 * for func(relname), the param to the function is the tuple
@@ -593,7 +637,9 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
 	if (attisset)
 	{
 		if (!strcmp(funcname, "*"))
-			funcnode->func_tlist = expandAll(pstate, relname, refname, curr_resno);
+			funcnode->func_tlist = expandAll(pstate, relname,
+											 makeAttr(refname, NULL),
+											 curr_resno);
 		else
 		{
 			funcnode->func_tlist = setup_tlist(funcname, argrelid);
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 4e72a7c029..02a3cd2de0 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.34 2000/01/26 05:56:42 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.35 2000/02/15 03:37:47 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -65,6 +65,39 @@ static char *attnum_type[SPECIALS] = {
 	"cid",
 };
 
+/* refnameRangeTableEntries()
+ * Given refname, return a list of range table entries
+ * This is possible with JOIN syntax, where tables in a join
+ * acquire the same reference name
+ * - thomas 2000-01-20
+ */
+List *
+refnameRangeTableEntries(ParseState *pstate, char *refname);
+
+List *
+refnameRangeTableEntries(ParseState *pstate, char *refname)
+{
+	List	   *rteList = NULL;
+	List	   *temp;
+
+	while (pstate != NULL)
+	{
+		foreach(temp, pstate->p_rtable)
+		{
+			RangeTblEntry *rte = lfirst(temp);
+
+			if (strcmp(rte->ref->relname, refname) == 0)
+				rteList = lappend(rteList, rte);
+		}
+		/* only allow correlated columns in WHERE clause */
+		if (pstate->p_in_where_clause)
+			pstate = pstate->parentParseState;
+		else
+			break;
+	}
+	return rteList;
+}
+
 /* given refname, return a pointer to the range table entry */
 RangeTblEntry *
 refnameRangeTableEntry(ParseState *pstate, char *refname)
@@ -77,7 +110,11 @@ refnameRangeTableEntry(ParseState *pstate, char *refname)
 		{
 			RangeTblEntry *rte = lfirst(temp);
 
+#ifndef DISABLE_JOIN_SYNTAX
+			if (strcmp(rte->ref->relname, refname) == 0)
+#else
 			if (!strcmp(rte->refname, refname))
+#endif
 				return rte;
 		}
 		/* only allow correlated columns in WHERE clause */
@@ -106,7 +143,11 @@ refnameRangeTablePosn(ParseState *pstate, char *refname, int *sublevels_up)
 		{
 			RangeTblEntry *rte = lfirst(temp);
 
+#ifndef DISABLE_JOIN_SYNTAX
+			if (strcmp(rte->ref->relname, refname) == 0)
+#else
 			if (!strcmp(rte->refname, refname))
+#endif
 				return index;
 			index++;
 		}
@@ -143,24 +184,52 @@ colnameRangeTableEntry(ParseState *pstate, char *colname)
 
 		foreach(et, rtable)
 		{
+			RangeTblEntry *rte_candidate = NULL;
 			RangeTblEntry *rte = lfirst(et);
 
 			/* only consider RTEs mentioned in FROM or UPDATE/DELETE */
 			if (!rte->inFromCl && rte != pstate->p_target_rangetblentry)
 				continue;
 
-			if (get_attnum(rte->relid, colname) != InvalidAttrNumber)
+			if (rte->ref->attrs != NULL)
 			{
-				if (rte_result != NULL)
+				List *c;
+				foreach (c, rte->ref->attrs)
 				{
-					if (!pstate->p_is_insert ||
-						rte != pstate->p_target_rangetblentry)
-						elog(ERROR, "Column '%s' is ambiguous", colname);
+					if (strcmp(strVal(lfirst(c)), colname) == 0)
+					{
+						if (rte_candidate != NULL)
+							elog(ERROR, "Column '%s' is ambiguous"
+								 " (internal error)", colname);
+						rte_candidate = rte;
+					}
 				}
-				else
-					rte_result = rte;
 			}
+
+			/* Even if we have an attribute list in the RTE,
+			 * look for the column here anyway. This is the only
+			 * way we will find implicit columns like "oid".
+			 * - thomas 2000-02-07
+			 */
+			if ((rte_candidate == NULL)
+				&& (get_attnum(rte->relid, colname) != InvalidAttrNumber))
+			{
+				rte_candidate = rte;
+			}
+
+			if (rte_candidate == NULL)
+				continue;
+
+			if (rte_result != NULL)
+			{
+				if (!pstate->p_is_insert ||
+					rte != pstate->p_target_rangetblentry)
+					elog(ERROR, "Column '%s' is ambiguous", colname);
+			}
+			else
+				rte_result = rte;
 		}
+
 		/* only allow correlated columns in WHERE clause */
 		if (pstate->p_in_where_clause && rte_result == NULL)
 			pstate = pstate->parentParseState;
@@ -177,45 +246,65 @@ colnameRangeTableEntry(ParseState *pstate, char *colname)
 RangeTblEntry *
 addRangeTableEntry(ParseState *pstate,
 				   char *relname,
-				   char *refname,
+				   Attr *ref,
 				   bool inh,
 				   bool inFromCl,
 				   bool inJoinSet)
 {
-	Relation	relation;
-	RangeTblEntry *rte;
-	int			sublevels_up;
+	Relation		rel;
+	RangeTblEntry  *rte;
+	int				maxattrs;
+	int				sublevels_up;
+	int				varattno;
 
+	/* Look for an existing rte, if available... */
 	if (pstate != NULL)
 	{
-		int			rt_index = refnameRangeTablePosn(pstate, refname,
-													 &sublevels_up);
+		int rt_index = refnameRangeTablePosn(pstate, ref->relname,
+											 &sublevels_up);
 
 		if (rt_index != 0 && (!inFromCl || sublevels_up == 0))
 		{
-			if (!strcmp(refname, "*CURRENT*") || !strcmp(refname, "*NEW*"))
+			if (!strcmp(ref->relname, "*CURRENT*") || !strcmp(ref->relname, "*NEW*"))
 				return (RangeTblEntry *) nth(rt_index - 1, pstate->p_rtable);
-			elog(ERROR, "Table name '%s' specified more than once", refname);
+			elog(ERROR, "Table name '%s' specified more than once", ref->relname);
 		}
 	}
 
 	rte = makeNode(RangeTblEntry);
 
-	rte->relname = pstrdup(relname);
-	rte->refname = pstrdup(refname);
+	rte->relname = relname;
+	rte->ref = ref;
 
 	/* Get the rel's OID.  This access also ensures that we have an
 	 * up-to-date relcache entry for the rel.  We don't need to keep
 	 * it open, however.
+	 * Since this is open anyway, let's check that the number of column
+	 * aliases is reasonable.
+	 * - Thomas 2000-02-04
 	 */
-	relation = heap_openr(relname, AccessShareLock);
-	rte->relid = RelationGetRelid(relation);
-	heap_close(relation, AccessShareLock);
+	rel = heap_openr(relname, AccessShareLock);
+	rte->relid = RelationGetRelid(rel);
+	maxattrs = RelationGetNumberOfAttributes(rel);
+	if (maxattrs < length(ref->attrs))
+		elog(ERROR, "Table '%s' has %d columns available but %d columns specified",
+			 relname, maxattrs, length(ref->attrs));
+
+	/* fill in any unspecified alias columns */
+	for (varattno = length(ref->attrs); varattno < maxattrs; varattno++)
+	{
+		char	   *attrname;
+
+		attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
+		ref->attrs = lappend(ref->attrs, makeString(attrname));
+	}
+	heap_close(rel, AccessShareLock);
 
 	/*
-	 * Flags: this RTE should be expanded to include descendant tables,
-	 * this RTE is in the FROM clause, this RTE should be included in
-	 * the planner's final join.
+	 * Flags:
+	 * - this RTE should be expanded to include descendant tables,
+	 * - this RTE is in the FROM clause,
+	 * - this RTE should be included in the planner's final join.
 	 */
 	rte->inh = inh;
 	rte->inFromCl = inFromCl;
@@ -231,23 +320,71 @@ addRangeTableEntry(ParseState *pstate,
 	return rte;
 }
 
+/* expandTable()
+ * Populates an Attr with table name and column names
+ * This is similar to expandAll(), but does not create an RTE
+ * if it does not already exist.
+ * - thomas 2000-01-19
+ */
+Attr *
+expandTable(ParseState *pstate, char *refname, bool getaliases)
+{
+	Attr			   *attr;
+	RangeTblEntry	   *rte;
+	Relation			rel;
+	int			varattno,
+				maxattrs;
+
+	rte = refnameRangeTableEntry(pstate, refname);
+
+	if (getaliases && (rte != NULL) && (rte->ref != NULL)
+		&& (length(rte->ref->attrs) > 0))
+	{
+		return rte->ref;
+	}
+
+	if (rte != NULL)
+		rel = heap_open(rte->relid, AccessShareLock);
+	else
+		rel = heap_openr(refname, AccessShareLock);
+
+	if (rel == NULL)
+		elog(ERROR, "Relation '%s' not found", refname);
+
+	maxattrs = RelationGetNumberOfAttributes(rel);
+
+	attr = makeAttr(refname, NULL);
+
+	for (varattno = 0; varattno < maxattrs; varattno++)
+	{
+		char	   *attrname;
+
+		attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
+		attr->attrs = lappend(attr->attrs, makeString(attrname));
+	}
+
+	heap_close(rel, AccessShareLock);
+
+	return attr;
+}
+
 /*
  * expandAll -
  *	  makes a list of attributes
  */
 List *
-expandAll(ParseState *pstate, char *relname, char *refname, int *this_resno)
+expandAll(ParseState *pstate, char *relname, Attr *ref, int *this_resno)
 {
-	List	   *te_list = NIL;
-	RangeTblEntry *rte;
-	Relation	rel;
-	int			varattno,
-				maxattrs;
+	List		   *te_list = NIL;
+	RangeTblEntry  *rte;
+	Relation		rel;
+	int				varattno,
+					maxattrs;
 
-	rte = refnameRangeTableEntry(pstate, refname);
+	rte = refnameRangeTableEntry(pstate, ref->relname);
 	if (rte == NULL)
 	{
-		rte = addRangeTableEntry(pstate, relname, refname,
+		rte = addRangeTableEntry(pstate, relname, ref,
 								 FALSE, FALSE, TRUE);
 #ifdef WARN_FROM
 		elog(NOTICE,"Adding missing FROM-clause entry%s for table %s",
@@ -262,12 +399,19 @@ expandAll(ParseState *pstate, char *relname, char *refname, int *this_resno)
 
 	for (varattno = 0; varattno < maxattrs; varattno++)
 	{
-		char	   *attrname;
-		Var		   *varnode;
-		TargetEntry *te = makeNode(TargetEntry);
+		char		   *attrname;
+		char		   *label;
+		Var			   *varnode;
+		TargetEntry	   *te = makeNode(TargetEntry);
 
 		attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
-		varnode = make_var(pstate, rte->relid, refname, attrname);
+
+		/* varattno is zero-based, so check that length() is always greater */
+		if (length(rte->ref->attrs) > varattno)
+			label = pstrdup(strVal(nth(varattno, rte->ref->attrs)));
+		else
+			label = attrname;
+		varnode = make_var(pstate, rte->relid, relname, attrname);
 
 		/*
 		 * Even if the elements making up a set are complex, the set
@@ -277,7 +421,7 @@ expandAll(ParseState *pstate, char *relname, char *refname, int *this_resno)
 		te->resdom = makeResdom((AttrNumber) (*this_resno)++,
 								varnode->vartype,
 								varnode->vartypmod,
-								attrname,
+								label,
 								(Index) 0,
 								(Oid) 0,
 								false);
@@ -306,16 +450,32 @@ attnameAttNum(Relation rd, char *a)
 		if (!namestrcmp(&(rd->rd_att->attrs[i]->attname), a))
 			return i + 1;
 
-	for (i = 0; i < SPECIALS; i++)
-		if (!strcmp(special_attr[i].field, a))
-			return special_attr[i].code;
+	if ((i = specialAttNum(a)) != InvalidAttrNumber)
+		return i;
 
 	/* on failure */
 	elog(ERROR, "Relation '%s' does not have attribute '%s'",
 		 RelationGetRelationName(rd), a);
-	return 0;					/* lint */
+	return InvalidAttrNumber;		/* lint */
 }
 
+/* specialAttNum()
+ * Check attribute name to see if it is "special", e.g. "oid".
+ * - thomas 2000-02-07
+ */
+int
+specialAttNum(char *a)
+{
+	int			i;
+
+	for (i = 0; i < SPECIALS; i++)
+		if (!strcmp(special_attr[i].field, a))
+			return special_attr[i].code;
+
+	return InvalidAttrNumber;
+}
+
+
 /*
  * Given range variable, return whether attribute of this name
  * is a set.
@@ -372,3 +532,8 @@ attnumTypeId(Relation rd, int attid)
 	 */
 	return rd->rd_att->attrs[attid - 1]->atttypid;
 }
+
+
+
+
+
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 653afe7058..9d00e1789e 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.54 2000/01/26 05:56:42 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.55 2000/02/15 03:37:47 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -105,8 +105,35 @@ transformTargetList(ParseState *pstate, List *targetlist)
 				 * Target item is a single '*', expand all tables
 				 * (eg. SELECT * FROM emp)
 				 */
-				p_target = nconc(p_target,
-								 ExpandAllTables(pstate));
+				if (pstate->p_shape != NULL)
+				{
+					List *s, *a;
+					int i;
+
+					Assert(length(pstate->p_shape) == length(pstate->p_alias));
+
+					s = pstate->p_shape;
+					a = pstate->p_alias;
+					for (i = 0; i < length(pstate->p_shape); i++)
+					{
+						TargetEntry	   *te;
+						char		   *colname;
+						Attr *shape = lfirst(s);
+						Attr *alias = lfirst(a);
+
+						Assert(IsA(shape, Attr) && IsA(alias, Attr));
+
+						colname = strVal(lfirst(alias->attrs));
+						te = transformTargetEntry(pstate, (Node *) shape,
+												  NULL, colname, false);
+						p_target = lappend(p_target, te);
+						s = lnext(s);
+						a = lnext(a);
+					}
+				}
+				else
+					p_target = nconc(p_target,
+									 ExpandAllTables(pstate));
 			}
 			else if (att->attrs != NIL &&
 					 strcmp(strVal(lfirst(att->attrs)), "*") == 0)
@@ -116,9 +143,8 @@ transformTargetList(ParseState *pstate, List *targetlist)
 				 * (eg. SELECT emp.*, dname FROM emp, dept)
 				 */
 				p_target = nconc(p_target,
-								 expandAll(pstate,
-										   att->relname,
-										   att->relname,
+								 expandAll(pstate, att->relname,
+										   makeAttr(att->relname, NULL),
 										   &pstate->p_last_resno));
 			}
 			else
@@ -192,12 +218,18 @@ updateTargetListEntry(ParseState *pstate,
 	 */
 	if (indirection)
 	{
+#ifndef DISABLE_JOIN_SYNTAX
+		Attr	   *att = makeAttr(pstrdup(RelationGetRelationName(rd)), colname);
+#else
 		Attr	   *att = makeNode(Attr);
+#endif
 		Node	   *arrayBase;
 		ArrayRef   *aref;
 
+#ifdef DISABLE_JOIN_SYNTAX
 		att->relname = pstrdup(RelationGetRelationName(rd));
 		att->attrs = lcons(makeString(colname), NIL);
+#endif
 		arrayBase = ParseNestedFuncOrColumn(pstate, att,
 											&pstate->p_last_resno,
 											EXPR_COLUMN_FIRST);
@@ -355,10 +387,9 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
 	return cols;
 }
 
-/*
- * ExpandAllTables -
- *	  turns '*' (in the target list) into a list of attributes
- *	   (of all relations in the range table)
+/* ExpandAllTables()
+ * Turns '*' (in the target list) into a list of attributes
+ * (of all relations in the range table)
  */
 static List *
 ExpandAllTables(ParseState *pstate)
@@ -378,7 +409,7 @@ ExpandAllTables(ParseState *pstate)
 
 	/* SELECT *; */
 	if (rtable == NIL)
-		elog(ERROR, "Wildcard with no tables specified.");
+		elog(ERROR, "Wildcard with no tables specified not allowed");
 
 	foreach(rt, rtable)
 	{
@@ -393,7 +424,7 @@ ExpandAllTables(ParseState *pstate)
 			continue;
 
 		target = nconc(target,
-					   expandAll(pstate, rte->relname, rte->refname,
+					   expandAll(pstate, rte->ref->relname, rte->ref,
 								 &pstate->p_last_resno));
 	}
 	return target;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index c4e3149160..d3c2534505 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
  *			  out of its tuple
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.39 2000/01/15 22:43:24 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.40 2000/02/15 03:37:56 thomas Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -922,9 +922,9 @@ get_select_query_def(Query *query, deparse_context *context)
 			continue;
 
 		rte = (RangeTblEntry *) lfirst(l);
-		if (!strcmp(rte->refname, "*NEW*"))
+		if (!strcmp(rte->ref->relname, "*NEW*"))
 			continue;
-		if (!strcmp(rte->refname, "*CURRENT*"))
+		if (!strcmp(rte->ref->relname, "*CURRENT*"))
 			continue;
 
 		rt_constonly = FALSE;
@@ -980,10 +980,10 @@ get_select_query_def(Query *query, deparse_context *context)
 			{
 				rte = (RangeTblEntry *) lfirst(l);
 
-				if (!strcmp(rte->refname, "*NEW*"))
+				if (!strcmp(rte->ref->relname, "*NEW*"))
 					continue;
 
-				if (!strcmp(rte->refname, "*CURRENT*"))
+				if (!strcmp(rte->ref->relname, "*CURRENT*"))
 					continue;
 
 				appendStringInfo(buf, sep);
@@ -991,9 +991,19 @@ get_select_query_def(Query *query, deparse_context *context)
 				appendStringInfo(buf, "%s%s",
 								 quote_identifier(rte->relname),
 								 inherit_marker(rte));
-				if (strcmp(rte->relname, rte->refname) != 0)
+				if (strcmp(rte->relname, rte->ref->relname) != 0)
+				{
+					List *col;
 					appendStringInfo(buf, " %s",
-									 quote_identifier(rte->refname));
+									 quote_identifier(rte->ref->relname));
+					appendStringInfo(buf, " (");
+					foreach (col, rte->ref->attrs)
+					{
+						if (col != lfirst(rte->ref->attrs))
+							appendStringInfo(buf, ", ");
+						appendStringInfo(buf, "%s", strVal(col));
+					}
+				}
 			}
 		}
 	}
@@ -1071,9 +1081,9 @@ get_insert_query_def(Query *query, deparse_context *context)
 			continue;
 
 		rte = (RangeTblEntry *) lfirst(l);
-		if (!strcmp(rte->refname, "*NEW*"))
+		if (!strcmp(rte->ref->relname, "*NEW*"))
 			continue;
-		if (!strcmp(rte->refname, "*CURRENT*"))
+		if (!strcmp(rte->ref->relname, "*CURRENT*"))
 			continue;
 
 		rt_constonly = FALSE;
@@ -1241,13 +1251,13 @@ get_rule_expr(Node *node, deparse_context *context)
 
 				if (context->varprefix)
 				{
-					if (!strcmp(rte->refname, "*NEW*"))
+					if (!strcmp(rte->ref->relname, "*NEW*"))
 						appendStringInfo(buf, "new.");
-					else if (!strcmp(rte->refname, "*CURRENT*"))
+					else if (!strcmp(rte->ref->relname, "*CURRENT*"))
 						appendStringInfo(buf, "old.");
 					else
 						appendStringInfo(buf, "%s.",
-										 quote_identifier(rte->refname));
+										 quote_identifier(rte->ref->relname));
 				}
 				appendStringInfo(buf, "%s",
 						quote_identifier(get_attribute_name(rte->relid,
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
index 2b3c5339c6..f934eb5e63 100644
--- a/src/include/nodes/makefuncs.h
+++ b/src/include/nodes/makefuncs.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: makefuncs.h,v 1.22 2000/01/26 05:58:16 momjian Exp $
+ * $Id: makefuncs.h,v 1.23 2000/02/15 03:38:13 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -46,4 +46,7 @@ extern Const *makeConst(Oid consttype,
 		  bool constisset,
 		  bool constiscast);
 
+extern Attr *
+makeAttr(char *relname, char *attname);
+
 #endif	 /* MAKEFUNC_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 288e7f96b8..6eb47618c5 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.97 2000/01/27 18:11:44 tgl Exp $
+ * $Id: parsenodes.h,v 1.98 2000/02/15 03:38:14 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1031,7 +1031,7 @@ typedef struct RangeVar
 {
 	NodeTag		type;
 	RelExpr    *relExpr;		/* the relation expression */
-	char	   *name;			/* the name to be referenced (optional) */
+	Attr	   *name;			/* the name to be referenced (optional) */
 } RangeVar;
 
 /*
@@ -1064,9 +1064,11 @@ typedef struct JoinExpr
 {
 	NodeTag		type;
 	int			jointype;
-	RangeVar   *larg;
-	Node	   *rarg;
-	List	   *quals;
+	bool		isNatural;		/* Natural join? Will need to shape table */
+	Node	   *larg;			/* RangeVar or join expression */
+	Node	   *rarg;			/* RangeVar or join expression */
+	Attr	   *alias;			/* table and column aliases, if any */
+	List	   *quals;			/* qualifiers on join, if any */
 } JoinExpr;
 
 
@@ -1122,8 +1124,10 @@ typedef struct RangeTblEntry
 {
 	NodeTag		type;
 	char	   *relname;		/* real name of the relation */
-	char	   *refname;		/* the reference name (as specified in the
-								 * FROM clause) */
+//	char	   *refname;		/* reference name (given in FROM clause) */
+#ifndef DISABLE_JOIN_SYNTAX
+	Attr	   *ref;			/* reference names (given in FROM clause) */
+#endif
 	Oid			relid;			/* OID of the relation */
 	bool		inh;			/* inheritance requested? */
 	bool		inFromCl;		/* present in FROM clause */
diff --git a/src/include/parser/parse_clause.h b/src/include/parser/parse_clause.h
index 235a02bc4c..58b8fc60fa 100644
--- a/src/include/parser/parse_clause.h
+++ b/src/include/parser/parse_clause.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_clause.h,v 1.15 2000/01/27 18:11:47 tgl Exp $
+ * $Id: parse_clause.h,v 1.16 2000/02/15 03:38:28 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,10 +16,9 @@
 
 #include "parser/parse_node.h"
 
-extern void makeRangeTable(ParseState *pstate, List *frmList, Node **qual);
+extern void makeRangeTable(ParseState *pstate, List *frmList);
 extern void setTargetTable(ParseState *pstate, char *relname);
-extern Node *transformWhereClause(ParseState *pstate, Node *where,
-								  Node *using);
+extern Node *transformWhereClause(ParseState *pstate, Node *where);
 extern List *transformGroupClause(ParseState *pstate, List *grouplist,
 								  List *targetlist);
 extern List *transformSortClause(ParseState *pstate, List *orderlist,
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 4ba502ebaf..16641b530a 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_node.h,v 1.17 2000/01/26 05:58:27 momjian Exp $
+ * $Id: parse_node.h,v 1.18 2000/02/15 03:38:29 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,7 +16,11 @@
 #include "nodes/parsenodes.h"
 #include "utils/rel.h"
 
-/* state information used during parse analysis */
+/* State information used during parse analysis
+ * p_join_quals is a list of qualification expressions
+ * found in the FROM clause. Needs to be available later
+ * to merge with other qualifiers from the WHERE clause.
+ */
 typedef struct ParseState
 {
 	int			p_last_resno;
@@ -30,6 +34,9 @@ typedef struct ParseState
 	bool		p_in_where_clause;
 	Relation	p_target_relation;
 	RangeTblEntry *p_target_rangetblentry;
+	List	   *p_shape;
+	List	   *p_alias;
+	Node	   *p_join_quals;
 } ParseState;
 
 extern ParseState *make_parsestate(ParseState *parentParseState);
diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h
index b9fe0b1b77..5ba5db3f9d 100644
--- a/src/include/parser/parse_relation.h
+++ b/src/include/parser/parse_relation.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_relation.h,v 1.14 2000/01/26 05:58:27 momjian Exp $
+ * $Id: parse_relation.h,v 1.15 2000/02/15 03:38:29 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,17 +18,20 @@
 
 extern RangeTblEntry *refnameRangeTableEntry(ParseState *pstate, char *refname);
 extern int refnameRangeTablePosn(ParseState *pstate,
-					  char *refname, int *sublevels_up);
+								 char *refname,
+								 int *sublevels_up);
 extern RangeTblEntry *colnameRangeTableEntry(ParseState *pstate, char *colname);
 extern RangeTblEntry *addRangeTableEntry(ParseState *pstate,
-				   char *relname,
-				   char *refname,
-				   bool inh,
-				   bool inFromCl,
-				   bool inJoinSet);
-extern List *expandAll(ParseState *pstate, char *relname, char *refname,
-		  int *this_resno);
+										 char *relname,
+										 Attr *ref,
+										 bool inh,
+										 bool inFromCl,
+										 bool inJoinSet);
+extern Attr *expandTable(ParseState *pstate, char *refname, bool getaliases);
+extern List *expandAll(ParseState *pstate, char *relname, Attr *ref,
+					   int *this_resno);
 extern int	attnameAttNum(Relation rd, char *a);
+extern int	specialAttNum(char *a);
 extern bool attnameIsSet(Relation rd, char *name);
 extern int	attnumAttNelems(Relation rd, int attid);
 extern Oid	attnumTypeId(Relation rd, int attid);
diff --git a/src/include/parser/parsetree.h b/src/include/parser/parsetree.h
index 979ebf327e..3f5e09cc1d 100644
--- a/src/include/parser/parsetree.h
+++ b/src/include/parser/parsetree.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsetree.h,v 1.8 2000/01/26 05:58:27 momjian Exp $
+ * $Id: parsetree.h,v 1.9 2000/02/15 03:38:29 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -39,8 +39,8 @@
  */
 
 #define rt_relname(rt_entry) \
-	  ((!strcmp(((rt_entry)->refname),"*CURRENT*") ||\
-		!strcmp(((rt_entry)->refname),"*NEW*")) ? ((rt_entry)->refname) : \
+	  ((!strcmp(((rt_entry)->ref->relname),"*CURRENT*") ||\
+		!strcmp(((rt_entry)->ref->relname),"*NEW*")) ? ((rt_entry)->ref->relname) : \
 		((char *)(rt_entry)->relname))
 
 /*