Skip to content

Commit b4def32

Browse files
author
Thomas G. Lockhart
committed
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
1 parent 449020f commit b4def32

File tree

1 file changed

+222
-20
lines changed

1 file changed

+222
-20
lines changed

src/backend/parser/parse_clause.c

Lines changed: 222 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.28 1999/02/13 23:17:07 momjian Exp $
10+
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.29 1999/02/23 07:46:42 thomas Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -26,7 +26,9 @@
2626
#include "parser/parse_relation.h"
2727
#include "parser/parse_target.h"
2828
#include "parser/parse_coerce.h"
29+
#include "nodes/print.h"
2930

31+
#include "parse.h"
3032

3133

3234
#define ORDER_CLAUSE 0
@@ -37,7 +39,15 @@ static char *clauseText[] = {"ORDER", "GROUP"};
3739
static TargetEntry *
3840
findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause);
3941

40-
static void parseFromClause(ParseState *pstate, List *frmList);
42+
static void parseFromClause(ParseState *pstate, List *frmList, Node **qual);
43+
44+
Attr *makeAttr(char *relname, char *attname);
45+
46+
#ifdef ENABLE_OUTER_JOINS
47+
Node *transformUsingClause(ParseState *pstate, List *onList, char *lname, char *rname);
48+
#endif
49+
50+
char *transformTableEntry(ParseState *pstate, RangeVar *r);
4151

4252

