OSDN Git Service

Add support for doing FULL JOIN ON FALSE. While this is really a rather
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 5 Jan 2010 23:25:36 +0000 (23:25 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 5 Jan 2010 23:25:36 +0000 (23:25 +0000)
peculiar variant of UNION ALL, and so wouldn't likely get written directly
as-is, it's possible for it to arise as a result of simplification of
less-obviously-silly queries.  In particular, now that we can do flattening
of subqueries that have constant outputs and are underneath an outer join,
it's possible for the case to result from simplification of queries of the
type exhibited in bug #5263.  Back-patch to 8.4 to avoid a functionality
regression for this type of query.

src/backend/executor/nodeMergejoin.c
src/backend/optimizer/path/joinpath.c
src/include/nodes/execnodes.h
src/test/regress/expected/join.out
src/test/regress/sql/join.sql

index 362181c..0c61101 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.99 2010/01/02 16:57:44 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.100 2010/01/05 23:25:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -398,8 +398,13 @@ MJCompare(MergeJoinState *mergestate)
         * want to report that the tuples are equal.  Instead, if result is still
         * 0, change it to +1.  This will result in advancing the inner side of
         * the join.
+        *
+        * Likewise, if there was a constant-false joinqual, do not report
+        * equality.  We have to check this as part of the mergequals, else the
+        * rescan logic will do the wrong thing.
         */
-       if (nulleqnull && result == 0)
+       if (result == 0 &&
+               (nulleqnull || mergestate->mj_ConstFalseJoin))
                result = 1;
 
        MemoryContextSwitchTo(oldContext);
@@ -487,6 +492,32 @@ MJFillInner(MergeJoinState *node)
 }
 
 
+/*
+ * Check that a qual condition is constant true or constant false.
+ * If it is constant false (or null), set *is_const_false to TRUE.
+ *
+ * Constant true would normally be represented by a NIL list, but we allow an
+ * actual bool Const as well.  We do expect that the planner will have thrown
+ * away any non-constant terms that have been ANDed with a constant false.
+ */
+static bool
+check_constant_qual(List *qual, bool *is_const_false)
+{
+       ListCell   *lc;
+
+       foreach(lc, qual)
+       {
+               Const  *con = (Const *) lfirst(lc);
+
+               if (!con || !IsA(con, Const))
+                       return false;
+               if (con->constisnull || !DatumGetBool(con->constvalue))
+                       *is_const_false = true;
+       }
+       return true;
+}
+
+
 /* ----------------------------------------------------------------
  *             ExecMergeTupleDump
  *
@@ -1025,9 +1056,13 @@ ExecMergeJoin(MergeJoinState *node)
                                         * state for the rescanned inner tuples.  We know all of
                                         * them will match this new outer tuple and therefore
                                         * won't be emitted as fill tuples.  This works *only*
-                                        * because we require the extra joinquals to be nil when
-                                        * doing a right or full join --- otherwise some of the
-                                        * rescanned tuples might fail the extra joinquals.
+                                        * because we require the extra joinquals to be constant
+                                        * when doing a right or full join --- otherwise some of
+                                        * the rescanned tuples might fail the extra joinquals.
+                                        * This obviously won't happen for a constant-true extra
+                                        * joinqual, while the constant-false case is handled by
+                                        * forcing the merge clause to never match, so we never
+                                        * get here.
                                         */
                                        ExecRestrPos(innerPlan);
 
@@ -1439,6 +1474,7 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
        mergestate->js.joinqual = (List *)
                ExecInitExpr((Expr *) node->join.joinqual,
                                         (PlanState *) mergestate);
+       mergestate->mj_ConstFalseJoin = false;
        /* mergeclauses are handled below */
 
        /*
@@ -1498,10 +1534,11 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
                                                          ExecGetResultType(outerPlanState(mergestate)));
 
                        /*
-                        * Can't handle right or full join with non-nil extra joinclauses.
-                        * This should have been caught by planner.
+                        * Can't handle right or full join with non-constant extra
+                        * joinclauses.  This should have been caught by planner.
                         */
-                       if (node->join.joinqual != NIL)
+                       if (!check_constant_qual(node->join.joinqual,
+                                                                        &mergestate->mj_ConstFalseJoin))
                                ereport(ERROR,
                                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                                 errmsg("RIGHT JOIN is only supported with merge-joinable join conditions")));
