Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Defines | Typedefs | Enumerations | Functions

nodeMergejoin.c File Reference

#include "postgres.h"
#include "access/nbtree.h"
#include "executor/execdebug.h"
#include "executor/nodeMergejoin.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
Include dependency graph for nodeMergejoin.c:

Go to the source code of this file.

Data Structures

struct  MergeJoinClauseData

Defines

#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
#define MarkInnerTuple(innerTupleSlot, mergestate)   ExecCopySlot((mergestate)->mj_MarkedTupleSlot, (innerTupleSlot))

Typedefs

typedef struct MergeJoinClauseData MergeJoinClauseData

Enumerations

enum  MJEvalResult { MJEVAL_MATCHABLE, MJEVAL_NONMATCHABLE, MJEVAL_ENDOFJOIN }

Functions

static MergeJoinClause MJExamineQuals (List *mergeclauses, Oid *mergefamilies, Oid *mergecollations, int *mergestrategies, bool *mergenullsfirst, PlanState *parent)
static MJEvalResult MJEvalOuterValues (MergeJoinState *mergestate)
static MJEvalResult MJEvalInnerValues (MergeJoinState *mergestate, TupleTableSlot *innerslot)
static int MJCompare (MergeJoinState *mergestate)
static TupleTableSlotMJFillOuter (MergeJoinState *node)
static TupleTableSlotMJFillInner (MergeJoinState *node)
static bool check_constant_qual (List *qual, bool *is_const_false)
TupleTableSlotExecMergeJoin (MergeJoinState *node)
MergeJoinStateExecInitMergeJoin (MergeJoin *node, EState *estate, int eflags)
void ExecEndMergeJoin (MergeJoinState *node)
void ExecReScanMergeJoin (MergeJoinState *node)

Define Documentation

#define EXEC_MJ_ENDINNER   11

Definition at line 115 of file nodeMergejoin.c.

Referenced by ExecMergeJoin().

#define EXEC_MJ_ENDOUTER   10

Definition at line 114 of file nodeMergejoin.c.

Referenced by ExecMergeJoin().

#define EXEC_MJ_INITIALIZE_INNER   2

Definition at line 106 of file nodeMergejoin.c.

Referenced by ExecMergeJoin().

#define EXEC_MJ_INITIALIZE_OUTER   1

Definition at line 105 of file nodeMergejoin.c.

Referenced by ExecMergeJoin().

#define EXEC_MJ_JOINTUPLES   3

Definition at line 107 of file nodeMergejoin.c.

Referenced by ExecMergeJoin().

#define EXEC_MJ_NEXTINNER   6

Definition at line 110 of file nodeMergejoin.c.

Referenced by ExecMergeJoin().

#define EXEC_MJ_NEXTOUTER   4

Definition at line 108 of file nodeMergejoin.c.

Referenced by ExecMergeJoin().

#define EXEC_MJ_SKIP_TEST   7

Definition at line 111 of file nodeMergejoin.c.

Referenced by ExecMergeJoin().

#define EXEC_MJ_SKIPINNER_ADVANCE   9

Definition at line 113 of file nodeMergejoin.c.

Referenced by ExecMergeJoin().

#define EXEC_MJ_SKIPOUTER_ADVANCE   8

Definition at line 112 of file nodeMergejoin.c.

Referenced by ExecMergeJoin().

#define EXEC_MJ_TESTOUTER   5

Definition at line 109 of file nodeMergejoin.c.

Referenced by ExecMergeJoin().

#define MarkInnerTuple (   innerTupleSlot,
  mergestate 
)    ExecCopySlot((mergestate)->mj_MarkedTupleSlot, (innerTupleSlot))

Definition at line 151 of file nodeMergejoin.c.

Referenced by ExecMergeJoin().


Typedef Documentation


Enumeration Type Documentation

Enumerator:
MJEVAL_MATCHABLE 
MJEVAL_NONMATCHABLE 
MJEVAL_ENDOFJOIN 

Definition at line 143 of file nodeMergejoin.c.

