From: Tom Lane Date: Tue, 5 Jan 2010 23:25:36 +0000 (+0000) Subject: Add support for doing FULL JOIN ON FALSE. While this is really a rather X-Git-Tag: REL9_0_0~1216 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=90f4c2d960a3f3b9d51d8349119f0fbb4c35fc9b;p=pg-rex%2Fsyncrep.git 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. --- diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 362181c26f..0c61101513 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.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"))); diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index f69ffbed7a..2e208cb621 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.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 */ } diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e091d0b1d4..866cd3ef3f 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -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; diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index fed424cb33..f2b346e508 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -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) + diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index 46ff66c690..977765551d 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -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;