@@ -1517,9 +1554,11 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
                                                          ExecGetResultType(innerPlanState(mergestate)));
 
                        /*
-                        * Can't handle right or full join with non-nil extra joinclauses.
+                        * Can't handle right or full join with non-constant extra
+                        * joinclauses.  This should have been caught by planner.
                         */
-                       if (node->join.joinqual != NIL)
+                       if (!check_constant_qual(node->join.joinqual,
+                                                                        &mergestate->mj_ConstFalseJoin))
                                ereport(ERROR,
                                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                                 errmsg("FULL JOIN is only supported with merge-joinable join conditions")));
index f69ffbe..2e208cb 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.128 2010/01/02 16:57:46 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.129 2010/01/05 23:25:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1184,10 +1184,18 @@ select_mergejoin_clauses(PlannerInfo *root,
                if (isouterjoin && restrictinfo->is_pushed_down)
                        continue;
 
+               /* Check that clause is a mergeable operator clause */
                if (!restrictinfo->can_join ||
                        restrictinfo->mergeopfamilies == NIL)
                {
-                       have_nonmergeable_joinclause = true;
+                       /*
+                        * The executor can handle extra joinquals that are constants,
+                        * but not anything else, when doing right/full merge join.  (The
+                        * reason to support constants is so we can do FULL JOIN ON
+                        * FALSE.)
+                        */
+                       if (!restrictinfo->clause || !IsA(restrictinfo->clause, Const))
+                               have_nonmergeable_joinclause = true;
                        continue;                       /* not mergejoinable */
                }
 
index e091d0b..866cd3e 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.216 2010/01/02 16:58:04 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.217 2010/01/05 23:25:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1396,6 +1396,7 @@ typedef struct NestLoopState
  *             Clauses                    info for each mergejoinable clause
  *             JoinState                  current "state" of join.  see execdefs.h
  *             ExtraMarks                 true to issue extra Mark operations on inner scan
+ *             ConstFalseJoin     true if we have a constant-false joinqual
  *             FillOuter                  true if should emit unjoined outer tuples anyway
  *             FillInner                  true if should emit unjoined inner tuples anyway
  *             MatchedOuter       true if found a join match for current outer tuple
@@ -1419,6 +1420,7 @@ typedef struct MergeJoinState
        MergeJoinClause mj_Clauses; /* array of length mj_NumClauses */
        int                     mj_JoinState;
        bool            mj_ExtraMarks;
+       bool            mj_ConstFalseJoin;
        bool            mj_FillOuter;
        bool            mj_FillInner;
        bool            mj_MatchedOuter;
index fed424c..f2b346e 100644 (file)
@@ -2443,3 +2443,51 @@ group by t1.q2 order by 1;
   4567890123456789 |     6
 (4 rows)
 
+--
+-- test the corner cases FULL JOIN ON TRUE and FULL JOIN ON FALSE
+--
+select * from int4_tbl a full join int4_tbl b on true;
+     f1      |     f1      
+-------------+-------------
+           0 |           0
+           0 |      123456
+           0 |     -123456
+           0 |  2147483647
+           0 | -2147483647
+      123456 |           0
+      123456 |      123456
+      123456 |     -123456
+      123456 |  2147483647
+      123456 | -2147483647
+     -123456 |           0
+     -123456 |      123456
+     -123456 |     -123456
+     -123456 |  2147483647
+     -123456 | -2147483647
+  2147483647 |           0
+  2147483647 |      123456
+  2147483647 |     -123456
+  2147483647 |  2147483647
+  2147483647 | -2147483647
+ -2147483647 |           0
+ -2147483647 |      123456
+ -2147483647 |     -123456
+ -2147483647 |  2147483647
+ -2147483647 | -2147483647
+(25 rows)
+
+select * from int4_tbl a full join int4_tbl b on false;
+     f1      |     f1      
+-------------+-------------
+             |           0
+             |      123456
+             |     -123456
+             |  2147483647
+             | -2147483647
+           0 |            
+      123456 |            
+     -123456 |            
+  2147483647 |            
+ -2147483647 |            
+(10 rows)
+
index 46ff66c..9777655 100644 (file)
@@ -561,3 +561,9 @@ from int8_tbl t1 left join
   (select q1, case when q2=1 then 1 else q2 end as q2 from int8_tbl) t2
   on (t1.q2 = t2.q1)
 group by t1.q2 order by 1;
+
+--
+-- test the corner cases FULL JOIN ON TRUE and FULL JOIN ON FALSE
+--
+select * from int4_tbl a full join int4_tbl b on true;
+select * from int4_tbl a full join int4_tbl b on false;