]> granicus.if.org Git - postgresql/blobdiff - src/backend/executor/nodeMergejoin.c
Update copyright notices for year 2012.
[postgresql] / src / backend / executor / nodeMergejoin.c
index 52bdf3bfe5fd643497196ec28b3009b57e7c384d..f921847db906a85bc0159faae31cdf7609afd8f3 100644 (file)
@@ -3,12 +3,12 @@
  * nodeMergejoin.c
  *       routines supporting merge joins
  *
- * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.96 2009/04/02 20:59:10 momjian Exp $
+ *       src/backend/executor/nodeMergejoin.c
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include "access/nbtree.h"
-#include "catalog/pg_amop.h"
 #include "executor/execdebug.h"
-#include "executor/execdefs.h"
 #include "executor/nodeMergejoin.h"
-#include "miscadmin.h"
-#include "utils/acl.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
-#include "utils/syscache.h"
 
 
+/*
+ * States of the ExecMergeJoin state machine
+ */
+#define EXEC_MJ_INITIALIZE_OUTER               1
+#define EXEC_MJ_INITIALIZE_INNER               2
+#define EXEC_MJ_JOINTUPLES                             3
+#define EXEC_MJ_NEXTOUTER                              4
+#define EXEC_MJ_TESTOUTER                              5
+#define EXEC_MJ_NEXTINNER                              6
+#define EXEC_MJ_SKIP_TEST                              7
+#define EXEC_MJ_SKIPOUTER_ADVANCE              8
+#define EXEC_MJ_SKIPINNER_ADVANCE              9
+#define EXEC_MJ_ENDOUTER                               10
+#define EXEC_MJ_ENDINNER                               11
+
 /*
  * Runtime data for each mergejoin clause
  */
@@ -123,13 +133,19 @@ typedef struct MergeJoinClauseData
        bool            risnull;
 
        /*
-        * The comparison strategy in use, and the lookup info to let us call the
-        * btree comparison support function.
+        * Everything we need to know to compare the left and right values is
+        * stored here.
         */
-       bool            reverse;                /* if true, negate the cmpfn's output */
-       bool            nulls_first;    /* if true, nulls sort low */
-       FmgrInfo        cmpfinfo;
-} MergeJoinClauseData;
+       SortSupportData ssup;
+}      MergeJoinClauseData;
+
+/* Result type for MJEvalOuterValues and MJEvalInnerValues */
+typedef enum
+{
+       MJEVAL_MATCHABLE,                       /* normal, potentially matchable tuple */
+       MJEVAL_NONMATCHABLE,            /* tuple cannot join because it has a null */
+       MJEVAL_ENDOFJOIN                        /* end of input (physical or effective) */
+} MJEvalResult;
 
 
 #define MarkInnerTuple(innerTupleSlot, mergestate) \
@@ -147,17 +163,18 @@ typedef struct MergeJoinClauseData
  * the two expressions from the original clause.
  *
  * In addition to the expressions themselves, the planner passes the btree