{
    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;


Function Documentation

static bool check_constant_qual ( List qual,
bool is_const_false 
) [static]

Definition at line 531 of file nodeMergejoin.c.

References Const::constisnull, Const::constvalue, DatumGetBool, IsA, and lfirst.

Referenced by ExecInitMergeJoin().

{
    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;
}

void ExecEndMergeJoin ( MergeJoinState node  ) 

Definition at line 1647 of file nodeMergejoin.c.

References ExecClearTuple(), ExecEndNode(), ExecFreeExprContext(), innerPlanState, MergeJoinState::js, MJ1_printf, MergeJoinState::mj_MarkedTupleSlot, outerPlanState, JoinState::ps, and PlanState::ps_ResultTupleSlot.

Referenced by ExecEndNode().

{
    MJ1_printf("ExecEndMergeJoin: %s\n",
               "ending node processing");

    /*
     * Free the exprcontext
     */
    ExecFreeExprContext(&node->js.ps);

    /*
     * clean out the tuple table
     */
    ExecClearTuple(node->js.ps.ps_ResultTupleSlot);
    ExecClearTuple(node->mj_MarkedTupleSlot);

    /*
     * shut down the subplans
     */
    ExecEndNode(innerPlanState(node));
    ExecEndNode(outerPlanState(node));

    MJ1_printf("ExecEndMergeJoin: %s\n",
               "node processing ended");
}

MergeJoinState* ExecInitMergeJoin ( MergeJoin node,
EState estate,
int  eflags 
)

Definition at line 1464 of file nodeMergejoin.c.

References Assert, check_constant_qual(), CreateExprContext(), elog, ereport, errcode(), errmsg(), ERROR, EXEC_FLAG_BACKWARD, EXEC_FLAG_MARK, EXEC_FLAG_REWIND, ExecAssignExprContext(), ExecAssignProjectionInfo(), ExecAssignResultTypeFromTL(), ExecGetResultType(), ExecInitExpr(), ExecInitExtraTupleSlot(), ExecInitNode(), ExecInitNullTupleSlot(), ExecInitResultTupleSlot(), ExecSetSlotDescriptor(), innerPlan, innerPlanState, IsA, MergeJoin::join, JOIN_ANTI, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_RIGHT, JOIN_SEMI, Join::joinqual, JoinState::joinqual, Join::jointype, JoinState::jointype, MergeJoinState::js, list_length(), makeNode, MergeJoin::mergeclauses, MergeJoin::mergeCollations, MergeJoin::mergeFamilies, MergeJoin::mergeNullsFirst, MergeJoin::mergeStrategies, MJ1_printf, MergeJoinState::mj_Clauses, MergeJoinState::mj_ConstFalseJoin, MergeJoinState::mj_ExtraMarks, MergeJoinState::mj_FillInner, MergeJoinState::mj_FillOuter, MergeJoinState::mj_InnerEContext, MergeJoinState::mj_MarkedTupleSlot, MergeJoinState::mj_NullInnerTupleSlot, MergeJoinState::mj_NullOuterTupleSlot, MergeJoinState::mj_NumClauses, MergeJoinState::mj_OuterEContext, MJExamineQuals(), NULL, outerPlan, outerPlanState, Join::plan, PlanState::plan, JoinState::ps, PlanState::ps_TupFromTlist, Plan::qual, PlanState::qual, PlanState::state, Plan::targetlist, and PlanState::targetlist.

Referenced by ExecInitNode().

{
    MergeJoinState *mergestate;

    /* check for unsupported flags */
    Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));

    MJ1_printf("ExecInitMergeJoin: %s\n",
               "initializing node");

    /*
     * create state structure
     */
    mergestate = makeNode(MergeJoinState);
    mergestate->js.ps.plan = (Plan *) node;
    mergestate->js.ps.state = estate;

    /*
     * Miscellaneous initialization
     *
     * create expression context for node
     */
    ExecAssignExprContext(estate, &mergestate->js.ps);

    /*
     * we need two additional econtexts in which we can compute the join
     * expressions from the left and right input tuples.  The node's regular
     * econtext won't do because it gets reset too often.
     */
    mergestate->mj_OuterEContext = CreateExprContext(estate);
    mergestate->mj_InnerEContext = CreateExprContext(estate);

    /*
     * initialize child expressions
     */
    mergestate->js.ps.targetlist = (List *)
        ExecInitExpr((Expr *) node->join.plan.targetlist,
                     (PlanState *) mergestate);
    mergestate->js.ps.qual = (List *)
        ExecInitExpr((Expr *) node->join.plan.qual,
                     (PlanState *) mergestate);
    mergestate->js.jointype = node->join.jointype;
    mergestate->js.joinqual = (List *)
        ExecInitExpr((Expr *) node->join.joinqual,
                     (PlanState *) mergestate);
    mergestate->mj_ConstFalseJoin = false;
    /* mergeclauses are handled below */

    /*
     * initialize child nodes
     *
     * inner child must support MARK/RESTORE.
     */
    outerPlanState(mergestate) = ExecInitNode(outerPlan(node), estate, eflags);
    innerPlanState(mergestate) = ExecInitNode(innerPlan(node), estate,
                                              eflags | EXEC_FLAG_MARK);

    /*
     * For certain types of inner child nodes, it is advantageous to issue
     * MARK every time we advance past an inner tuple we will never return to.
     * For other types, MARK on a tuple we cannot return to is a waste of
     * cycles.  Detect which case applies and set mj_ExtraMarks if we want to
     * issue "unnecessary" MARK calls.
     *
     * Currently, only Material wants the extra MARKs, and it will be helpful
     * only if eflags doesn't specify REWIND.
     */
    if (IsA(innerPlan(node), Material) &&
        (eflags & EXEC_FLAG_REWIND) == 0)
        mergestate->mj_ExtraMarks = true;
    else
        mergestate->mj_ExtraMarks = false;

    /*
     * tuple table initialization
     */
    ExecInitResultTupleSlot(estate, &mergestate->js.ps);

    mergestate->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate);
    ExecSetSlotDescriptor(mergestate->mj_MarkedTupleSlot,
                          ExecGetResultType(innerPlanState(mergestate)));

    switch (node->join.jointype)
    {
        case JOIN_INNER:
        case JOIN_SEMI:
            mergestate->mj_FillOuter = false;
            mergestate->mj_FillInner = false;
            break;
        case JOIN_LEFT:
        case JOIN_ANTI:
            mergestate->mj_FillOuter = true;
            mergestate->mj_FillInner = false;
            mergestate->mj_NullInnerTupleSlot =
                ExecInitNullTupleSlot(estate,
                              ExecGetResultType(innerPlanState(mergestate)));
            break;
        case JOIN_RIGHT:
            mergestate->mj_FillOuter = false;
            mergestate->mj_FillInner = true;
            mergestate->mj_NullOuterTupleSlot =
                ExecInitNullTupleSlot(estate,
                              ExecGetResultType(outerPlanState(mergestate)));

            /*
             * Can't handle right or full join with non-constant extra
             * joinclauses.  This should have been caught by planner.
             */
            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")));
            break;
        case JOIN_FULL:
            mergestate->mj_FillOuter = true;
            mergestate->mj_FillInner = true;
            mergestate->mj_NullOuterTupleSlot =
                ExecInitNullTupleSlot(estate,
                              ExecGetResultType(outerPlanState(mergestate)));
            mergestate->mj_NullInnerTupleSlot =
                ExecInitNullTupleSlot(estate,
                              ExecGetResultType(innerPlanState(mergestate)));

            /*
             * Can't handle right or full join with non-constant extra
             * joinclauses.  This should have been caught by planner.
             */
            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")));
            break;
        default:
            elog(ERROR, "unrecognized join type: %d",
                 (int) node->join.jointype);
    }

    /*
     * initialize tuple type and projection info
     */
    ExecAssignResultTypeFromTL(&mergestate->js.ps);
    ExecAssignProjectionInfo(&mergestate->js.ps, NULL);

    /*
     * preprocess the merge clauses
     */
    mergestate->mj_NumClauses = list_length(node->mergeclauses);
    mergestate->mj_Clauses = MJExamineQuals(node->mergeclauses,
                                            node->mergeFamilies,
                                            node->mergeCollations,
                                            node->mergeStrategies,
                                            node->mergeNullsFirst,
                                            (PlanState *) mergestate);

    /*
     * initialize join state
     */
    mergestate->mj_JoinState = EXEC_MJ_INITIALIZE_OUTER;
    mergestate->js.ps.ps_TupFromTlist = false;
    mergestate->mj_MatchedOuter = false;
    mergestate->mj_MatchedInner = false;
    mergestate->mj_OuterTupleSlot = NULL;
    mergestate->mj_InnerTupleSlot = NULL;

    /*
     * initialization successful
     */
    MJ1_printf("ExecInitMergeJoin: %s\n",
               "node initialized");

    return mergestate;
}

