Add support for doing FULL JOIN ON FALSE. While this is really a rather

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.
This commit is contained in:
Tom Lane 2010-01-05 23:25:36 +00:00
parent d86d51a958
commit 90f4c2d960
5 changed files with 116 additions and 13 deletions

View 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")));

View 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 */
}

View 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;

View 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)

View 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;