- * opfamily OID, btree strategy number (BTLessStrategyNumber or
+ * opfamily OID, collation OID, btree strategy number (BTLessStrategyNumber or
  * BTGreaterStrategyNumber), and nulls-first flag that identify the intended
  * sort ordering for each merge key.  The mergejoinable operator is an
- * equality operator in this opfamily, and the two inputs are guaranteed to be
+ * equality operator in the opfamily, and the two inputs are guaranteed to be
  * ordered in either increasing or decreasing (respectively) order according
- * to this opfamily, with nulls at the indicated end of the range.     This
- * allows us to obtain the needed comparison function from the opfamily.
+ * to the opfamily and collation, with nulls at the indicated end of the range.
+ * This allows us to obtain the needed comparison function from the opfamily.
  */
 static MergeJoinClause
 MJExamineQuals(List *mergeclauses,
                           Oid *mergefamilies,
+                          Oid *mergecollations,
                           int *mergestrategies,
                           bool *mergenullsfirst,
                           PlanState *parent)
@@ -175,13 +192,13 @@ MJExamineQuals(List *mergeclauses,
                OpExpr     *qual = (OpExpr *) lfirst(cl);
                MergeJoinClause clause = &clauses[iClause];
                Oid                     opfamily = mergefamilies[iClause];
+               Oid                     collation = mergecollations[iClause];
                StrategyNumber opstrategy = mergestrategies[iClause];
                bool            nulls_first = mergenullsfirst[iClause];
                int                     op_strategy;
                Oid                     op_lefttype;
                Oid                     op_righttype;
-               RegProcedure cmpproc;
-               AclResult       aclresult;
+               Oid                     sortfunc;
 
                if (!IsA(qual, OpExpr))
                        elog(ERROR, "mergejoin clause is not an OpExpr");
@@ -192,8 +209,19 @@ MJExamineQuals(List *mergeclauses,
                clause->lexpr = ExecInitExpr((Expr *) linitial(qual->args), parent);
                clause->rexpr = ExecInitExpr((Expr *) lsecond(qual->args), parent);
 
+               /* Set up sort support data */
+               clause->ssup.ssup_cxt = CurrentMemoryContext;
+               clause->ssup.ssup_collation = collation;
+               if (opstrategy == BTLessStrategyNumber)
+                       clause->ssup.ssup_reverse = false;
+               else if (opstrategy == BTGreaterStrategyNumber)
+                       clause->ssup.ssup_reverse = true;
+               else    /* planner screwed up */
+                       elog(ERROR, "unsupported mergejoin strategy %d", opstrategy);
+               clause->ssup.ssup_nulls_first = nulls_first;
+
                /* Extract the operator's declared left/right datatypes */
-               get_op_opfamily_properties(qual->opno, opfamily,
+               get_op_opfamily_properties(qual->opno, opfamily, false,
                                                                   &op_strategy,
                                                                   &op_lefttype,
                                                                   &op_righttype);
@@ -201,33 +229,30 @@ MJExamineQuals(List *mergeclauses,
                        elog(ERROR, "cannot merge using non-equality operator %u",
                                 qual->opno);
 
-               /* And get the matching support procedure (comparison function) */
-               cmpproc = get_opfamily_proc(opfamily,
-                                                                       op_lefttype,
-                                                                       op_righttype,
-                                                                       BTORDER_PROC);
-               if (!RegProcedureIsValid(cmpproc))              /* should not happen */
-                       elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
-                                BTORDER_PROC, op_lefttype, op_righttype, opfamily);
-
-               /* Check permission to call cmp function */
-               aclresult = pg_proc_aclcheck(cmpproc, GetUserId(), ACL_EXECUTE);
-               if (aclresult != ACLCHECK_OK)
-                       aclcheck_error(aclresult, ACL_KIND_PROC,
-                                                  get_func_name(cmpproc));
-
-               /* Set up the fmgr lookup information */
-               fmgr_info(cmpproc, &(clause->cmpfinfo));
-
-               /* Fill the additional comparison-strategy flags */
-               if (opstrategy == BTLessStrategyNumber)
-                       clause->reverse = false;
-               else if (opstrategy == BTGreaterStrategyNumber)
-                       clause->reverse = true;
-               else    /* planner screwed up */
-                       elog(ERROR, "unsupported mergejoin strategy %d", opstrategy);
-
-               clause->nulls_first = nulls_first;
+               /* And get the matching support or comparison function */
+               sortfunc = get_opfamily_proc(opfamily,
+                                                                        op_lefttype,
+                                                                        op_righttype,
+                                                                        BTSORTSUPPORT_PROC);
+               if (OidIsValid(sortfunc))
+               {
+                       /* The sort support function should provide a comparator */
+                       OidFunctionCall1(sortfunc, PointerGetDatum(&clause->ssup));
+                       Assert(clause->ssup.comparator != NULL);
+               }
+               else
+               {
+                       /* opfamily doesn't provide sort support, get comparison func */
+                       sortfunc = get_opfamily_proc(opfamily,
+                                                                                op_lefttype,
+                                                                                op_righttype,
+                                                                                BTORDER_PROC);
+                       if (!OidIsValid(sortfunc))              /* should not happen */
+                               elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
+                                        BTORDER_PROC, op_lefttype, op_righttype, opfamily);
+                       /* We'll use a shim to call the old-style btree comparator */
+                       PrepareSortSupportComparisonShim(sortfunc, &clause->ssup);
+               }
 
                iClause++;
        }
@@ -241,19 +266,34 @@ MJExamineQuals(List *mergeclauses,
  * Compute the values of the mergejoined expressions for the current
  * outer tuple.  We also detect whether it's impossible for the current
  * outer tuple to match anything --- this is true if it yields a NULL
- * input, since we assume mergejoin operators are strict.
+ * input, since we assume mergejoin operators are strict.  If the NULL
+ * is in the first join column, and that column sorts nulls last, then
+ * we can further conclude that no following tuple can match anything
+ * either, since they must all have nulls in the first column. However,
+ * that case is only interesting if we're not in FillOuter mode, else
+ * we have to visit all the tuples anyway.
+ *
+ * For the convenience of callers, we also make this routine responsible
+ * for testing for end-of-input (null outer tuple), and returning
+ * MJEVAL_ENDOFJOIN when that's seen.  This allows the same code to be used
+ * for both real end-of-input and the effective end-of-input represented by
+ * a first-column NULL.
  *
  * We evaluate the values in OuterEContext, which can be reset each
  * time we move to a new tuple.
  */
-static bool
+static MJEvalResult
 MJEvalOuterValues(MergeJoinState *mergestate)
 {
        ExprContext *econtext = mergestate->mj_OuterEContext;
-       bool            canmatch = true;
+       MJEvalResult result = MJEVAL_MATCHABLE;
        int                     i;
        MemoryContext oldContext;
 
+       /* Check for end of outer subplan */
+       if (TupIsNull(mergestate->mj_OuterTupleSlot))
+               return MJEVAL_ENDOFJOIN;
+
        ResetExprContext(econtext);
 
        oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
@@ -267,12 +307,19 @@ MJEvalOuterValues(MergeJoinState *mergestate)
                clause->ldatum = ExecEvalExpr(clause->lexpr, econtext,
                                                                          &clause->lisnull, NULL);
                if (clause->lisnull)
-                       canmatch = false;
+               {
+                       /* match is impossible; can we end the join early? */
+                       if (i == 0 && !clause->ssup.ssup_nulls_first &&
+                               !mergestate->mj_FillOuter)
+                               result = MJEVAL_ENDOFJOIN;
+                       else if (result == MJEVAL_MATCHABLE)
+                               result = MJEVAL_NONMATCHABLE;
+               }
        }
 
        MemoryContextSwitchTo(oldContext);
 
-       return canmatch;
+       return result;
 }
 
 /*
@@ -282,14 +329,18 @@ MJEvalOuterValues(MergeJoinState *mergestate)
  * to load data from either the true current inner, or the marked inner,
  * so caller must tell us which slot to load from.
  */
-static bool
+static MJEvalResult
 MJEvalInnerValues(MergeJoinState *mergestate, TupleTableSlot *innerslot)
 {
        ExprContext *econtext = mergestate->mj_InnerEContext;
-       bool            canmatch = true;
+       MJEvalResult result = MJEVAL_MATCHABLE;
        int                     i;
        MemoryContext oldContext;
 
+       /* Check for end of inner subplan */
+       if (TupIsNull(innerslot))
+               return MJEVAL_ENDOFJOIN;
+
        ResetExprContext(econtext);
 
        oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
@@ -303,12 +354,19 @@ MJEvalInnerValues(MergeJoinState *mergestate, TupleTableSlot *innerslot)
                clause->rdatum = ExecEvalExpr(clause->rexpr, econtext,
                                                                          &clause->risnull, NULL);
                if (clause->risnull)
-                       canmatch = false;
+               {
+                       /* match is impossible; can we end the join early? */
+                       if (i == 0 && !clause->ssup.ssup_nulls_first &&
+                               !mergestate->mj_FillInner)
+                               result = MJEVAL_ENDOFJOIN;
+                       else if (result == MJEVAL_MATCHABLE)
+                               result = MJEVAL_NONMATCHABLE;
+               }
        }
 
        MemoryContextSwitchTo(oldContext);
 
-       return canmatch;
+       return result;
 }
 
 /*
@@ -316,20 +374,19 @@ MJEvalInnerValues(MergeJoinState *mergestate, TupleTableSlot *innerslot)
  *
  * Compare the mergejoinable values of the current two input tuples
  * and return 0 if they are equal (ie, the mergejoin equalities all
- * succeed), +1 if outer > inner, -1 if outer < inner.
+ * succeed), >0 if outer > inner, <0 if outer < inner.
  *
  * MJEvalOuterValues and MJEvalInnerValues must already have been called
  * for the current outer and inner tuples, respectively.
  */
-static int32
+static int
 MJCompare(MergeJoinState *mergestate)
 {
-       int32           result = 0;
+       int                     result = 0;
        bool            nulleqnull = false;
        ExprContext *econtext = mergestate->js.ps.ps_ExprContext;
        int                     i;
        MemoryContext oldContext;
-       FunctionCallInfoData fcinfo;
 
        /*
         * Call the comparison functions in short-lived context, in case they leak
@@ -342,64 +399,35 @@ MJCompare(MergeJoinState *mergestate)
        for (i = 0; i < mergestate->mj_NumClauses; i++)
        {
                MergeJoinClause clause = &mergestate->mj_Clauses[i];
-               Datum           fresult;
 
                /*
-                * Deal with null inputs.
+                * Special case for NULL-vs-NULL, else use standard comparison.
                 */
-               if (clause->lisnull)
-               {
-                       if (clause->risnull)
-                       {
-                               nulleqnull = true;              /* NULL "=" NULL */
-                               continue;
-                       }
-                       if (clause->nulls_first)
-                               result = -1;    /* NULL "<" NOT_NULL */
-                       else
-                               result = 1;             /* NULL ">" NOT_NULL */
-                       break;
-               }
-               if (clause->risnull)
+               if (clause->lisnull && clause->risnull)
                {
-                       if (clause->nulls_first)
-                               result = 1;             /* NOT_NULL ">" NULL */
-                       else
-                               result = -1;    /* NOT_NULL "<" NULL */
-                       break;
-               }
-
-               /*
-                * OK to call the comparison function.
-                */
-               InitFunctionCallInfoData(fcinfo, &(clause->cmpfinfo), 2,
-                                                                NULL, NULL);
-               fcinfo.arg[0] = clause->ldatum;
-               fcinfo.arg[1] = clause->rdatum;
-               fcinfo.argnull[0] = false;
-               fcinfo.argnull[1] = false;
-               fresult = FunctionCallInvoke(&fcinfo);
-               if (fcinfo.isnull)
-               {
-                       nulleqnull = true;      /* treat like NULL = NULL */
+                       nulleqnull = true;              /* NULL "=" NULL */
                        continue;
                }
-               result = DatumGetInt32(fresult);
 
-               if (clause->reverse)
-                       result = -result;
+               result = ApplySortComparator(clause->ldatum, clause->lisnull,
+                                                                        clause->rdatum, clause->risnull,
+                                                                        &clause->ssup);
 
                if (result != 0)
                        break;
        }
 
        /*
-        * If we had any null comparison results or NULL-vs-NULL inputs, we do not
-        * 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.
+        * If we had any NULL-vs-NULL inputs, we do not 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);
@@ -443,6 +471,8 @@ MJFillOuter(MergeJoinState *node)
                        return result;
                }
        }
+       else
+               InstrCountFiltered2(node, 1);
 
        return NULL;
 }
@@ -482,11 +512,39 @@ MJFillInner(MergeJoinState *node)
                        return result;
                }
        }
+       else
+               InstrCountFiltered2(node, 1);
 
        return NULL;
 }
 
 
+/*
+ * 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
  *
@@ -552,11 +610,10 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-       EState     *estate;
        List       *joinqual;
        List       *otherqual;
        bool            qualResult;
-       int32           compareResult;
+       int                     compareResult;
        PlanState  *innerPlan;
        TupleTableSlot *innerTupleSlot;
        PlanState  *outerPlan;
@@ -568,7 +625,6 @@ ExecMergeJoin(MergeJoinState *node)
        /*
         * get information from node
         */
-       estate = node->js.ps.state;
        innerPlan = innerPlanState(node);
        outerPlan = outerPlanState(node);
        econtext = node->js.ps.ps_ExprContext;
@@ -625,46 +681,46 @@ ExecMergeJoin(MergeJoinState *node)
 
                                outerTupleSlot = ExecProcNode(outerPlan);
                                node->mj_OuterTupleSlot = outerTupleSlot;
-                               if (TupIsNull(outerTupleSlot))
-                               {
-                                       MJ_printf("ExecMergeJoin: nothing in outer subplan\n");
-                                       if (doFillInner)
-                                       {
-                                               /*
-                                                * Need to emit right-join tuples for remaining inner
-                                                * tuples.      We set MatchedInner = true to force the
-                                                * ENDOUTER state to advance inner.
-                                                */
-                                               node->mj_JoinState = EXEC_MJ_ENDOUTER;
-                                               node->mj_MatchedInner = true;
-                                               break;
-                                       }
-                                       /* Otherwise we're done. */
-                                       return NULL;
-                               }
 
                                /* Compute join values and check for unmatchability */
-                               if (MJEvalOuterValues(node))
+                               switch (MJEvalOuterValues(node))
                                {
-                                       /* OK to go get the first inner tuple */
-                                       node->mj_JoinState = EXEC_MJ_INITIALIZE_INNER;
-                               }
-                               else
-                               {
-                                       /* Stay in same state to fetch next outer tuple */
-                                       if (doFillOuter)
-                                       {
-                                               /*
-                                                * Generate a fake join tuple with nulls for the inner
-                                                * tuple, and return it if it passes the non-join
-                                                * quals.
-                                                */
-                                               TupleTableSlot *result;
+                                       case MJEVAL_MATCHABLE:
+                                               /* OK to go get the first inner tuple */
+                                               node->mj_JoinState = EXEC_MJ_INITIALIZE_INNER;
+                                               break;
+                                       case MJEVAL_NONMATCHABLE:
+                                               /* Stay in same state to fetch next outer tuple */
+                                               if (doFillOuter)
+                                               {
+                                                       /*
+                                                        * Generate a fake join tuple with nulls for the
+                                                        * inner tuple, and return it if it passes the
+                                                        * non-join quals.
+                                                        */
+                                                       TupleTableSlot *result;
 
-                                               result = MJFillOuter(node);
-                                               if (result)
-                                                       return result;
-                                       }
+                                                       result = MJFillOuter(node);
+                                                       if (result)
+                                                               return result;
+                                               }
+                                               break;
+                                       case MJEVAL_ENDOFJOIN:
+                                               /* No more outer tuples */
+                                               MJ_printf("ExecMergeJoin: nothing in outer subplan\n");
+                                               if (doFillInner)
+                                               {
+                                                       /*
+                                                        * Need to emit right-join tuples for remaining
+                                                        * inner tuples. We set MatchedInner = true to
+                                                        * force the ENDOUTER state to advance inner.
+                                                        */
+                                                       node->mj_JoinState = EXEC_MJ_ENDOUTER;
+                                                       node->mj_MatchedInner = true;
+                                                       break;
+                                               }
+                                               /* Otherwise we're done. */
+                                               return NULL;
                                }
                                break;
 
@@ -673,53 +729,55 @@ ExecMergeJoin(MergeJoinState *node)
 
                                innerTupleSlot = ExecProcNode(innerPlan);
                                node->mj_InnerTupleSlot = innerTupleSlot;
-                               if (TupIsNull(innerTupleSlot))
-                               {
-                                       MJ_printf("ExecMergeJoin: nothing in inner subplan\n");
-                                       if (doFillOuter)
-                                       {
-                                               /*
-                                                * Need to emit left-join tuples for all outer tuples,
-                                                * including the one we just fetched.  We set
-                                                * MatchedOuter = false to force the ENDINNER state to
-                                                * emit first tuple before advancing outer.
-                                                */
-                                               node->mj_JoinState = EXEC_MJ_ENDINNER;
-                                               node->mj_MatchedOuter = false;
-                                               break;
-                                       }
-                                       /* Otherwise we're done. */
-                                       return NULL;
-                               }
 
                                /* Compute join values and check for unmatchability */
-                               if (MJEvalInnerValues(node, innerTupleSlot))
-                               {
-                                       /*
-                                        * OK, we have the initial tuples.      Begin by skipping
-                                        * non-matching tuples.
-                                        */
-                                       node->mj_JoinState = EXEC_MJ_SKIP_TEST;
-                               }
-                               else
+                               switch (MJEvalInnerValues(node, innerTupleSlot))
                                {
-                                       /* Mark before advancing, if wanted */
-                                       if (node->mj_ExtraMarks)
-                                               ExecMarkPos(innerPlan);
-                                       /* Stay in same state to fetch next inner tuple */
-                                       if (doFillInner)
-                                       {
+                                       case MJEVAL_MATCHABLE:
+
                                                /*
-                                                * Generate a fake join tuple with nulls for the outer
-                                                * tuple, and return it if it passes the non-join
-                                                * quals.
+                                                * OK, we have the initial tuples.      Begin by skipping
+                                                * non-matching tuples.
                                                 */
-                                               TupleTableSlot *result;
+                                               node->mj_JoinState = EXEC_MJ_SKIP_TEST;
+                                               break;
+                                       case MJEVAL_NONMATCHABLE:
+                                               /* Mark before advancing, if wanted */
+                                               if (node->mj_ExtraMarks)
+                                                       ExecMarkPos(innerPlan);
+                                               /* Stay in same state to fetch next inner tuple */
+                                               if (doFillInner)
+                                               {
+                                                       /*
+                                                        * Generate a fake join tuple with nulls for the
+                                                        * outer tuple, and return it if it passes the
+                                                        * non-join quals.
+                                                        */
+                                                       TupleTableSlot *result;
 
-                                               result = MJFillInner(node);
-                                               if (result)
-                                                       return result;
-                                       }
+                                                       result = MJFillInner(node);
+                                                       if (result)
+                                                               return result;
+                                               }
+                                               break;
+                                       case MJEVAL_ENDOFJOIN:
+                                               /* No more inner tuples */
+                                               MJ_printf("ExecMergeJoin: nothing in inner subplan\n");
+                                               if (doFillOuter)
+                                               {
+                                                       /*
+                                                        * Need to emit left-join tuples for all outer
+                                                        * tuples, including the one we just fetched.  We
+                                                        * set MatchedOuter = false to force the ENDINNER
+                                                        * state to emit first tuple before advancing
+                                                        * outer.
+                                                        */
+                                                       node->mj_JoinState = EXEC_MJ_ENDINNER;
+                                                       node->mj_MatchedOuter = false;
+                                                       break;
+                                               }
+                                               /* Otherwise we're done. */
+                                               return NULL;
                                }
                                break;
 
@@ -774,8 +832,8 @@ ExecMergeJoin(MergeJoinState *node)
                                        }
 
                                        /*
-                                        * In a semijoin, we'll consider returning the first match,
-                                        * but after that we're done with this outer tuple.
+                                        * In a semijoin, we'll consider returning the first
+                                        * match, but after that we're done with this outer tuple.
                                         */
                                        if (node->js.jointype == JOIN_SEMI)
                                                node->mj_JoinState = EXEC_MJ_NEXTOUTER;