TupleTableSlot* ExecMergeJoin ( MergeJoinState node  ) 

Definition at line 611 of file nodeMergejoin.c.

References Assert, ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, elog, ERROR, EXEC_MJ_ENDINNER, EXEC_MJ_ENDOUTER, EXEC_MJ_INITIALIZE_INNER, EXEC_MJ_INITIALIZE_OUTER, EXEC_MJ_JOINTUPLES, EXEC_MJ_NEXTINNER, EXEC_MJ_NEXTOUTER, EXEC_MJ_SKIP_TEST, EXEC_MJ_SKIPINNER_ADVANCE, EXEC_MJ_SKIPOUTER_ADVANCE, EXEC_MJ_TESTOUTER, ExecMarkPos(), ExecProcNode(), ExecProject(), ExecQual(), ExecRestrPos(), ExprEndResult, ExprMultipleResult, innerPlanState, InstrCountFiltered1, InstrCountFiltered2, JOIN_ANTI, JOIN_SEMI, JoinState::joinqual, JoinState::jointype, MergeJoinState::js, MarkInnerTuple, MJ_DEBUG_COMPARE, MJ_DEBUG_PROC_NODE, MJ_DEBUG_QUAL, MJ_dump, MergeJoinState::mj_ExtraMarks, MergeJoinState::mj_FillInner, MergeJoinState::mj_FillOuter, MergeJoinState::mj_InnerTupleSlot, MergeJoinState::mj_JoinState, MergeJoinState::mj_MarkedTupleSlot, MergeJoinState::mj_MatchedInner, MergeJoinState::mj_MatchedOuter, MergeJoinState::mj_OuterTupleSlot, MJ_printf, MJCompare(), MJEVAL_ENDOFJOIN, MJEVAL_MATCHABLE, MJEVAL_NONMATCHABLE, MJEvalInnerValues(), MJEvalOuterValues(), MJFillInner(), MJFillOuter(), NIL, outerPlanState, JoinState::ps, PlanState::ps_ExprContext, PlanState::ps_ProjInfo, PlanState::ps_TupFromTlist, PlanState::qual, ResetExprContext, and TupIsNull.

Referenced by ExecProcNode().

