88 *
99 *
1010 * IDENTIFICATION
11- * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.66 2000/09/12 21:07:02 tgl Exp $
11+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.67 2000/09/17 22: 21:27 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
1515
1616#include "postgres.h"
1717
1818#include "access/heapam.h"
19- #include "optimizer/tlist.h"
2019#include "nodes/makefuncs.h"
20+ #include "optimizer/tlist.h"
21+ #include "optimizer/var.h"
2122#include "parser/analyze.h"
2223#include "parser/parse.h"
2324#include "parser/parsetree.h"
@@ -38,12 +39,15 @@ static char *clauseText[] = {"ORDER BY", "GROUP BY", "DISTINCT ON"};
3839static void extractUniqueColumns (List * common_colnames ,
3940List * src_colnames ,List * src_colvars ,
4041List * * res_colnames ,List * * res_colvars );
41- static Node * transformUsingClause (ParseState * pstate ,
42- List * leftVars ,List * rightVars );
42+ static Node * transformJoinUsingClause (ParseState * pstate ,
43+ List * leftVars ,List * rightVars );
44+ static Node * transformJoinOnClause (ParseState * pstate ,JoinExpr * j ,
45+ List * containedRels );
4346static RangeTblRef * transformTableEntry (ParseState * pstate ,RangeVar * r );
4447static RangeTblRef * transformRangeSubselect (ParseState * pstate ,
4548RangeSubselect * r );
46- static Node * transformFromClauseItem (ParseState * pstate ,Node * n );
49+ static Node * transformFromClauseItem (ParseState * pstate ,Node * n ,
50+ List * * containedRels );
4751static TargetEntry * findTargetlistEntry (ParseState * pstate ,Node * node ,
4852List * tlist ,int clause );
4953static List * addTargetToSortList (TargetEntry * tle ,List * sortlist ,
@@ -76,8 +80,9 @@ makeRangeTable(ParseState *pstate, List *frmList)
7680foreach (fl ,frmList )
7781{
7882Node * n = lfirst (fl );
83+ List * containedRels ;
7984
80- n = transformFromClauseItem (pstate ,n );
85+ n = transformFromClauseItem (pstate ,n , & containedRels );
8186pstate -> p_jointree = lappend (pstate -> p_jointree ,n );
8287}
8388}
@@ -164,13 +169,13 @@ extractUniqueColumns(List *common_colnames,
164169* res_colvars = new_colvars ;
165170}
166171
167- /*transformUsingClause ()
168- * Build a complete ON clause from a partially-transformed USING list.
169- * We are given lists of Var nodes representing left and right match columns.
170- * Result is a transformed qualification expression.
172+ /*transformJoinUsingClause ()
173+ * Build a complete ON clause from a partially-transformed USING list.
174+ * We are given lists of nodes representing left and right match columns.
175+ * Result is a transformed qualification expression.
171176 */
172177static Node *
173- transformUsingClause (ParseState * pstate ,List * leftVars ,List * rightVars )
178+ transformJoinUsingClause (ParseState * pstate ,List * leftVars ,List * rightVars )
174179{
175180Node * result = NULL ;
176181List * lvars ,
@@ -210,18 +215,82 @@ transformUsingClause(ParseState *pstate, List *leftVars, List *rightVars)
210215rvars = lnext (rvars );
211216}
212217
218+ /*
219+ * Since the references are already Vars, and are certainly from the
220+ * input relations, we don't have to go through the same pushups that
221+ * transformJoinOnClause() does. Just invoke transformExpr() to fix
222+ * up the operators, and we're done.
223+ */
213224result = transformExpr (pstate ,result ,EXPR_COLUMN_FIRST );
214225
215226if (exprType (result )!= BOOLOID )
216227{
217228/* This could only happen if someone defines a funny version of '=' */
218- elog (ERROR ,"USING clause must return type bool, not type %s" ,
229+ elog (ERROR ,"JOIN/ USING clause must return type bool, not type %s" ,
219230typeidTypeName (exprType (result )));
220231}
221232
222233return result ;
223- }/* transformUsingClause() */
234+ }/* transformJoinUsingClause() */
235+
236+ /* transformJoinOnClause()
237+ * Transform the qual conditions for JOIN/ON.
238+ * Result is a transformed qualification expression.
239+ */
240+ static Node *
241+ transformJoinOnClause (ParseState * pstate ,JoinExpr * j ,
242+ List * containedRels )
243+ {
244+ Node * result ;
245+ List * sv_jointree ;
246+ List * clause_varnos ,
247+ * l ;
248+
249+ /*
250+ * This is a tad tricky, for two reasons. First, at the point where
251+ * we're called, the two subtrees of the JOIN node aren't yet part of
252+ * the pstate's jointree, which means that transformExpr() won't resolve
253+ * unqualified references to their columns correctly. We fix this in a
254+ * slightly klugy way: temporarily make the pstate's jointree consist of
255+ * just those two subtrees (which creates exactly the namespace the ON
256+ * clause should see). This is OK only because the ON clause can't
257+ * legally alter the jointree by causing relation refs to be added.
258+ */
259+ sv_jointree = pstate -> p_jointree ;
260+ pstate -> p_jointree = lcons (j -> larg ,lcons (j -> rarg ,NIL ));
261+
262+ /* This part is just like transformWhereClause() */
263+ result = transformExpr (pstate ,j -> quals ,EXPR_COLUMN_FIRST );
264+ if (exprType (result )!= BOOLOID )
265+ {
266+ elog (ERROR ,"JOIN/ON clause must return type bool, not type %s" ,
267+ typeidTypeName (exprType (result )));
268+ }
269+
270+ pstate -> p_jointree = sv_jointree ;
271+
272+ /*
273+ * Second, we need to check that the ON condition doesn't refer to any
274+ * rels outside the input subtrees of the JOIN. It could do that despite
275+ * our hack on the jointree if it uses fully-qualified names. So, grovel
276+ * through the transformed clause and make sure there are no bogus
277+ * references.
278+ */
279+ clause_varnos = pull_varnos (result );
280+ foreach (l ,clause_varnos )
281+ {
282+ int varno = lfirsti (l );
224283
284+ if (!intMember (varno ,containedRels ))
285+ {
286+ elog (ERROR ,"JOIN/ON clause refers to \"%s\", which is not part of JOIN" ,
287+ rt_fetch (varno ,pstate -> p_rtable )-> eref -> relname );
288+ }
289+ }
290+ freeList (clause_varnos );
291+
292+ return result ;
293+ }
225294
226295/*
227296 * transformTableEntry --- transform a RangeVar (simple relation reference)
@@ -309,25 +378,40 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
309378 * range table list being built in the ParseState, and return the
310379 * transformed item ready to include in the jointree list.
311380 * This routine can recurse to handle SQL92 JOIN expressions.
381+ *
382+ * Aside from the primary return value (the transformed jointree item)
383+ * this routine also returns an integer list of the rangetable indexes
384+ * of all the base relations represented in the jointree item. This
385+ * list is needed for checking JOIN/ON conditions in higher levels.
312386 */
313387static Node *
314- transformFromClauseItem (ParseState * pstate ,Node * n )
388+ transformFromClauseItem (ParseState * pstate ,Node * n , List * * containedRels )
315389{
316390if (IsA (n ,RangeVar ))
317391{
318392/* Plain relation reference */
319- return (Node * )transformTableEntry (pstate , (RangeVar * )n );
393+ RangeTblRef * rtr ;
394+
395+ rtr = transformTableEntry (pstate , (RangeVar * )n );
396+ * containedRels = lconsi (rtr -> rtindex ,NIL );
397+ return (Node * )rtr ;
320398}
321399else if (IsA (n ,RangeSubselect ))
322400{
323- /* Plain relation reference */
324- return (Node * )transformRangeSubselect (pstate , (RangeSubselect * )n );
401+ /* sub-SELECT is like a plain relation */
402+ RangeTblRef * rtr ;
403+
404+ rtr = transformRangeSubselect (pstate , (RangeSubselect * )n );
405+ * containedRels = lconsi (rtr -> rtindex ,NIL );
406+ return (Node * )rtr ;
325407}
326408else if (IsA (n ,JoinExpr ))
327409{
328410/* A newfangled join expression */
329411JoinExpr * j = (JoinExpr * )n ;
330- List * l_colnames ,
412+ List * l_containedRels ,
413+ * r_containedRels ,
414+ * l_colnames ,
331415* r_colnames ,
332416* res_colnames ,
333417* l_colvars ,
@@ -337,8 +421,13 @@ transformFromClauseItem(ParseState *pstate, Node *n)
337421/*
338422 * Recursively process the left and right subtrees
339423 */
340- j -> larg = transformFromClauseItem (pstate ,j -> larg );
341- j -> rarg = transformFromClauseItem (pstate ,j -> rarg );
424+ j -> larg = transformFromClauseItem (pstate ,j -> larg ,& l_containedRels );
425+ j -> rarg = transformFromClauseItem (pstate ,j -> rarg ,& r_containedRels );
426+
427+ /*
428+ * Generate combined list of relation indexes
429+ */
430+ * containedRels = nconc (l_containedRels ,r_containedRels );
342431
343432/*
344433 * Extract column name and var lists from both subtrees
@@ -463,7 +552,7 @@ transformFromClauseItem(ParseState *pstate, Node *n)
463552ndx ++ ;
464553}
465554if (l_index < 0 )
466- elog (ERROR ,"USING column \"%s\" not found in left table" ,
555+ elog (ERROR ,"JOIN/ USING column \"%s\" not found in left table" ,
467556u_colname );
468557
469558ndx = 0 ;
@@ -480,7 +569,7 @@ transformFromClauseItem(ParseState *pstate, Node *n)
480569ndx ++ ;
481570}
482571if (r_index < 0 )
483- elog (ERROR ,"USING column \"%s\" not found in right table" ,
572+ elog (ERROR ,"JOIN/ USING column \"%s\" not found in right table" ,
484573u_colname );
485574
486575l_colvar = nth (l_index ,l_colvars );
@@ -520,18 +609,14 @@ transformFromClauseItem(ParseState *pstate, Node *n)
520609res_colvars = lappend (res_colvars ,colvar );
521610}
522611
523- j -> quals = transformUsingClause (pstate ,l_usingvars ,r_usingvars );
612+ j -> quals = transformJoinUsingClause (pstate ,
613+ l_usingvars ,
614+ r_usingvars );
524615}
525616else if (j -> quals )
526617{
527618/* User-written ON-condition; transform it */
528- j -> quals = transformExpr (pstate ,j -> quals ,EXPR_COLUMN_FIRST );
529- if (exprType (j -> quals )!= BOOLOID )
530- {
531- elog (ERROR ,"ON clause must return type bool, not type %s" ,
532- typeidTypeName (exprType (j -> quals )));
533- }
534- /* XXX should check that ON clause refers only to joined tbls */
619+ j -> quals = transformJoinOnClause (pstate ,j ,* containedRels );
535620}
536621else
537622{