aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_clause.c
diff options
context:
space:
mode:
authorThomas G. Lockhart <lockhart@fourpalms.org>2000-02-15 03:38:29 +0000
committerThomas G. Lockhart <lockhart@fourpalms.org>2000-02-15 03:38:29 +0000
commita344a6e7b5d7b2b87f33a155c3ef88bdfdce3fd8 (patch)
tree78c19baad416cfe014237bc726baff63aff2b150 /src/backend/parser/parse_clause.c
parent92c8437d8de8efeb5324fcccb0175beec8e66619 (diff)
downloadpostgresql-a344a6e7b5d7b2b87f33a155c3ef88bdfdce3fd8.tar.gz
postgresql-a344a6e7b5d7b2b87f33a155c3ef88bdfdce3fd8.zip
Carry column aliases from the parser frontend. Enables queries like
SELECT a FROM t1 tx (a); Allow join syntax, including queries like SELECT * FROM t1 NATURAL JOIN t2; Update RTE structure to hold column aliases in an Attr structure.
Diffstat (limited to 'src/backend/parser/parse_clause.c')
-rw-r--r--src/backend/parser/parse_clause.c604
1 files changed, 488 insertions, 116 deletions
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index b22691fa3c2..d0a3eb1edb4 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;
+
+ Assert(length(attr->attrs) == 1);
+
+ val = lfirst(attr->attrs);
- a->relname = relname;
- a->paramNo = NULL;
- a->attrs = lcons(makeString(attname), NIL);
- a->indirection = NULL;
+ Assert(IsA(val, String));
- return a;
+ 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);
-
- e->oper = OP;
- e->opname = "=";
- e->lexpr = (Node *) lattr;
- e->rexpr = (Node *) rattr;
+ Attr *attr = lfirst(col);
- if (expr != NULL)
+ if (strcmp(AttrString(attr), AttrString(uattr)) == 0)
{
- A_Expr *a = makeNode(A_Expr);
+ lattr = attr;
+ break;
+ }
+ }
+ foreach (col, rightList)
+ {
+ Attr *attr = lfirst(col);
- a->oper = AND;
- a->opname = NULL;
- a->lexpr = (Node *) expr;
- a->rexpr = (Node *) e;
- expr = a;
+ if (strcmp(AttrString(attr), AttrString(uattr)) == 0)
+ {
+ rattr = attr;
+ break;
}
- else
- expr = e;
}
- /* otherwise, we have an expression from an ON clause... */
- else
+ 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)
{
- if (expr != NULL)
- {
- A_Expr *a = makeNode(A_Expr);
+ 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;
+ a->oper = AND;
+ a->opname = NULL;
+ a->lexpr = (Node *) expr;
+ a->rexpr = (Node *) e;
+ expr = a;
}
+ else
+ 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;
+ /* 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
+ {
+ Assert(IsA(j->larg, RangeVar));
+ l_rte = transformTableEntry(pstate, (RangeVar *) j->larg);
+ l_name = expandTable(pstate, l_rte->ref->relname, TRUE);
+ }
- if (IsA((Node *) j->rarg, RangeVar))
- rname = transformTableEntry(pstate, (RangeVar *) j->rarg);
+ 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
- elog(ERROR, "Nested JOINs are not yet supported");
+ {
+ 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() */
/*