{
    List       *joinqual;
    List       *otherqual;
    bool        qualResult;
    int         compareResult;
    PlanState  *innerPlan;
    TupleTableSlot *innerTupleSlot;
    PlanState  *outerPlan;
    TupleTableSlot *outerTupleSlot;
    ExprContext *econtext;
    bool        doFillOuter;
    bool        doFillInner;

    /*
     * get information from node
     */
    innerPlan = innerPlanState(node);
    outerPlan = outerPlanState(node);
    econtext = node->js.ps.ps_ExprContext;
    joinqual = node->js.joinqual;
    otherqual = node->js.ps.qual;
    doFillOuter = node->mj_FillOuter;
    doFillInner = node->mj_FillInner;

    /*
     * Check to see if we're still projecting out tuples from a previous join
     * tuple (because there is a function-returning-set in the projection
     * expressions).  If so, try to project another one.
     */
    if (node->js.ps.ps_TupFromTlist)
    {
        TupleTableSlot *result;
        ExprDoneCond isDone;

        result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
        if (isDone == ExprMultipleResult)
            return result;
        /* Done with that source tuple... */
        node->js.ps.ps_TupFromTlist = false;
    }

    /*
     * Reset per-tuple memory context to free any expression evaluation
     * storage allocated in the previous tuple cycle.  Note this can't happen
     * until we're done projecting out tuples from a join tuple.
     */
    ResetExprContext(econtext);

    /*
     * ok, everything is setup.. let's go to work
     */
    for (;;)
    {
        MJ_dump(node);

        /*
         * get the current state of the join and do things accordingly.
         */
        switch (node->mj_JoinState)
        {
                /*
                 * EXEC_MJ_INITIALIZE_OUTER means that this is the first time
                 * ExecMergeJoin() has been called and so we have to fetch the
                 * first matchable tuple for both outer and inner subplans. We
                 * do the outer side in INITIALIZE_OUTER state, then advance
                 * to INITIALIZE_INNER state for the inner subplan.
                 */
            case EXEC_MJ_INITIALIZE_OUTER:
                MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE_OUTER\n");

                outerTupleSlot = ExecProcNode(outerPlan);
                node->mj_OuterTupleSlot = outerTupleSlot;

                /* Compute join values and check for unmatchability */
                switch (MJEvalOuterValues(node))
                {
                    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;
                        }
                        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;

            case EXEC_MJ_INITIALIZE_INNER:
                MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE_INNER\n");

                innerTupleSlot = ExecProcNode(innerPlan);
                node->mj_InnerTupleSlot = innerTupleSlot;

                /* Compute join values and check for unmatchability */
                switch (MJEvalInnerValues(node, innerTupleSlot))
                {
                    case MJEVAL_MATCHABLE:

                        /*
                         * OK, we have the initial tuples.  Begin by skipping
                         * non-matching tuples.
                         */
                        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;
                        }
                        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;

                /*
                 * EXEC_MJ_JOINTUPLES means we have two tuples which satisfied
                 * the merge clause so we join them and then proceed to get
                 * the next inner tuple (EXEC_MJ_NEXTINNER).
                 */
            case EXEC_MJ_JOINTUPLES:
                MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");

                /*
                 * Set the next state machine state.  The right things will
                 * happen whether we return this join tuple or just fall
                 * through to continue the state machine execution.
                 */
                node->mj_JoinState = EXEC_MJ_NEXTINNER;

                /*
                 * Check the extra qual conditions to see if we actually want
                 * to return this join tuple.  If not, can proceed with merge.
                 * We must distinguish the additional joinquals (which must
                 * pass to consider the tuples "matched" for outer-join logic)
                 * from the otherquals (which must pass before we actually
                 * return the tuple).
                 *
                 * We don't bother with a ResetExprContext here, on the
                 * assumption that we just did one while checking the merge
                 * qual.  One per tuple should be sufficient.  We do have to
                 * set up the econtext links to the tuples for ExecQual to
                 * use.
                 */
                outerTupleSlot = node->mj_OuterTupleSlot;
                econtext->ecxt_outertuple = outerTupleSlot;
                innerTupleSlot = node->mj_InnerTupleSlot;
                econtext->ecxt_innertuple = innerTupleSlot;

                qualResult = (joinqual == NIL ||
                              ExecQual(joinqual, econtext, false));
                MJ_DEBUG_QUAL(joinqual, qualResult);

                if (qualResult)
                {
                    node->mj_MatchedOuter = true;
                    node->mj_MatchedInner = true;

                    /* In an antijoin, we never return a matched tuple */
                    if (node->js.jointype == JOIN_ANTI)
                    {
                        node->mj_JoinState = EXEC_MJ_NEXTOUTER;
                        break;
                    }

                    /*
                     * 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;

                    qualResult = (otherqual == NIL ||
                                  ExecQual(otherqual, econtext, false));
                    MJ_DEBUG_QUAL(otherqual, qualResult);

                    if (qualResult)
                    {
                        /*
                         * qualification succeeded.  now form the desired
                         * projection tuple and return the slot containing it.
                         */
                        TupleTableSlot *result;
                        ExprDoneCond isDone;

                        MJ_printf("ExecMergeJoin: returning tuple\n");

                        result = ExecProject(node->js.ps.ps_ProjInfo,
                                             &isDone);

                        if (isDone != ExprEndResult)
                        {
                            node->js.ps.ps_TupFromTlist =
                                (isDone == ExprMultipleResult);
                            return result;
                        }
                    }
                    else
                        InstrCountFiltered2(node, 1);
                }
                else
                    InstrCountFiltered1(node, 1);
                break;

                /*
                 * EXEC_MJ_NEXTINNER means advance the inner scan to the next
                 * tuple. If the tuple is not nil, we then proceed to test it
                 * against the join qualification.
                 *
                 * Before advancing, we check to see if we must emit an
                 * outer-join fill tuple for this inner tuple.
                 */
            case EXEC_MJ_NEXTINNER:
                MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n");

                if (doFillInner && !node->mj_MatchedInner)
                {
                    /*
                     * Generate a fake join tuple with nulls for the outer
                     * tuple, and return it if it passes the non-join quals.
                     */
                    TupleTableSlot *result;

                    node->mj_MatchedInner = true;       /* do it only once */

                    result = MJFillInner(node);
                    if (result)
                        return result;
                }

                /*
                 * 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).
                 *
                 * NB: must NOT do "extraMarks" here, since we may need to
                 * return to previously marked tuples.
                 */
                innerTupleSlot = ExecProcNode(innerPlan);
                node->mj_InnerTupleSlot = innerTupleSlot;
                MJ_DEBUG_PROC_NODE(innerTupleSlot);
                node->mj_MatchedInner = false;

                /* Compute join values and check for unmatchability */
                switch (MJEvalInnerValues(node, innerTupleSlot))
                {
                    case MJEVAL_MATCHABLE:

                        /*
                         * 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:

                        /*
                         * 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;

                /*-------------------------------------------
                 * EXEC_MJ_NEXTOUTER means
                 *
                 *              outer inner
                 * outer tuple -  5     5  - marked tuple
                 *                5     5
                 *                6     6  - inner tuple
                 *                7     7
                 *
                 * we know we just bumped into the
                 * first inner tuple > current outer tuple (or possibly
                 * the end of the inner stream)
                 * so get a new outer tuple and then
                 * proceed to test it against the marked tuple
                 * (EXEC_MJ_TESTOUTER)
                 *
                 * Before advancing, we check to see if we must emit an
                 * outer-join fill tuple for this outer tuple.
                 *------------------------------------------------
                 */
            case EXEC_MJ_NEXTOUTER:
                MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n");

                if (doFillOuter && !node->mj_MatchedOuter)
                {
                    /*
                     * Generate a fake join tuple with nulls for the inner
                     * tuple, and return it if it passes the non-join quals.
                     */
                    TupleTableSlot *result;

                    node->mj_MatchedOuter = true;       /* do it only once */

                    result = MJFillOuter(node);
                    if (result)
                        return result;
                }

                /*
                 * now we get the next outer tuple, if any
                 */
                outerTupleSlot = ExecProcNode(outerPlan);
                node->mj_OuterTupleSlot = outerTupleSlot;
                MJ_DEBUG_PROC_NODE(outerTupleSlot);
                node->mj_MatchedOuter = false;

                /* Compute join values and check for unmatchability */
                switch (MJEvalOuterValues(node))
                {
                    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;

                /*--------------------------------------------------------
                 * EXEC_MJ_TESTOUTER If the new outer tuple and the marked
                 * 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 tuple with the inner tuples.
                 *
                 * This is the case when
                 *                        outer inner
                 *                          4     5  - marked tuple
                 *           outer tuple -  5     5
                 *       new outer tuple -  5     5
                 *                          6     8  - inner tuple
                 *                          7    12
                 *
                 *              new outer tuple == marked tuple
                 *
                 * If the outer tuple fails the test, then we are done
                 * with the marked tuples, and we have to look for a
                 * match to the current inner tuple.  So we will
                 * proceed to skip outer tuples until outer >= inner
                 * (EXEC_MJ_SKIP_TEST).
                 *
                 *      This is the case when
                 *
                 *                        outer inner
                 *                          5     5  - marked tuple
                 *           outer tuple -  5     5
                 *       new outer tuple -  6     8  - inner tuple
                 *                          7    12
                 *
                 *              new outer tuple > marked tuple
                 *
                 *---------------------------------------------------------
                 */
            case EXEC_MJ_TESTOUTER:
                MJ_printf("ExecMergeJoin: EXEC_MJ_TESTOUTER\n");

                /*
                 * 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);

                compareResult = MJCompare(node);
                MJ_DEBUG_COMPARE(compareResult);

                if (compareResult == 0)
                {
                    /*
                     * the merge clause matched so now we restore the inner
                     * scan position to the first mark, and go join that tuple
                     * (and any following ones) to the new outer.
                     *
                     * NOTE: we do not need to worry about the MatchedInner
                     * 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 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);

                    /*
                     * ExecRestrPos probably should give us back a new Slot,
                     * but since it doesn't, use the marked slot.  (The
                     * previously returned mj_InnerTupleSlot cannot be assumed
                     * to hold the required tuple.)
                     */
                    node->mj_InnerTupleSlot = innerTupleSlot;
                    /* we need not do MJEvalInnerValues again */

                    node->mj_JoinState = EXEC_MJ_JOINTUPLES;
                }
                else
                {
                    /* ----------------
                     *  if the new outer tuple didn't match the marked inner
                     *  tuple then we have a case like:
                     *
                     *           outer inner
                     *             4     4  - marked tuple
                     * new outer - 5     4
                     *             6     5  - inner tuple
                     *             7
                     *
                     *  which means that all subsequent outer tuples will be
                     *  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, no more matches are possible.
                     * ----------------
                     */
                    Assert(compareResult > 0);
                    innerTupleSlot = node->mj_InnerTupleSlot;

                    /* reload comparison data for current inner */
                    switch (MJEvalInnerValues(node, innerTupleSlot))
                    {
                        case MJEVAL_MATCHABLE:
                            /* proceed to compare it to the current outer */
                            node->mj_JoinState = EXEC_MJ_SKIP_TEST;
                            break;
                        case MJEVAL_NONMATCHABLE:

                            /*
                             * 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;
                        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;

                /*----------------------------------------------------------
                 * EXEC_MJ_SKIP means compare tuples and if they do not
                 * match, skip whichever is lesser.
                 *
                 * For example:
                 *
                 *              outer inner
                 *                5     5
                 *                5     5
                 * outer tuple -  6     8  - inner tuple
                 *                7    12
                 *                8    14
                 *
                 * we have to advance the outer scan
                 * until we find the outer 8.
                 *
                 * On the other hand:
                 *
                 *              outer inner
                 *                5     5
                 *                5     5
                 * outer tuple - 12     8  - inner tuple
                 *               14    10
                 *               17    12
                 *
                 * we have to advance the inner scan
                 * until we find the inner 12.
                 *----------------------------------------------------------
                 */
            case EXEC_MJ_SKIP_TEST:
                MJ_printf("ExecMergeJoin: EXEC_MJ_SKIP_TEST\n");

                /*
                 * before we advance, make sure the current tuples do not
                 * satisfy the mergeclauses.  If they do, then we update the
                 * marked tuple position and go join them.
                 */
                compareResult = MJCompare(node);
                MJ_DEBUG_COMPARE(compareResult);

                if (compareResult == 0)
                {
                    ExecMarkPos(innerPlan);

                    MarkInnerTuple(node->mj_InnerTupleSlot, node);

                    node->mj_JoinState = EXEC_MJ_JOINTUPLES;
                }
                else if (compareResult < 0)
                    node->mj_JoinState = EXEC_MJ_SKIPOUTER_ADVANCE;
                else
                    /* compareResult > 0 */
                    node->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE;
                break;

                /*
                 * SKIPOUTER_ADVANCE: advance over an outer tuple that is
                 * known not to join to any inner tuple.
                 *
                 * Before advancing, we check to see if we must emit an
                 * outer-join fill tuple for this outer tuple.
                 */
            case EXEC_MJ_SKIPOUTER_ADVANCE:
                MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_ADVANCE\n");

                if (doFillOuter && !node->mj_MatchedOuter)
                {
                    /*
                     * Generate a fake join tuple with nulls for the inner
                     * tuple, and return it if it passes the non-join quals.
                     */
                    TupleTableSlot *result;

                    node->mj_MatchedOuter = true;       /* do it only once */

                    result = MJFillOuter(node);
                    if (result)
                        return result;
                }

                /*
                 * now we get the next outer tuple, if any
                 */
                outerTupleSlot = ExecProcNode(outerPlan);
                node->mj_OuterTupleSlot = outerTupleSlot;
                MJ_DEBUG_PROC_NODE(outerTupleSlot);
                node->mj_MatchedOuter = false;

                /* Compute join values and check for unmatchability */
                switch (MJEvalOuterValues(node))
                {
                    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;

                /*
                 * SKIPINNER_ADVANCE: advance over an inner tuple that is
                 * known not to join to any outer tuple.
                 *
                 * Before advancing, we check to see if we must emit an
                 * outer-join fill tuple for this inner tuple.
                 */
            case EXEC_MJ_SKIPINNER_ADVANCE:
                MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_ADVANCE\n");

                if (doFillInner && !node->mj_MatchedInner)
                {
                    /*
                     * Generate a fake join tuple with nulls for the outer
                     * tuple, and return it if it passes the non-join quals.
                     */
                    TupleTableSlot *result;

                    node->mj_MatchedInner = true;       /* do it only once */

                    result = MJFillInner(node);
                    if (result)
                        return result;
                }

                /* Mark before advancing, if wanted */
                if (node->mj_ExtraMarks)
                    ExecMarkPos(innerPlan);

                /*
                 * now we get the next inner tuple, if any
                 */
                innerTupleSlot = ExecProcNode(innerPlan);
                node->mj_InnerTupleSlot = innerTupleSlot;
                MJ_DEBUG_PROC_NODE(innerTupleSlot);
                node->mj_MatchedInner = false;

                /* Compute join values and check for unmatchability */
                switch (MJEvalInnerValues(node, innerTupleSlot))
                {
                    case MJEVAL_MATCHABLE:
                        /* proceed to compare it to the current outer */
                        node->mj_JoinState = EXEC_MJ_SKIP_TEST;
                        break;
                    case MJEVAL_NONMATCHABLE:

                        /*
                         * 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;
                    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;

                /*
                 * EXEC_MJ_ENDOUTER means we have run out of outer tuples, but
                 * are doing a right/full join and therefore must null-fill
                 * any remaining unmatched inner tuples.
                 */
            case EXEC_MJ_ENDOUTER:
                MJ_printf("ExecMergeJoin: EXEC_MJ_ENDOUTER\n");

                Assert(doFillInner);

                if (!node->mj_MatchedInner)
                {
                    /*
                     * Generate a fake join tuple with nulls for the outer
                     * tuple, and return it if it passes the non-join quals.
                     */
                    TupleTableSlot *result;

                    node->mj_MatchedInner = true;       /* do it only once */

                    result = MJFillInner(node);
                    if (result)
                        return result;
                }

                /* Mark before advancing, if wanted */
                if (node->mj_ExtraMarks)
                    ExecMarkPos(innerPlan);

                /*
                 * now we get the next inner tuple, if any
                 */
                innerTupleSlot = ExecProcNode(innerPlan);
                node->mj_InnerTupleSlot = innerTupleSlot;
                MJ_DEBUG_PROC_NODE(innerTupleSlot);
                node->mj_MatchedInner = false;

                if (TupIsNull(innerTupleSlot))
                {
                    MJ_printf("ExecMergeJoin: end of inner subplan\n");
                    return NULL;
                }

                /* Else remain in ENDOUTER state and process next tuple. */
                break;

                /*
                 * EXEC_MJ_ENDINNER means we have run out of inner tuples, but
                 * are doing a left/full join and therefore must null- fill
                 * any remaining unmatched outer tuples.
                 */
            case EXEC_MJ_ENDINNER:
                MJ_printf("ExecMergeJoin: EXEC_MJ_ENDINNER\n");

                Assert(doFillOuter);

                if (!node->mj_MatchedOuter)
                {
                    /*
                     * Generate a fake join tuple with nulls for the inner
                     * tuple, and return it if it passes the non-join quals.
                     */
                    TupleTableSlot *result;

                    node->mj_MatchedOuter = true;       /* do it only once */

                    result = MJFillOuter(node);
                    if (result)
                        return result;
                }

                /*
                 * now we get the next outer tuple, if any
                 */
                outerTupleSlot = ExecProcNode(outerPlan);
                node->mj_OuterTupleSlot = outerTupleSlot;
                MJ_DEBUG_PROC_NODE(outerTupleSlot);
                node->mj_MatchedOuter = false;

                if (TupIsNull(outerTupleSlot))
                {
                    MJ_printf("ExecMergeJoin: end of outer subplan\n");
                    return NULL;
                }

                /* Else remain in ENDINNER state and process next tuple. */
                break;

                /*
                 * broken state value?
                 */
            default:
                elog(ERROR, "unrecognized mergejoin state: %d",
                     (int) node->mj_JoinState);
        }
    }
}

void ExecReScanMergeJoin ( MergeJoinState node  ) 
static int MJCompare ( MergeJoinState mergestate  )  [static]

Definition at line 383 of file nodeMergejoin.c.

References ApplySortComparator(), ExprContext::ecxt_per_tuple_memory, i, MergeJoinState::js, MergeJoinClauseData::ldatum, MergeJoinClauseData::lisnull, MemoryContextSwitchTo(), MergeJoinState::mj_Clauses, MergeJoinState::mj_ConstFalseJoin, MergeJoinState::mj_NumClauses, JoinState::ps, PlanState::ps_ExprContext, MergeJoinClauseData::rdatum, ResetExprContext, MergeJoinClauseData::risnull, and MergeJoinClauseData::ssup.

Referenced by ExecMergeJoin().

{
    int         result = 0;
    bool        nulleqnull = false;
    ExprContext *econtext = mergestate->js.ps.ps_ExprContext;
    int         i;
    MemoryContext oldContext;

    /*
     * Call the comparison functions in short-lived context, in case they leak
     * memory.
     */
    ResetExprContext(econtext);

    oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);

    for (i = 0; i < mergestate->mj_NumClauses; i++)
    {
        MergeJoinClause clause = &mergestate->mj_Clauses[i];

        /*
         * Special case for NULL-vs-NULL, else use standard comparison.
         */
        if (clause->lisnull && clause->risnull)
        {
            nulleqnull = true;  /* NULL "=" NULL */
            continue;
        }

        result = ApplySortComparator(clause->ldatum, clause->lisnull,
                                     clause->rdatum, clause->risnull,
                                     &clause->ssup);

        if (result != 0)
            break;
    }

    /*
     * 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 (result == 0 &&
        (nulleqnull || mergestate->mj_ConstFalseJoin))
        result = 1;

    MemoryContextSwitchTo(oldContext);

    return result;
}

static MJEvalResult MJEvalInnerValues ( MergeJoinState mergestate,
TupleTableSlot innerslot 
) [static]

Definition at line 333 of file nodeMergejoin.c.

References ExprContext::ecxt_innertuple, ExprContext::ecxt_per_tuple_memory, ExecEvalExpr, i, MemoryContextSwitchTo(), MergeJoinState::mj_Clauses, MergeJoinState::mj_FillInner, MergeJoinState::mj_InnerEContext, MergeJoinState::mj_NumClauses, MJEVAL_MATCHABLE, NULL, MergeJoinClauseData::rdatum, ResetExprContext, MergeJoinClauseData::rexpr, MergeJoinClauseData::risnull, MergeJoinClauseData::ssup, SortSupportData::ssup_nulls_first, and TupIsNull.

Referenced by ExecMergeJoin().

{
    ExprContext *econtext = mergestate->mj_InnerEContext;
    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);

    econtext->ecxt_innertuple = innerslot;

    for (i = 0; i < mergestate->mj_NumClauses; i++)
    {
        MergeJoinClause clause = &mergestate->mj_Clauses[i];

        clause->rdatum = ExecEvalExpr(clause->rexpr, econtext,
                                      &clause->risnull, NULL);
        if (clause->risnull)
        {
            /* 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 result;
}

static MJEvalResult MJEvalOuterValues ( MergeJoinState mergestate  )  [static]

Definition at line 286 of file nodeMergejoin.c.

References ExprContext::ecxt_outertuple, ExprContext::ecxt_per_tuple_memory, ExecEvalExpr, i, MergeJoinClauseData::ldatum, MergeJoinClauseData::lexpr, MergeJoinClauseData::lisnull, MemoryContextSwitchTo(), MergeJoinState::mj_Clauses, MergeJoinState::mj_FillOuter, MergeJoinState::mj_NumClauses, MergeJoinState::mj_OuterEContext, MergeJoinState::mj_OuterTupleSlot, MJEVAL_MATCHABLE, NULL, ResetExprContext, MergeJoinClauseData::ssup, SortSupportData::ssup_nulls_first, and TupIsNull.

Referenced by ExecMergeJoin().

{
    ExprContext *econtext = mergestate->mj_OuterEContext;
    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);

    econtext->ecxt_outertuple = mergestate->mj_OuterTupleSlot;

    for (i = 0; i < mergestate->mj_NumClauses; i++)
    {
        MergeJoinClause clause = &mergestate->mj_Clauses[i];

        clause->ldatum = ExecEvalExpr(clause->lexpr, econtext,
                                      &clause->lisnull, NULL);
        if (clause->lisnull)
        {
            /* 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 result;
}

static MergeJoinClause MJExamineQuals ( List mergeclauses,
Oid mergefamilies,
Oid mergecollations,
int *  mergestrategies,
bool mergenullsfirst,
PlanState parent 
) [static]

Definition at line 175 of file nodeMergejoin.c.

References OpExpr::args, Assert, BTEqualStrategyNumber, BTGreaterStrategyNumber, BTLessStrategyNumber, BTORDER_PROC, BTSORTSUPPORT_PROC, SortSupportData::comparator, CurrentMemoryContext, elog, ERROR, ExecInitExpr(), get_op_opfamily_properties(), get_opfamily_proc(), IsA, MergeJoinClauseData::lexpr, lfirst, linitial, list_length(), lsecond, NULL, OidFunctionCall1, OidIsValid, OpExpr::opno, palloc0(), PointerGetDatum, PrepareSortSupportComparisonShim(), MergeJoinClauseData::rexpr, MergeJoinClauseData::ssup, SortSupportData::ssup_collation, SortSupportData::ssup_cxt, SortSupportData::ssup_nulls_first, and SortSupportData::ssup_reverse.

Referenced by ExecInitMergeJoin().

{
    MergeJoinClause clauses;
    int         nClauses = list_length(mergeclauses);
    int         iClause;
    ListCell   *cl;

    clauses = (MergeJoinClause) palloc0(nClauses * sizeof(MergeJoinClauseData));

    iClause = 0;
    foreach(cl, 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;
        Oid         sortfunc;

        if (!IsA(qual, OpExpr))
            elog(ERROR, "mergejoin clause is not an OpExpr");

        /*
         * Prepare the input expressions for execution.
         */
        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, false,
                                   &op_strategy,
                                   &op_lefttype,
                                   &op_righttype);
        if (op_strategy != BTEqualStrategyNumber)       /* should not happen */
            elog(ERROR, "cannot merge using non-equality operator %u",
                 qual->opno);

        /* 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++;
    }

    return clauses;
}

static TupleTableSlot* MJFillInner ( MergeJoinState node  )  [static]

Definition at line 485 of file nodeMergejoin.c.

References ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, ExecProject(), ExecQual(), ExprEndResult, InstrCountFiltered2, MergeJoinState::js, MergeJoinState::mj_InnerTupleSlot, MergeJoinState::mj_NullOuterTupleSlot, MJ_printf, JoinState::ps, PlanState::ps_ExprContext, PlanState::ps_ProjInfo, PlanState::ps_TupFromTlist, PlanState::qual, and ResetExprContext.

Referenced by ExecMergeJoin().

{
    ExprContext *econtext = node->js.ps.ps_ExprContext;
    List       *otherqual = node->js.ps.qual;

    ResetExprContext(econtext);

    econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
    econtext->ecxt_innertuple = node->mj_InnerTupleSlot;

    if (ExecQual(otherqual, econtext, false))
    {
        /*
         * qualification succeeded.  now form the desired projection tuple and
         * return the slot containing it.
         */
        TupleTableSlot *result;
        ExprDoneCond isDone;

        MJ_printf("ExecMergeJoin: returning inner fill tuple\n");

        result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);

        if (isDone != ExprEndResult)
        {
            node->js.ps.ps_TupFromTlist =
                (isDone == ExprMultipleResult);
            return result;
        }
    }
    else
        InstrCountFiltered2(node, 1);

    return NULL;
}