@@ -805,7 +863,11 @@ ExecMergeJoin(MergeJoinState *node)
                                                        return result;
                                                }
                                        }
+                                       else
+                                               InstrCountFiltered2(node, 1);
                                }
+                               else
+                                       InstrCountFiltered1(node, 1);
                                break;
 
                                /*
@@ -847,41 +909,54 @@ ExecMergeJoin(MergeJoinState *node)
                                MJ_DEBUG_PROC_NODE(innerTupleSlot);
                                node->mj_MatchedInner = false;
 
-                               if (TupIsNull(innerTupleSlot))
+                               /* Compute join values and check for unmatchability */
+                               switch (MJEvalInnerValues(node, innerTupleSlot))
                                {
-                                       node->mj_JoinState = EXEC_MJ_NEXTOUTER;
-                                       break;
-                               }
+                                       case MJEVAL_MATCHABLE:
 
-                               /*
-                                * 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))
-                               {
-                                       node->mj_JoinState = EXEC_MJ_NEXTOUTER;
-                                       break;
-                               }
+                                               /*
+                                                * Test the new inner tuple to see if it matches
+                                                * outer.
+                                                *
+                                                * If they do match, then we join them and move on to
+                                                * the next inner tuple (EXEC_MJ_JOINTUPLES).
+                                                *
+                                                * If they do not match then advance to next outer
+                                                * tuple.
+                                                */
+                                               compareResult = MJCompare(node);
+                                               MJ_DEBUG_COMPARE(compareResult);
 