4353
/*
@@ -46,18 +56,18 @@ static void parseFromClause(ParseState *pstate, List *frmList);
4656
* from_clause.
4757
*/
4858
void
49-
makeRangeTable(ParseState *pstate, char *relname, List *frmList)
59+
makeRangeTable(ParseState *pstate, char *relname, List *frmList, Node **qual)
5060
{
5161
RangeTblEntry *rte;
5262
int sublevels_up;
5363

54-
parseFromClause(pstate, frmList);
64+
parseFromClause(pstate, frmList, qual);
5565

5666
if (relname == NULL)
5767
return;
5868

59-
if (refnameRangeTablePosn(pstate, relname, &sublevels_up) == 0 ||
60-
sublevels_up != 0)
69+
if ((refnameRangeTablePosn(pstate, relname, &sublevels_up) == 0)
70+
|| (sublevels_up != 0))
6171
rte = addRangeTableEntry(pstate, relname, relname, FALSE, FALSE);
6272
else
6373
rte = refnameRangeTableEntry(pstate, relname);
@@ -77,17 +87,35 @@ makeRangeTable(ParseState *pstate, char *relname, List *frmList)
7787
* transformWhereClause -
7888
* transforms the qualification and make sure it is of type Boolean
7989
*
90+
* Now accept an additional argument, which is a qualification derived
91+
* from the JOIN/ON or JOIN/USING syntax.
92+
* - thomas 1998-12-16
8093
*/
8194
Node *
82-
transformWhereClause(ParseState *pstate, Node *a_expr)
95+
transformWhereClause(ParseState *pstate, Node *a_expr, Node *o_expr)
8396
{
97+
A_Expr *expr;
8498
Node *qual;
8599

86-
if (a_expr == NULL)
100+
if ((a_expr == NULL) && (o_expr == NULL))
87101
return NULL; /* no qualifiers */
88102

103+
if ((a_expr != NULL) && (o_expr != NULL))
104+
{
105+
A_Expr *a = makeNode(A_Expr);
106+
a->oper = AND;
107+
a->opname = NULL;
108+
a->lexpr = o_expr;
109+
a->rexpr = a_expr;
110+
expr = a;
111+
}
112+
else if (o_expr != NULL)
113+
expr = (A_Expr *)o_expr;
114+
else
115+
expr = (A_Expr *)a_expr;
116+
89117
pstate->p_in_where_clause = true;
90-
qual = transformExpr(pstate, a_expr, EXPR_COLUMN_FIRST);
118+
qual = transformExpr(pstate, (Node *)expr, EXPR_COLUMN_FIRST);
91119
pstate->p_in_where_clause = false;
92120

93121
if (exprType(qual) != BOOLOID)
@@ -98,6 +126,122 @@ transformWhereClause(ParseState *pstate, Node *a_expr)
98126
return qual;
99127
}
100128

129+
Attr *
130+
makeAttr(char *relname, char *attname)
131+
{
132+
Attr *a = makeNode(Attr);
133+
a->relname = relname;
134+
a->paramNo = NULL;
135+
a->attrs = lcons(makeString(attname), NIL);
136+
a->indirection = NULL;
137+
138+
return a;
139+
}
140+
141+
#ifdef ENABLE_OUTER_JOINS
142+
/* transformUsingClause()
143+
* Take an ON or USING clause from a join expression and expand if necessary.
144+
*/
145+
Node *
146+
transformUsingClause(ParseState *pstate, List *onList, char *lname, char *rname)
147+
{
148+
A_Expr *expr = NULL;
149+
List *on;
150+
Node *qual;
151+
152+
foreach (on, onList)
153+
{
154+
qual = lfirst(on);
155+
156+
/* Ident node means it is just a column name from a real USING clause... */
157+
if (IsA(qual, Ident))
158+
{
159+
Ident *i = (Ident *)qual;
160+
Attr *lattr = makeAttr(lname, i->name);
161+
Attr *rattr = makeAttr(rname, i->name);
162+
A_Expr *e = makeNode(A_Expr);
163+
164+
#ifdef PARSEDEBUG
165+
printf("transformUsingClause- transform %s", nodeToString(i));
166+
#endif
167+
168+
e->oper = OP;
169+
e->opname = "=";
170+
e->lexpr = (Node *)lattr;
171+
e->rexpr = (Node *)rattr;
172+
173+
if (expr != NULL)
174+
{
175+
A_Expr *a = makeNode(A_Expr);
176+
a->oper = AND;
177+
a->opname = NULL;
178+
a->lexpr = (Node *)expr;
179+
a->rexpr = (Node *)e;
180+
expr = a;
181+
}
182+
else
183+
expr = e;
184+
}
185+
186+
/* otherwise, we have an expression from an ON clause... */
187+
else
188+
{
189+
if (expr != NULL)
190+
{
191+
A_Expr *a = makeNode(A_Expr);
192+
a->oper = AND;
193+
a->opname = NULL;
194+
a->lexpr = (Node *)expr;
195+
a->rexpr = (Node *)qual;
196+
expr = a;
197+
}
198+
else
199+
{
200+
expr = (A_Expr *)qual;
201+
}
202+
203+
#ifdef PARSEDEBUG
204+
printf("transformUsingClause- transform %s", nodeToString(qual));
205+
#endif
206+
207+
}
208+
209+
#ifdef PARSEDEBUG
210+
printf(" to %s\n", nodeToString(expr));
211+
#endif
212+
}
213+
return ((Node *)transformExpr(pstate, (Node *)expr, EXPR_COLUMN_FIRST));
214+
}
215+
#endif
216+
217+
char *
218+
transformTableEntry(ParseState *pstate, RangeVar *r)
219+
{
220+
RelExpr *baserel = r->relExpr;
221+
char *relname = baserel->relname;
222+
char *refname = r->name;
223+
RangeTblEntry *rte;
224+
225+
if (refname == NULL)
226+
refname = relname;
227+
228+
/*
229+
* marks this entry to indicate it comes from the FROM clause. In
230+
* SQL, the target list can only refer to range variables
231+
* specified in the from clause but we follow the more powerful
232+
* POSTQUEL semantics and automatically generate the range
233+
* variable if not specified. However there are times we need to
234+
* know whether the entries are legitimate.
235+
*
236+
* eg. select * from foo f where f.x = 1; will generate wrong answer
237+
* if we expand * to foo.x.
238+
*/
239+
240+
rte = addRangeTableEntry(pstate, relname, refname, baserel->inh, TRUE);
241+
242+
return refname;
243+
}
244+
101245
/*
102246
* parseFromClause -
103247
* turns the table references specified in the from-clause into a
@@ -106,23 +250,23 @@ transformWhereClause(ParseState *pstate, Node *a_expr)
106250
* allow references to relations not specified in the from-clause. We
107251
* also allow now as an extension.)
108252
*
253+
* The FROM clause can now contain JoinExpr nodes, which contain parsing info
254+
* for inner and outer joins. The USING clause must be expanded into a qualification
255+
* for an inner join at least, since that is compatible with the old syntax.
256+
* Not sure yet how to handle outer joins, but it will become clear eventually?
257+
* - thomas 1998-12-16
109258
*/
110259
static void
111-
parseFromClause(ParseState *pstate, List *frmList)
260+
parseFromClause(ParseState *pstate, List *frmList, Node **qual)
112261
{
113262
List *fl;
114263

264+
if (qual != NULL)
265+
*qual = NULL;
266+
115267
foreach(fl, frmList)
116268
{
117-
RangeVar *r = lfirst(fl);
118-
RelExpr *baserel = r->relExpr;
119-
char *relname = baserel->relname;
120-
char *refname = r->name;
121-
RangeTblEntry *rte;
122-
123-
if (refname == NULL)
124-
refname = relname;
125-
269+
Node *n = lfirst(fl);
126270
/*
127271
* marks this entry to indicate it comes from the FROM clause. In
128272
* SQL, the target list can only refer to range variables
@@ -134,7 +278,65 @@ parseFromClause(ParseState *pstate, List *frmList)
134278
* eg. select * from foo f where f.x = 1; will generate wrong answer
135279
* if we expand * to foo.x.
136280
*/
137-
rte = addRangeTableEntry(pstate, relname, refname, baserel->inh, TRUE);
281+
if (IsA(n, RangeVar))
282+
{
283+
transformTableEntry(pstate, (RangeVar *)n);
284+
}
285+
else if (IsA(n, JoinExpr))
286+
{
287+
JoinExpr *j = (JoinExpr *)n;
288+
char *lname = transformTableEntry(pstate, (RangeVar *)j->larg);
289+
char *rname;
290+
291+
if (IsA((Node *)j->rarg, RangeVar))
292+
rname = transformTableEntry(pstate, (RangeVar *)j->rarg);
293+
else
294+
elog(ERROR, "Nested JOINs are not yet supported");
295+
296+
#ifdef ENABLE_OUTER_JOINS
297+
if (j->jointype == INNER_P)
298+
{
299+
/* This is an inner join, so rip apart the join node
300+
* and transform into a traditional FROM list.
301+
* NATURAL JOIN and USING clauses both change the shape
302+
* of the result. Need to generate a list of result columns
303+
* to use for target list expansion and validation.
304+
* Not doing this yet though!
305+
*/
306+
if (IsA(j->quals, List))
307+
j->quals = lcons(transformUsingClause(pstate, (List *)j->quals, lname, rname), NIL);
308+
309+
Assert(qual != NULL);
310+
311+
if (*qual == NULL)
312+
*qual = lfirst(j->quals);
313+
else
314+
elog(ERROR, "Multiple JOIN/ON clauses not handled (internal error)");
315+
316+
/* if we are transforming this node back into a FROM list,
317+
* then we will need to replace the node with two nodes.
318+
* Will need access to the previous list item to change
319+
* the link pointer to reference these new nodes.
320+
* Try accumulating and returning a new list.
321+
* - thomas 1999-01-08
322+
* Not doing this yet though!
323+
*/
324+
325+
}
326+
else if ((j->jointype == LEFT)
327+
|| (j->jointype == RIGHT)
328+
|| (j->jointype == FULL))
329+
elog(ERROR, "OUTER JOIN is not implemented");
330+
else
331+
elog(ERROR, "Unrecognized JOIN clause; tag is %d (internal error)",
332+
j->jointype);
333+
#else
334+
elog(ERROR, "JOIN expressions are not yet implemented");
335+
#endif
336+
}
337+
else
338+
elog(ERROR, "parseFromClause: unexpected FROM clause node (internal error)"
339+
"\n\t%s", nodeToString(n));
138340
}
139341
}
140342

0 commit comments

Comments
 (0)