static TupleTableSlot* MJFillOuter ( MergeJoinState node  )  [static]

Definition at line 444 of file nodeMergejoin.c.

References ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, ExecProject(), ExecQual(), ExprEndResult, InstrCountFiltered2, MergeJoinState::js, MergeJoinState::mj_NullInnerTupleSlot, MergeJoinState::mj_OuterTupleSlot, MJ_printf, JoinState::ps, PlanState::ps_ExprContext, PlanState::ps_ProjInfo, PlanState::ps_TupFromTlist, PlanState::qual, and ResetExprContext.

Referenced by ExecMergeJoin().

{
    ExprContext *econtext = node->js.ps.ps_ExprContext;
    List       *otherqual = node->js.ps.qual;

    ResetExprContext(econtext);

    econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
    econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;

    if (ExecQual(otherqual, econtext, false))
    {
        /*
         * qualification succeeded.  now form the desired projection tuple and
         * return the slot containing it.
         */
        TupleTableSlot *result;
        ExprDoneCond isDone;

        MJ_printf("ExecMergeJoin: returning outer fill tuple\n");

        result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);

        if (isDone != ExprEndResult)
        {
            node->js.ps.ps_TupFromTlist =
                (isDone == ExprMultipleResult);
            return result;
        }
    }
    else
        InstrCountFiltered2(node, 1);

    return NULL;
}