-                               /*
-                                * Test the new inner tuple to see if it matches outer.
-                                *
-                                * If they do match, then we join them and move on to the next
-                                * inner tuple (EXEC_MJ_JOINTUPLES).
-                                *
-                                * If they do not match then advance to next outer tuple.
-                                */
-                               compareResult = MJCompare(node);
-                               MJ_DEBUG_COMPARE(compareResult);
+                                               if (compareResult == 0)
+                                                       node->mj_JoinState = EXEC_MJ_JOINTUPLES;
+                                               else
+                                               {
+                                                       Assert(compareResult < 0);
+                                                       node->mj_JoinState = EXEC_MJ_NEXTOUTER;
+                                               }
+                                               break;
+                                       case MJEVAL_NONMATCHABLE:
 
-                               if (compareResult == 0)
-                                       node->mj_JoinState = EXEC_MJ_JOINTUPLES;
-                               else
-                               {
-                                       Assert(compareResult < 0);
-                                       node->mj_JoinState = EXEC_MJ_NEXTOUTER;
+                                               /*
+                                                * It contains a NULL and hence can't match any outer
+                                                * tuple, so we can skip the comparison and assume the
+                                                * new tuple is greater than current outer.
+                                                */
+                                               node->mj_JoinState = EXEC_MJ_NEXTOUTER;
+                                               break;
+                                       case MJEVAL_ENDOFJOIN:
+
+                                               /*
+                                                * No more inner tuples.  However, this might be only
+                                                * effective and not physical end of inner plan, so
+                                                * force mj_InnerTupleSlot to null to make sure we
+                                                * don't fetch more inner tuples.  (We need this hack
+                                                * because we are not transiting to a state where the
+                                                * inner plan is assumed to be exhausted.)
+                                                */
+                                               node->mj_InnerTupleSlot = NULL;
+                                               node->mj_JoinState = EXEC_MJ_NEXTOUTER;
+                                               break;
                                }
                                break;
 
