diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 3b9bcb7d56..cc15e5ab48 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.97 2009/06/11 14:48:57 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.97.2.1 2010/01/05 23:25:44 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 */ /* @@ -1500,10 +1536,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"))); @@ -1519,9 +1556,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"))); diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index bc0831933e..dec484d4af 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.122 2009/06/11 14:48:59 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.122.2.1 2010/01/05 23:25:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -981,10 +981,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 */ } diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 57be60626f..c2e1bd8c47 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.205.2.1 2009/08/23 18:26:15 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.205.2.2 2010/01/05 23:25:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1344,6 +1344,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 @@ -1367,6 +1368,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; diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index c7b46928f2..4b1b73b109 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -2416,3 +2416,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) + diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index e6ee2215f9..a0dd6dde33 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -543,3 +543,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;