mirror of
https://github.com/postgres/postgres.git
synced 2025-06-14 18:42:34 +03:00
Include some new code for outer joins. Disabled by default, but enable by
including the following in your Makefile.custom: CFLAGS+= -DENABLE_OUTER_JOINS -DEXEC_MERGEJOINDEBUG
This commit is contained in:
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.28 1999/02/13 23:17:07 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.29 1999/02/23 07:46:42 thomas Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -26,7 +26,9 @@
|
|||||||
#include "parser/parse_relation.h"
|
#include "parser/parse_relation.h"
|
||||||
#include "parser/parse_target.h"
|
#include "parser/parse_target.h"
|
||||||
#include "parser/parse_coerce.h"
|
#include "parser/parse_coerce.h"
|
||||||
|
#include "nodes/print.h"
|
||||||
|
|
||||||
|
#include "parse.h"
|
||||||
|
|
||||||
|
|
||||||
#define ORDER_CLAUSE 0
|
#define ORDER_CLAUSE 0
|
||||||
@ -37,7 +39,15 @@ static char *clauseText[] = {"ORDER", "GROUP"};
|
|||||||
static TargetEntry *
|
static TargetEntry *
|
||||||
findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause);
|
findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause);
|
||||||
|
|
||||||
static void parseFromClause(ParseState *pstate, List *frmList);
|
static void parseFromClause(ParseState *pstate, List *frmList, Node **qual);
|
||||||
|
|
||||||
|
Attr *makeAttr(char *relname, char *attname);
|
||||||
|
|
||||||
|
#ifdef ENABLE_OUTER_JOINS
|
||||||
|
Node *transformUsingClause(ParseState *pstate, List *onList, char *lname, char *rname);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char *transformTableEntry(ParseState *pstate, RangeVar *r);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -46,18 +56,18 @@ static void parseFromClause(ParseState *pstate, List *frmList);
|
|||||||
* from_clause.
|
* from_clause.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
makeRangeTable(ParseState *pstate, char *relname, List *frmList)
|
makeRangeTable(ParseState *pstate, char *relname, List *frmList, Node **qual)
|
||||||
{
|
{
|
||||||
RangeTblEntry *rte;
|
RangeTblEntry *rte;
|
||||||
int sublevels_up;
|
int sublevels_up;
|
||||||
|
|
||||||
parseFromClause(pstate, frmList);
|
parseFromClause(pstate, frmList, qual);
|
||||||
|
|
||||||
if (relname == NULL)
|
if (relname == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (refnameRangeTablePosn(pstate, relname, &sublevels_up) == 0 ||
|
if ((refnameRangeTablePosn(pstate, relname, &sublevels_up) == 0)
|
||||||
sublevels_up != 0)
|
|| (sublevels_up != 0))
|
||||||
rte = addRangeTableEntry(pstate, relname, relname, FALSE, FALSE);
|
rte = addRangeTableEntry(pstate, relname, relname, FALSE, FALSE);
|
||||||
else
|
else
|
||||||
rte = refnameRangeTableEntry(pstate, relname);
|
rte = refnameRangeTableEntry(pstate, relname);
|
||||||
@ -77,17 +87,35 @@ makeRangeTable(ParseState *pstate, char *relname, List *frmList)
|
|||||||
* transformWhereClause -
|
* transformWhereClause -
|
||||||
* transforms the qualification and make sure it is of type Boolean
|
* 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 *
|
Node *
|
||||||
transformWhereClause(ParseState *pstate, Node *a_expr)
|
transformWhereClause(ParseState *pstate, Node *a_expr, Node *o_expr)
|
||||||
{
|
{
|
||||||
|
A_Expr *expr;
|
||||||
Node *qual;
|
Node *qual;
|
||||||
|
|
||||||
if (a_expr == NULL)
|
if ((a_expr == NULL) && (o_expr == NULL))
|
||||||
return NULL; /* no qualifiers */
|
return NULL; /* no qualifiers */
|
||||||
|
|
||||||
|
if ((a_expr != NULL) && (o_expr != NULL))
|
||||||
|
{
|
||||||
|
A_Expr *a = makeNode(A_Expr);
|
||||||
|
a->oper = AND;
|
||||||
|
a->opname = NULL;
|
||||||
|
a->lexpr = o_expr;
|
||||||
|
a->rexpr = a_expr;
|
||||||
|
expr = a;
|
||||||
|
}
|
||||||
|
else if (o_expr != NULL)
|
||||||
|
expr = (A_Expr *)o_expr;
|
||||||
|
else
|
||||||
|
expr = (A_Expr *)a_expr;
|
||||||
|
|
||||||
pstate->p_in_where_clause = true;
|
pstate->p_in_where_clause = true;
|
||||||
qual = transformExpr(pstate, a_expr, EXPR_COLUMN_FIRST);
|
qual = transformExpr(pstate, (Node *)expr, EXPR_COLUMN_FIRST);
|
||||||
pstate->p_in_where_clause = false;
|
pstate->p_in_where_clause = false;
|
||||||
|
|
||||||
if (exprType(qual) != BOOLOID)
|
if (exprType(qual) != BOOLOID)
|
||||||
@ -98,23 +126,97 @@ transformWhereClause(ParseState *pstate, Node *a_expr)
|
|||||||
return qual;
|
return qual;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
Attr *
|
||||||
* parseFromClause -
|
makeAttr(char *relname, char *attname)
|
||||||
* turns the table references specified in the from-clause into a
|
|
||||||
* range table. The range table may grow as we transform the expressions
|
|
||||||
* in the target list. (Note that this happens because in POSTQUEL, we
|
|
||||||
* allow references to relations not specified in the from-clause. We
|
|
||||||
* also allow now as an extension.)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
parseFromClause(ParseState *pstate, List *frmList)
|
|
||||||
{
|
{
|
||||||
List *fl;
|
Attr *a = makeNode(Attr);
|
||||||
|
a->relname = relname;
|
||||||
|
a->paramNo = NULL;
|
||||||
|
a->attrs = lcons(makeString(attname), NIL);
|
||||||
|
a->indirection = NULL;
|
||||||
|
|
||||||
foreach(fl, frmList)
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ENABLE_OUTER_JOINS
|
||||||
|
/* transformUsingClause()
|
||||||
|
* Take an ON or USING clause from a join expression and expand if necessary.
|
||||||
|
*/
|
||||||
|
Node *
|
||||||
|
transformUsingClause(ParseState *pstate, List *onList, char *lname, char *rname)
|
||||||
|
{
|
||||||
|
A_Expr *expr = NULL;
|
||||||
|
List *on;
|
||||||
|
Node *qual;
|
||||||
|
|
||||||
|
foreach (on, onList)
|
||||||
|
{
|
||||||
|
qual = lfirst(on);
|
||||||
|
|
||||||
|
/* Ident node means it is just a column name from a real USING clause... */
|
||||||
|
if (IsA(qual, Ident))
|
||||||
|
{
|
||||||
|
Ident *i = (Ident *)qual;
|
||||||
|
Attr *lattr = makeAttr(lname, i->name);
|
||||||
|
Attr *rattr = makeAttr(rname, i->name);
|
||||||
|
A_Expr *e = makeNode(A_Expr);
|
||||||
|
|
||||||
|
#ifdef PARSEDEBUG
|
||||||
|
printf("transformUsingClause- transform %s", nodeToString(i));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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
|
||||||
|
expr = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* otherwise, we have an expression from an ON clause... */
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PARSEDEBUG
|
||||||
|
printf("transformUsingClause- transform %s", nodeToString(qual));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PARSEDEBUG
|
||||||
|
printf(" to %s\n", nodeToString(expr));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return ((Node *)transformExpr(pstate, (Node *)expr, EXPR_COLUMN_FIRST));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char *
|
||||||
|
transformTableEntry(ParseState *pstate, RangeVar *r)
|
||||||
{
|
{
|
||||||
RangeVar *r = lfirst(fl);
|
|
||||||
RelExpr *baserel = r->relExpr;
|
RelExpr *baserel = r->relExpr;
|
||||||
char *relname = baserel->relname;
|
char *relname = baserel->relname;
|
||||||
char *refname = r->name;
|
char *refname = r->name;
|
||||||
@ -134,7 +236,107 @@ parseFromClause(ParseState *pstate, List *frmList)
|
|||||||
* eg. select * from foo f where f.x = 1; will generate wrong answer
|
* eg. select * from foo f where f.x = 1; will generate wrong answer
|
||||||
* if we expand * to foo.x.
|
* if we expand * to foo.x.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
rte = addRangeTableEntry(pstate, relname, refname, baserel->inh, TRUE);
|
rte = addRangeTableEntry(pstate, relname, refname, baserel->inh, TRUE);
|
||||||
|
|
||||||
|
return refname;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* parseFromClause -
|
||||||
|
* turns the table references specified in the from-clause into a
|
||||||
|
* range table. The range table may grow as we transform the expressions
|
||||||
|
* in the target list. (Note that this happens because in POSTQUEL, we
|
||||||
|
* allow references to relations not specified in the from-clause. We
|
||||||
|
* also allow now as an extension.)
|
||||||
|
*
|
||||||
|
* The FROM clause can now contain JoinExpr nodes, which contain parsing info
|
||||||
|
* for inner and outer joins. The USING clause must be expanded into a qualification
|
||||||
|
* for an inner join at least, since that is compatible with the old syntax.
|
||||||
|
* Not sure yet how to handle outer joins, but it will become clear eventually?
|
||||||
|
* - thomas 1998-12-16
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
parseFromClause(ParseState *pstate, List *frmList, Node **qual)
|
||||||
|
{
|
||||||
|
List *fl;
|
||||||
|
|
||||||
|
if (qual != NULL)
|
||||||
|
*qual = NULL;
|
||||||
|
|
||||||
|
foreach(fl, frmList)
|
||||||
|
{
|
||||||
|
Node *n = lfirst(fl);
|
||||||
|
/*
|
||||||
|
* marks this entry to indicate it comes from the FROM clause. In
|
||||||
|
* SQL, the target list can only refer to range variables
|
||||||
|
* specified in the from clause but we follow the more powerful
|
||||||
|
* POSTQUEL semantics and automatically generate the range
|
||||||
|
* variable if not specified. However there are times we need to
|
||||||
|
* know whether the entries are legitimate.
|
||||||
|
*
|
||||||
|
* eg. select * from foo f where f.x = 1; will generate wrong answer
|
||||||
|
* if we expand * to foo.x.
|
||||||
|
*/
|
||||||
|
if (IsA(n, RangeVar))
|
||||||
|
{
|
||||||
|
transformTableEntry(pstate, (RangeVar *)n);
|
||||||
|
}
|
||||||
|
else if (IsA(n, JoinExpr))
|
||||||
|
{
|
||||||
|
JoinExpr *j = (JoinExpr *)n;
|
||||||
|
char *lname = transformTableEntry(pstate, (RangeVar *)j->larg);
|
||||||
|
char *rname;
|
||||||
|
|
||||||
|
if (IsA((Node *)j->rarg, RangeVar))
|
||||||
|
rname = transformTableEntry(pstate, (RangeVar *)j->rarg);
|
||||||
|
else
|
||||||
|
elog(ERROR, "Nested JOINs are not yet supported");
|
||||||
|
|
||||||
|
#ifdef ENABLE_OUTER_JOINS
|
||||||
|
if (j->jointype == INNER_P)
|
||||||
|
{
|
||||||
|
/* 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. Need to generate a list of result columns
|
||||||
|
* to use for target list expansion and validation.
|
||||||
|
* Not doing this yet though!
|
||||||
|
*/
|
||||||
|
if (IsA(j->quals, List))
|
||||||
|
j->quals = lcons(transformUsingClause(pstate, (List *)j->quals, lname, rname), NIL);
|
||||||
|
|
||||||
|
Assert(qual != NULL);
|
||||||
|
|
||||||
|
if (*qual == NULL)
|
||||||
|
*qual = lfirst(j->quals);
|
||||||
|
else
|
||||||
|
elog(ERROR, "Multiple JOIN/ON clauses not handled (internal error)");
|
||||||
|
|
||||||
|
/* 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!
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
else if ((j->jointype == LEFT)
|
||||||
|
|| (j->jointype == RIGHT)
|
||||||
|
|| (j->jointype == FULL))
|
||||||
|
elog(ERROR, "OUTER JOIN is not implemented");
|
||||||
|
else
|
||||||
|
elog(ERROR, "Unrecognized JOIN clause; tag is %d (internal error)",
|
||||||
|
j->jointype);
|
||||||
|
#else
|
||||||
|
elog(ERROR, "JOIN expressions are not yet implemented");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
elog(ERROR, "parseFromClause: unexpected FROM clause node (internal error)"
|
||||||
|
"\n\t%s", nodeToString(n));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user