@@ -931,37 +1006,32 @@ ExecMergeJoin(MergeJoinState *node)
                                MJ_DEBUG_PROC_NODE(outerTupleSlot);
                                node->mj_MatchedOuter = false;
 
-                               /*
-                                * if the outer tuple is null then we are done with the join,
-                                * unless we have inner tuples we need to null-fill.
-                                */
-                               if (TupIsNull(outerTupleSlot))
-                               {
-                                       MJ_printf("ExecMergeJoin: end of outer subplan\n");
-                                       innerTupleSlot = node->mj_InnerTupleSlot;
-                                       if (doFillInner && !TupIsNull(innerTupleSlot))
-                                       {
-                                               /*
-                                                * Need to emit right-join tuples for remaining inner
-                                                * tuples.
-                                                */
-                                               node->mj_JoinState = EXEC_MJ_ENDOUTER;
-                                               break;
-                                       }
-                                       /* Otherwise we're done. */
-                                       return NULL;
-                               }
-
                                /* Compute join values and check for unmatchability */
-                               if (MJEvalOuterValues(node))
-                               {
-                                       /* Go test the new tuple against the marked tuple */
-                                       node->mj_JoinState = EXEC_MJ_TESTOUTER;
-                               }
-                               else
+                               switch (MJEvalOuterValues(node))
                                {
-                                       /* Can't match, so fetch next outer tuple */
-                                       node->mj_JoinState = EXEC_MJ_NEXTOUTER;
+                                       case MJEVAL_MATCHABLE:
+                                               /* Go test the new tuple against the marked tuple */
+                                               node->mj_JoinState = EXEC_MJ_TESTOUTER;
+                                               break;
+                                       case MJEVAL_NONMATCHABLE:
+                                               /* Can't match, so fetch next outer tuple */
+                                               node->mj_JoinState = EXEC_MJ_NEXTOUTER;
+                                               break;
+                                       case MJEVAL_ENDOFJOIN:
+                                               /* No more outer tuples */
+                                               MJ_printf("ExecMergeJoin: end of outer subplan\n");
+                                               innerTupleSlot = node->mj_InnerTupleSlot;
+                                               if (doFillInner && !TupIsNull(innerTupleSlot))
+                                               {
+                                                       /*
+                                                        * Need to emit right-join tuples for remaining
+                                                        * inner tuples.
+                                                        */
+                                                       node->mj_JoinState = EXEC_MJ_ENDOUTER;
+                                                       break;
+                                               }
+                                               /* Otherwise we're done. */
+                                               return NULL;
                                }
                                break;
 
