diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2010-01-05 23:25:36 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2010-01-05 23:25:36 +0000 |
commit | 90f4c2d960a3f3b9d51d8349119f0fbb4c35fc9b (patch) | |
tree | 88f8e96f26083306f7523bd9c81394cc07246afa /src/backend/executor/nodeMergejoin.c | |
parent | d86d51a95810caebcea587498068ff32fe28293e (diff) | |
download | postgresql-90f4c2d960a3f3b9d51d8349119f0fbb4c35fc9b.tar.gz postgresql-90f4c2d960a3f3b9d51d8349119f0fbb4c35fc9b.zip |
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.
Diffstat (limited to 'src/backend/executor/nodeMergejoin.c')
-rw-r--r-- | src/backend/executor/nodeMergejoin.c | 59 |
1 files changed, 49 insertions, 10 deletions
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 362181c26f4..0c611015134 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"))); |