diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index c111b336611364410cf68f85b2dad298cc3d5e4a..0c59e11be60b3c035e1518c6a24e86dc202b8760 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.78 2006/03/05 15:58:26 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.79 2006/03/17 19:38:12 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -948,9 +948,6 @@ ExecMergeJoin(MergeJoinState *node) * now we get the next inner tuple, if any. If there's none, * advance to next outer tuple (which may be able to join to * previously marked tuples). - * - * If we find one but it cannot join to anything, stay in - * NEXTINNER state to fetch the next one. */ innerTupleSlot = ExecProcNode(innerPlan); node->mj_InnerTupleSlot = innerTupleSlot; @@ -963,8 +960,17 @@ ExecMergeJoin(MergeJoinState *node) break; } + /* + * Load up the new inner tuple's comparison values. If we + * see that it contains a NULL and hence can't match any + * outer tuple, we can skip the comparison and assume the + * new tuple is greater than current outer. + */ if (!MJEvalInnerValues(node, innerTupleSlot)) - break; /* stay in NEXTINNER state */ + { + node->mj_JoinState = EXEC_MJ_NEXTOUTER; + break; + } /* * Test the new inner tuple to see if it matches outer. @@ -1054,15 +1060,15 @@ ExecMergeJoin(MergeJoinState *node) } /* Compute join values and check for unmatchability */ - if (!MJEvalOuterValues(node)) + if (MJEvalOuterValues(node)) { - /* Stay in same state to fetch next outer tuple */ - node->mj_JoinState = EXEC_MJ_NEXTOUTER; + /* Go test the new tuple against the marked tuple */ + node->mj_JoinState = EXEC_MJ_TESTOUTER; } else { - /* Go test the tuple */ - node->mj_JoinState = EXEC_MJ_TESTOUTER; + /* Can't match, so fetch next outer tuple */ + node->mj_JoinState = EXEC_MJ_NEXTOUTER; } break; @@ -1071,7 +1077,7 @@ ExecMergeJoin(MergeJoinState *node) * tuple satisfy the merge clause then we know we have * duplicates in the outer scan so we have to restore the * inner scan to the marked tuple and proceed to join the - * new outer tuples with the inner tuples. + * new outer tuple with the inner tuples. * * This is the case when * outer inner @@ -1105,8 +1111,9 @@ ExecMergeJoin(MergeJoinState *node) MJ_printf("ExecMergeJoin: EXEC_MJ_TESTOUTER\n"); /* - * here we must compare the outer tuple with the marked inner - * tuple + * Here we must compare the outer tuple with the marked inner + * tuple. (We can ignore the result of MJEvalInnerValues, + * since the marked inner tuple is certainly matchable.) */ innerTupleSlot = node->mj_MarkedTupleSlot; (void) MJEvalInnerValues(node, innerTupleSlot); @@ -1179,10 +1186,19 @@ ExecMergeJoin(MergeJoinState *node) } /* reload comparison data for current inner */ - (void) MJEvalInnerValues(node, innerTupleSlot); - - /* continue on to skip outer tuples */ - node->mj_JoinState = EXEC_MJ_SKIP_TEST; + if (MJEvalInnerValues(node, innerTupleSlot)) + { + /* proceed to compare it to the current outer */ + node->mj_JoinState = EXEC_MJ_SKIP_TEST; + } + else + { + /* + * current inner can't possibly match any outer; + * better to advance the inner scan than the outer. + */ + node->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE; + } } break; @@ -1293,15 +1309,16 @@ ExecMergeJoin(MergeJoinState *node) } /* Compute join values and check for unmatchability */ - if (!MJEvalOuterValues(node)) + if (MJEvalOuterValues(node)) { - /* Stay in same state to fetch next outer tuple */ + /* Go test the new tuple against the current inner */ + node->mj_JoinState = EXEC_MJ_SKIP_TEST; + } + else + { + /* Can't match, so fetch next outer tuple */ node->mj_JoinState = EXEC_MJ_SKIPOUTER_ADVANCE; - break; } - - /* Test the new tuple against the current inner */ - node->mj_JoinState = EXEC_MJ_SKIP_TEST; break; /* @@ -1356,15 +1373,19 @@ ExecMergeJoin(MergeJoinState *node) } /* Compute join values and check for unmatchability */ - if (!MJEvalInnerValues(node, innerTupleSlot)) + if (MJEvalInnerValues(node, innerTupleSlot)) { - /* Stay in same state to fetch next inner tuple */ + /* proceed to compare it to the current outer */ + node->mj_JoinState = EXEC_MJ_SKIP_TEST; + } + else + { + /* + * current inner can't possibly match any outer; + * better to advance the inner scan than the outer. + */ node->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE; - break; } - - /* Test the new tuple against the current outer */ - node->mj_JoinState = EXEC_MJ_SKIP_TEST; break; /* diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index b110f08130010e1fc34e59ee49c2eff37e5c4cdb..bd92397b3af7f520b5e7757edc63c996ed9afb50 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -2182,3 +2182,31 @@ SELECT * FROM t3; ---+--- (0 rows) +-- +-- regression test for 8.1 merge right join bug +-- +CREATE TEMP TABLE tt1 ( tt1_id int4, joincol int4 ); +INSERT INTO tt1 VALUES (1, 11); +INSERT INTO tt1 VALUES (2, NULL); +CREATE TEMP TABLE tt2 ( tt2_id int4, joincol int4 ); +INSERT INTO tt2 VALUES (21, 11); +INSERT INTO tt2 VALUES (22, 11); +set enable_hashjoin to off; +set enable_nestloop to off; +-- these should give the same results +select tt1.*, tt2.* from tt1 left join tt2 on tt1.joincol = tt2.joincol; + tt1_id | joincol | tt2_id | joincol +--------+---------+--------+--------- + 1 | 11 | 21 | 11 + 1 | 11 | 22 | 11 + 2 | | | +(3 rows) + +select tt1.*, tt2.* from tt2 right join tt1 on tt1.joincol = tt2.joincol; + tt1_id | joincol | tt2_id | joincol +--------+---------+--------+--------- + 1 | 11 | 21 | 11 + 1 | 11 | 22 | 11 + 2 | | | +(3 rows) + diff --git a/src/test/regress/expected/join_1.out b/src/test/regress/expected/join_1.out index 172d6d6fe8ab3fc1285ae1848d76aa2c1491239f..6a38234c1de384d548ff8f42c135139dd5d7f095 100644 --- a/src/test/regress/expected/join_1.out +++ b/src/test/regress/expected/join_1.out @@ -2182,3 +2182,31 @@ SELECT * FROM t3; ---+--- (0 rows) +-- +-- regression test for 8.1 merge right join bug +-- +CREATE TEMP TABLE tt1 ( tt1_id int4, joincol int4 ); +INSERT INTO tt1 VALUES (1, 11); +INSERT INTO tt1 VALUES (2, NULL); +CREATE TEMP TABLE tt2 ( tt2_id int4, joincol int4 ); +INSERT INTO tt2 VALUES (21, 11); +INSERT INTO tt2 VALUES (22, 11); +set enable_hashjoin to off; +set enable_nestloop to off; +-- these should give the same results +select tt1.*, tt2.* from tt1 left join tt2 on tt1.joincol = tt2.joincol; + tt1_id | joincol | tt2_id | joincol +--------+---------+--------+--------- + 1 | 11 | 21 | 11 + 1 | 11 | 22 | 11 + 2 | | | +(3 rows) + +select tt1.*, tt2.* from tt2 right join tt1 on tt1.joincol = tt2.joincol; + tt1_id | joincol | tt2_id | joincol +--------+---------+--------+--------- + 1 | 11 | 21 | 11 + 1 | 11 | 22 | 11 + 2 | | | +(3 rows) + diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index ffc2afe6ed441e2ec996769fd5a4d49b8bee4bcd..149cdadc19084714c4f9ae9129f78198890b69a5 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -369,3 +369,24 @@ DELETE FROM t3 USING t1 JOIN t2 USING (a) WHERE t3.x > t1.a; SELECT * FROM t3; DELETE FROM t3 USING t3 t3_other WHERE t3.x = t3_other.x AND t3.y = t3_other.y; SELECT * FROM t3; + +-- +-- regression test for 8.1 merge right join bug +-- + +CREATE TEMP TABLE tt1 ( tt1_id int4, joincol int4 ); +INSERT INTO tt1 VALUES (1, 11); +INSERT INTO tt1 VALUES (2, NULL); + +CREATE TEMP TABLE tt2 ( tt2_id int4, joincol int4 ); +INSERT INTO tt2 VALUES (21, 11); +INSERT INTO tt2 VALUES (22, 11); + +set enable_hashjoin to off; +set enable_nestloop to off; + +-- these should give the same results + +select tt1.*, tt2.* from tt1 left join tt2 on tt1.joincol = tt2.joincol; + +select tt1.*, tt2.* from tt2 right join tt1 on tt1.joincol = tt2.joincol;