@@ -1025,9 +1095,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);
 
@@ -1058,39 +1132,41 @@ ExecMergeJoin(MergeJoinState *node)
                                         *      larger than our marked inner tuples.  So we need not
                                         *      revisit any of the marked tuples but can proceed to
                                         *      look for a match to the current inner.  If there's
-                                        *      no more inners, we are done.
+                                        *      no more inners, no more matches are possible.
                                         * ----------------
                                         */
                                        Assert(compareResult > 0);
                                        innerTupleSlot = node->mj_InnerTupleSlot;
-                                       if (TupIsNull(innerTupleSlot))
+
+                                       /* reload comparison data for current inner */
+                                       switch (MJEvalInnerValues(node, innerTupleSlot))
                                        {
-                                               if (doFillOuter)
-                                               {
+                                               case MJEVAL_MATCHABLE:
+                                                       /* proceed to compare it to the current outer */
+                                                       node->mj_JoinState = EXEC_MJ_SKIP_TEST;
+                                                       break;
+                                               case MJEVAL_NONMATCHABLE:
+
                                                        /*
-                                                        * Need to emit left-join tuples for remaining
-                                                        * outer tuples.
+                                                        * current inner can't possibly match any outer;
+                                                        * better to advance the inner scan than the
+                                                        * outer.
                                                         */
-                                                       node->mj_JoinState = EXEC_MJ_ENDINNER;
+                                                       node->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE;
                                                        break;
-                                               }
-                                               /* Otherwise we're done. */
-                                               return NULL;
-                                       }
-
-                                       /* reload comparison data for current inner */
-                                       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;
+                                               case MJEVAL_ENDOFJOIN:
+                                                       /* No more inner tuples */
+                                                       if (doFillOuter)
+                                                       {
+                                                               /*
+                                                                * Need to emit left-join tuples for remaining
+                                                                * outer tuples.
+                                                                */
+                                                               node->mj_JoinState = EXEC_MJ_ENDINNER;
+                                                               break;
+                                                       }
+                                                       /* Otherwise we're done. */
+                                                       return NULL;
                                        }
                                }
                                break;
@@ -1183,37 +1259,32 @@ ExecMergeJoin(MergeJoinState *node)
                                MJ_DEBUG_PROC_NODE(outerTupleSlot);
                                node->mj_MatchedOuter = false;
 
-                               /*
-                                * if the outer tuple is null then we are done with the join,
-                                * unless we have inner tuples we need to null-fill.
-                                */
-                               if (TupIsNull(outerTupleSlot))
-                               {
-                                       MJ_printf("ExecMergeJoin: end of outer subplan\n");
-                                       innerTupleSlot = node->mj_InnerTupleSlot;
-                                       if (doFillInner && !TupIsNull(innerTupleSlot))
-                                       {
-                                               /*
-                                                * Need to emit right-join tuples for remaining inner
-                                                * tuples.
-                                                */
-                                               node->mj_JoinState = EXEC_MJ_ENDOUTER;
-                                               break;
-                                       }
-                                       /* Otherwise we're done. */
-                                       return NULL;
-                               }
-
                                /* Compute join values and check for unmatchability */
-                               if (MJEvalOuterValues(node))
+                               switch (MJEvalOuterValues(node))
                                {
-                                       /* 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;
+                                       case MJEVAL_MATCHABLE:
+                                               /* Go test the new tuple against the current inner */
+                                               node->mj_JoinState = EXEC_MJ_SKIP_TEST;
+                                               break;
+                                       case MJEVAL_NONMATCHABLE:
+                                               /* Can't match, so fetch next outer tuple */
+                                               node->mj_JoinState = EXEC_MJ_SKIPOUTER_ADVANCE;
+                                               break;
+                                       case MJEVAL_ENDOFJOIN:
+                                               /* No more outer tuples */
+                                               MJ_printf("ExecMergeJoin: end of outer subplan\n");
+                                               innerTupleSlot = node->mj_InnerTupleSlot;
+                                               if (doFillInner && !TupIsNull(innerTupleSlot))
+                                               {
+                                                       /*
+                                                        * Need to emit right-join tuples for remaining
+                                                        * inner tuples.
+                                                        */
+                                                       node->mj_JoinState = EXEC_MJ_ENDOUTER;
+                                                       break;
+                                               }
+                                               /* Otherwise we're done. */
+                                               return NULL;
                                }
                                break;
 
@@ -1254,40 +1325,36 @@ ExecMergeJoin(MergeJoinState *node)
                                MJ_DEBUG_PROC_NODE(innerTupleSlot);
                                node->mj_MatchedInner = false;
 
-                               /*
-                                * if the inner tuple is null then we are done with the join,
-                                * unless we have outer tuples we need to null-fill.
-                                */
-                               if (TupIsNull(innerTupleSlot))
+                               /* Compute join values and check for unmatchability */
+                               switch (MJEvalInnerValues(node, innerTupleSlot))
                                {
-                                       MJ_printf("ExecMergeJoin: end of inner subplan\n");
-                                       outerTupleSlot = node->mj_OuterTupleSlot;
-                                       if (doFillOuter && !TupIsNull(outerTupleSlot))
-                                       {
+                                       case MJEVAL_MATCHABLE:
+                                               /* proceed to compare it to the current outer */
+                                               node->mj_JoinState = EXEC_MJ_SKIP_TEST;
+                                               break;
+                                       case MJEVAL_NONMATCHABLE:
+
                                                /*
-                                                * Need to emit left-join tuples for remaining outer
-                                                * tuples.
+                                                * current inner can't possibly match any outer;
+                                                * better to advance the inner scan than the outer.
                                                 */
-                                               node->mj_JoinState = EXEC_MJ_ENDINNER;
+                                               node->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE;
                                                break;
-                                       }
-                                       /* Otherwise we're done. */
-                                       return NULL;
-                               }
-
-                               /* Compute join values and check for unmatchability */
-                               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;
+                                       case MJEVAL_ENDOFJOIN:
+                                               /* No more inner tuples */
+                                               MJ_printf("ExecMergeJoin: end of inner subplan\n");
+                                               outerTupleSlot = node->mj_OuterTupleSlot;
+                                               if (doFillOuter && !TupIsNull(outerTupleSlot))
+                                               {
+                                                       /*
+                                                        * Need to emit left-join tuples for remaining
+                                                        * outer tuples.
+                                                        */
+                                                       node->mj_JoinState = EXEC_MJ_ENDINNER;
+                                                       break;
+                                               }
+                                               /* Otherwise we're done. */
+                                               return NULL;
                                }
                                break;
 
@@ -1439,6 +1506,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 */
 
        /*
@@ -1466,8 +1534,6 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
        else
                mergestate->mj_ExtraMarks = false;
 
-#define MERGEJOIN_NSLOTS 4
-
        /*
         * tuple table initialization
         */
@@ -1500,10 +1566,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 +1586,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")));
@@ -1543,6 +1612,7 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
        mergestate->mj_NumClauses = list_length(node->mergeclauses);
        mergestate->mj_Clauses = MJExamineQuals(node->mergeclauses,
                                                                                        node->mergeFamilies,
+                                                                                       node->mergeCollations,
                                                                                        node->mergeStrategies,
                                                                                        node->mergeNullsFirst,
                                                                                        (PlanState *) mergestate);
@@ -1566,14 +1636,6 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
        return mergestate;
 }
 
-int
-ExecCountSlotsMergeJoin(MergeJoin *node)
-{
-       return ExecCountSlotsNode(outerPlan((Plan *) node)) +
-               ExecCountSlotsNode(innerPlan((Plan *) node)) +
-               MERGEJOIN_NSLOTS;
-}
-
 /* ----------------------------------------------------------------
  *             ExecEndMergeJoin
  *
@@ -1609,7 +1671,7 @@ ExecEndMergeJoin(MergeJoinState *node)
 }
 
 void
-ExecReScanMergeJoin(MergeJoinState *node, ExprContext *exprCtxt)
+ExecReScanMergeJoin(MergeJoinState *node)
 {
        ExecClearTuple(node->mj_MarkedTupleSlot);
 
@@ -1624,9 +1686,9 @@ ExecReScanMergeJoin(MergeJoinState *node, ExprContext *exprCtxt)
         * if chgParam of subnodes is not null then plans will be re-scanned by
         * first ExecProcNode.
         */
-       if (((PlanState *) node)->lefttree->chgParam == NULL)
-               ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
-       if (((PlanState *) node)->righttree->chgParam == NULL)
-               ExecReScan(((PlanState *) node)->righttree, exprCtxt);
+       if (node->js.ps.lefttree->chgParam == NULL)
+               ExecReScan(node->js.ps.lefttree);
+       if (node->js.ps.righttree->chgParam == NULL)
+               ExecReScan(node->js.ps.righttree);
 
 }