Header And Logo

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

Data Structures | Typedefs | Functions

nodeSetOp.c File Reference

#include "postgres.h"
#include "access/htup_details.h"
#include "executor/executor.h"
#include "executor/nodeSetOp.h"
#include "utils/memutils.h"
Include dependency graph for nodeSetOp.c:

Go to the source code of this file.

Data Structures

struct  SetOpStatePerGroupData
struct  SetOpHashEntryData

Typedefs

typedef struct
SetOpStatePerGroupData 
SetOpStatePerGroupData
typedef struct SetOpHashEntryDataSetOpHashEntry
typedef struct SetOpHashEntryData SetOpHashEntryData

Functions

static TupleTableSlotsetop_retrieve_direct (SetOpState *setopstate)
static void setop_fill_hash_table (SetOpState *setopstate)
static TupleTableSlotsetop_retrieve_hash_table (SetOpState *setopstate)
static void initialize_counts (SetOpStatePerGroup pergroup)
static void advance_counts (SetOpStatePerGroup pergroup, int flag)
static int fetch_tuple_flag (SetOpState *setopstate, TupleTableSlot *inputslot)
static void build_hash_table (SetOpState *setopstate)
static void set_output_count (SetOpState *setopstate, SetOpStatePerGroup pergroup)
TupleTableSlotExecSetOp (SetOpState *node)
SetOpStateExecInitSetOp (SetOp *node, EState *estate, int eflags)
void ExecEndSetOp (SetOpState *node)
void ExecReScanSetOp (SetOpState *node)

Typedef Documentation

Definition at line 74 of file nodeSetOp.c.


Function Documentation

static void advance_counts ( SetOpStatePerGroup  pergroup,
int  flag 
) [inline, static]

Definition at line 101 of file nodeSetOp.c.

References SetOpStatePerGroupData::numLeft, and SetOpStatePerGroupData::numRight.

Referenced by setop_fill_hash_table(), and setop_retrieve_direct().

{
    if (flag)
        pergroup->numRight++;
    else
        pergroup->numLeft++;
}

static void build_hash_table ( SetOpState setopstate  )  [static]
void ExecEndSetOp ( SetOpState node  ) 

Definition at line 586 of file nodeSetOp.c.

References ExecClearTuple(), ExecEndNode(), MemoryContextDelete(), outerPlanState, SetOpState::ps, PlanState::ps_ResultTupleSlot, SetOpState::tableContext, and SetOpState::tempContext.

Referenced by ExecEndNode().

{
    /* clean up tuple table */
    ExecClearTuple(node->ps.ps_ResultTupleSlot);

    /* free subsidiary stuff including hashtable */
    MemoryContextDelete(node->tempContext);
    if (node->tableContext)
        MemoryContextDelete(node->tableContext);

    ExecEndNode(outerPlanState(node));
}

SetOpState* ExecInitSetOp ( SetOp node,
EState estate,
int  eflags 
)

Definition at line 477 of file nodeSetOp.c.

References ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), Assert, build_hash_table(), CurrentMemoryContext, SetOp::dupOperators, SetOpState::eqfunctions, EXEC_FLAG_BACKWARD, EXEC_FLAG_MARK, ExecAssignResultTypeFromTL(), ExecInitNode(), ExecInitResultTupleSlot(), execTuplesHashPrepare(), execTuplesMatchPrepare(), SetOpState::grp_firstTuple, SetOpState::hashfunctions, SetOpState::hashtable, makeNode, SetOp::numCols, SetOpState::numOutput, outerPlan, outerPlanState, palloc0(), SetOpState::pergroup, PlanState::plan, SetOpState::ps, PlanState::ps_ProjInfo, SetOpState::setop_done, SETOP_HASHED, PlanState::state, SetOp::strategy, SetOpState::table_filled, SetOpState::tableContext, and SetOpState::tempContext.

Referenced by ExecInitNode().

{
    SetOpState *setopstate;

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

    /*
     * create state structure
     */
    setopstate = makeNode(SetOpState);
    setopstate->ps.plan = (Plan *) node;
    setopstate->ps.state = estate;

    setopstate->eqfunctions = NULL;
    setopstate->hashfunctions = NULL;
    setopstate->setop_done = false;
    setopstate->numOutput = 0;
    setopstate->pergroup = NULL;
    setopstate->grp_firstTuple = NULL;
    setopstate->hashtable = NULL;
    setopstate->tableContext = NULL;

    /*
     * Miscellaneous initialization
     *
     * SetOp nodes have no ExprContext initialization because they never call
     * ExecQual or ExecProject.  But they do need a per-tuple memory context
     * anyway for calling execTuplesMatch.
     */
    setopstate->tempContext =
        AllocSetContextCreate(CurrentMemoryContext,
                              "SetOp",
                              ALLOCSET_DEFAULT_MINSIZE,
                              ALLOCSET_DEFAULT_INITSIZE,
                              ALLOCSET_DEFAULT_MAXSIZE);

    /*
     * If hashing, we also need a longer-lived context to store the hash
     * table.  The table can't just be kept in the per-query context because
     * we want to be able to throw it away in ExecReScanSetOp.
     */
    if (node->strategy == SETOP_HASHED)
        setopstate->tableContext =
            AllocSetContextCreate(CurrentMemoryContext,
                                  "SetOp hash table",
                                  ALLOCSET_DEFAULT_MINSIZE,
                                  ALLOCSET_DEFAULT_INITSIZE,
                                  ALLOCSET_DEFAULT_MAXSIZE);

    /*
     * Tuple table initialization
     */
    ExecInitResultTupleSlot(estate, &setopstate->ps);

    /*
     * initialize child nodes
     *
     * If we are hashing then the child plan does not need to handle REWIND
     * efficiently; see ExecReScanSetOp.
     */
    if (node->strategy == SETOP_HASHED)
        eflags &= ~EXEC_FLAG_REWIND;
    outerPlanState(setopstate) = ExecInitNode(outerPlan(node), estate, eflags);

    /*
     * setop nodes do no projections, so initialize projection info for this
     * node appropriately
     */
    ExecAssignResultTypeFromTL(&setopstate->ps);
    setopstate->ps.ps_ProjInfo = NULL;

    /*
     * Precompute fmgr lookup data for inner loop. We need both equality and
     * hashing functions to do it by hashing, but only equality if not
     * hashing.
     */
    if (node->strategy == SETOP_HASHED)
        execTuplesHashPrepare(node->numCols,
                              node->dupOperators,
                              &setopstate->eqfunctions,
                              &setopstate->hashfunctions);
    else
        setopstate->eqfunctions =
            execTuplesMatchPrepare(node->numCols,
                                   node->dupOperators);

    if (node->strategy == SETOP_HASHED)
    {
        build_hash_table(setopstate);
        setopstate->table_filled = false;
    }
    else
    {
        setopstate->pergroup =
            (SetOpStatePerGroup) palloc0(sizeof(SetOpStatePerGroupData));
    }

    return setopstate;
}

void ExecReScanSetOp ( SetOpState node  ) 

Definition at line 601 of file nodeSetOp.c.

References build_hash_table(), PlanState::chgParam, ExecClearTuple(), ExecReScan(), SetOpState::grp_firstTuple, SetOpState::hashiter, SetOpState::hashtable, heap_freetuple(), PlanState::lefttree, MemoryContextResetAndDeleteChildren(), NULL, SetOpState::numOutput, PlanState::plan, SetOpState::ps, PlanState::ps_ResultTupleSlot, ResetTupleHashIterator, SetOpState::setop_done, SETOP_HASHED, SetOpState::table_filled, and SetOpState::tableContext.

Referenced by ExecReScan().

{
    ExecClearTuple(node->ps.ps_ResultTupleSlot);
    node->setop_done = false;
    node->numOutput = 0;

    if (((SetOp *) node->ps.plan)->strategy == SETOP_HASHED)
    {
        /*
         * In the hashed case, if we haven't yet built the hash table then we
         * can just return; nothing done yet, so nothing to undo. If subnode's
         * chgParam is not NULL then it will be re-scanned by ExecProcNode,
         * else no reason to re-scan it at all.
         */
        if (!node->table_filled)
            return;

        /*
         * If we do have the hash table and the subplan does not have any
         * parameter changes, then we can just rescan the existing hash table;
         * no need to build it again.
         */
        if (node->ps.lefttree->chgParam == NULL)
        {
            ResetTupleHashIterator(node->hashtable, &node->hashiter);
            return;
        }
    }

    /* Release first tuple of group, if we have made a copy */
    if (node->grp_firstTuple != NULL)
    {
        heap_freetuple(node->grp_firstTuple);
        node->grp_firstTuple = NULL;
    }

    /* Release any hashtable storage */
    if (node->tableContext)
        MemoryContextResetAndDeleteChildren(node->tableContext);

    /* And rebuild empty hashtable if needed */
    if (((SetOp *) node->ps.plan)->strategy == SETOP_HASHED)
    {
        build_hash_table(node);
        node->table_filled = false;
    }

    /*
     * if chgParam of subnode is not null then plan will be re-scanned by
     * first ExecProcNode.
     */
    if (node->ps.lefttree->chgParam == NULL)
        ExecReScan(node->ps.lefttree);
}

TupleTableSlot* ExecSetOp ( SetOpState node  ) 

Definition at line 195 of file nodeSetOp.c.

References SetOpState::numOutput, PlanState::plan, SetOpState::ps, PlanState::ps_ResultTupleSlot, SetOpState::setop_done, setop_fill_hash_table(), SETOP_HASHED, setop_retrieve_direct(), setop_retrieve_hash_table(), SetOp::strategy, and SetOpState::table_filled.

Referenced by ExecProcNode().

{
    SetOp      *plannode = (SetOp *) node->ps.plan;
    TupleTableSlot *resultTupleSlot = node->ps.ps_ResultTupleSlot;

    /*
     * If the previously-returned tuple needs to be returned more than once,
     * keep returning it.
     */
    if (node->numOutput > 0)
    {
        node->numOutput--;
        return resultTupleSlot;
    }

    /* Otherwise, we're done if we are out of groups */
    if (node->setop_done)
        return NULL;

    /* Fetch the next tuple group according to the correct strategy */
    if (plannode->strategy == SETOP_HASHED)
    {
        if (!node->table_filled)
            setop_fill_hash_table(node);
        return setop_retrieve_hash_table(node);
    }
    else
        return setop_retrieve_direct(node);
}

static int fetch_tuple_flag ( SetOpState setopstate,
TupleTableSlot inputslot 
) [static]

Definition at line 114 of file nodeSetOp.c.

References Assert, DatumGetInt32, flag(), SetOp::flagColIdx, PlanState::plan, SetOpState::ps, and slot_getattr().

Referenced by setop_fill_hash_table(), and setop_retrieve_direct().

{
    SetOp      *node = (SetOp *) setopstate->ps.plan;
    int         flag;
    bool        isNull;

    flag = DatumGetInt32(slot_getattr(inputslot,
                                      node->flagColIdx,
                                      &isNull));
    Assert(!isNull);
    Assert(flag == 0 || flag == 1);
    return flag;
}

static void initialize_counts ( SetOpStatePerGroup  pergroup  )  [inline, static]

Definition at line 92 of file nodeSetOp.c.

References SetOpStatePerGroupData::numLeft, and SetOpStatePerGroupData::numRight.

Referenced by setop_fill_hash_table(), and setop_retrieve_direct().

{
    pergroup->numLeft = pergroup->numRight = 0;
}

static void set_output_count ( SetOpState setopstate,
SetOpStatePerGroup  pergroup 
) [static]

Definition at line 155 of file nodeSetOp.c.

References SetOp::cmd, elog, ERROR, SetOpStatePerGroupData::numLeft, SetOpState::numOutput, SetOpStatePerGroupData::numRight, PlanState::plan, SetOpState::ps, SETOPCMD_EXCEPT, SETOPCMD_EXCEPT_ALL, SETOPCMD_INTERSECT, and SETOPCMD_INTERSECT_ALL.

Referenced by setop_retrieve_direct(), and setop_retrieve_hash_table().

{
    SetOp      *plannode = (SetOp *) setopstate->ps.plan;

    switch (plannode->cmd)
    {
        case SETOPCMD_INTERSECT:
            if (pergroup->numLeft > 0 && pergroup->numRight > 0)
                setopstate->numOutput = 1;
            else
                setopstate->numOutput = 0;
            break;
        case SETOPCMD_INTERSECT_ALL:
            setopstate->numOutput =
                (pergroup->numLeft < pergroup->numRight) ?
                pergroup->numLeft : pergroup->numRight;
            break;
        case SETOPCMD_EXCEPT:
            if (pergroup->numLeft > 0 && pergroup->numRight == 0)
                setopstate->numOutput = 1;
            else
                setopstate->numOutput = 0;
            break;
        case SETOPCMD_EXCEPT_ALL:
            setopstate->numOutput =
                (pergroup->numLeft < pergroup->numRight) ?
                0 : (pergroup->numLeft - pergroup->numRight);
            break;
        default:
            elog(ERROR, "unrecognized set op: %d", (int) plannode->cmd);
            break;
    }
}

static void setop_fill_hash_table ( SetOpState setopstate  )  [static]

Definition at line 343 of file nodeSetOp.c.

References advance_counts(), Assert, SetOp::cmd, ExecProcNode(), fetch_tuple_flag(), SetOp::firstFlag, SetOpState::hashiter, SetOpState::hashtable, initialize_counts(), LookupTupleHashEntry(), MemoryContextReset(), NULL, outerPlan, outerPlanState, SetOpHashEntryData::pergroup, PG_USED_FOR_ASSERTS_ONLY, PlanState::plan, SetOpState::ps, ResetTupleHashIterator, SETOPCMD_INTERSECT, SETOPCMD_INTERSECT_ALL, SetOpState::table_filled, SetOpState::tempContext, and TupIsNull.

Referenced by ExecSetOp().

{
    SetOp      *node = (SetOp *) setopstate->ps.plan;
    PlanState  *outerPlan;
    int         firstFlag;
    bool in_first_rel PG_USED_FOR_ASSERTS_ONLY;

    /*
     * get state info from node
     */
    outerPlan = outerPlanState(setopstate);
    firstFlag = node->firstFlag;
    /* verify planner didn't mess up */
    Assert(firstFlag == 0 ||
           (firstFlag == 1 &&
            (node->cmd == SETOPCMD_INTERSECT ||
             node->cmd == SETOPCMD_INTERSECT_ALL)));

    /*
     * Process each outer-plan tuple, and then fetch the next one, until we
     * exhaust the outer plan.
     */
    in_first_rel = true;
    for (;;)
    {
        TupleTableSlot *outerslot;
        int         flag;
        SetOpHashEntry entry;
        bool        isnew;

        outerslot = ExecProcNode(outerPlan);
        if (TupIsNull(outerslot))
            break;

        /* Identify whether it's left or right input */
        flag = fetch_tuple_flag(setopstate, outerslot);

        if (flag == firstFlag)
        {
            /* (still) in first input relation */
            Assert(in_first_rel);

            /* Find or build hashtable entry for this tuple's group */
            entry = (SetOpHashEntry)
                LookupTupleHashEntry(setopstate->hashtable, outerslot, &isnew);

            /* If new tuple group, initialize counts */
            if (isnew)
                initialize_counts(&entry->pergroup);

            /* Advance the counts */
            advance_counts(&entry->pergroup, flag);
        }
        else
        {
            /* reached second relation */
            in_first_rel = false;

            /* For tuples not seen previously, do not make hashtable entry */
            entry = (SetOpHashEntry)
                LookupTupleHashEntry(setopstate->hashtable, outerslot, NULL);

            /* Advance the counts if entry is already present */
            if (entry)
                advance_counts(&entry->pergroup, flag);
        }

        /* Must reset temp context after each hashtable lookup */
        MemoryContextReset(setopstate->tempContext);
    }

    setopstate->table_filled = true;
    /* Initialize to walk the hash table */
    ResetTupleHashIterator(setopstate->hashtable, &setopstate->hashiter);
}

static TupleTableSlot * setop_retrieve_direct ( SetOpState setopstate  )  [static]

Definition at line 229 of file nodeSetOp.c.

References advance_counts(), SetOp::dupColIdx, SetOpState::eqfunctions, ExecClearTuple(), ExecCopySlotTuple(), ExecProcNode(), ExecStoreTuple(), execTuplesMatch(), fetch_tuple_flag(), SetOpState::grp_firstTuple, initialize_counts(), InvalidBuffer, NULL, SetOp::numCols, SetOpState::numOutput, outerPlan, outerPlanState, SetOpState::pergroup, PlanState::plan, SetOpState::ps, PlanState::ps_ResultTupleSlot, set_output_count(), SetOpState::setop_done, SetOpState::tempContext, and TupIsNull.

Referenced by ExecSetOp().

{
    SetOp      *node = (SetOp *) setopstate->ps.plan;
    PlanState  *outerPlan;
    SetOpStatePerGroup pergroup;
    TupleTableSlot *outerslot;
    TupleTableSlot *resultTupleSlot;

    /*
     * get state info from node
     */
    outerPlan = outerPlanState(setopstate);
    pergroup = setopstate->pergroup;
    resultTupleSlot = setopstate->ps.ps_ResultTupleSlot;

    /*
     * We loop retrieving groups until we find one we should return
     */
    while (!setopstate->setop_done)
    {
        /*
         * If we don't already have the first tuple of the new group, fetch it
         * from the outer plan.
         */
        if (setopstate->grp_firstTuple == NULL)
        {
            outerslot = ExecProcNode(outerPlan);
            if (!TupIsNull(outerslot))
            {
                /* Make a copy of the first input tuple */
                setopstate->grp_firstTuple = ExecCopySlotTuple(outerslot);
            }
            else
            {
                /* outer plan produced no tuples at all */
                setopstate->setop_done = true;
                return NULL;
            }
        }

        /*
         * Store the copied first input tuple in the tuple table slot reserved
         * for it.  The tuple will be deleted when it is cleared from the
         * slot.
         */
        ExecStoreTuple(setopstate->grp_firstTuple,
                       resultTupleSlot,
                       InvalidBuffer,
                       true);
        setopstate->grp_firstTuple = NULL;      /* don't keep two pointers */

        /* Initialize working state for a new input tuple group */
        initialize_counts(pergroup);

        /* Count the first input tuple */
        advance_counts(pergroup,
                       fetch_tuple_flag(setopstate, resultTupleSlot));

        /*
         * Scan the outer plan until we exhaust it or cross a group boundary.
         */
        for (;;)
        {
            outerslot = ExecProcNode(outerPlan);
            if (TupIsNull(outerslot))
            {
                /* no more outer-plan tuples available */
                setopstate->setop_done = true;
                break;
            }

            /*
             * Check whether we've crossed a group boundary.
             */
            if (!execTuplesMatch(resultTupleSlot,
                                 outerslot,
                                 node->numCols, node->dupColIdx,
                                 setopstate->eqfunctions,
                                 setopstate->tempContext))
            {
                /*
                 * Save the first input tuple of the next group.
                 */
                setopstate->grp_firstTuple = ExecCopySlotTuple(outerslot);
                break;
            }

            /* Still in same group, so count this tuple */
            advance_counts(pergroup,
                           fetch_tuple_flag(setopstate, outerslot));
        }

        /*
         * Done scanning input tuple group.  See if we should emit any copies
         * of result tuple, and if so return the first copy.
         */
        set_output_count(setopstate, pergroup);

        if (setopstate->numOutput > 0)
        {
            setopstate->numOutput--;
            return resultTupleSlot;
        }
    }

    /* No more groups */
    ExecClearTuple(resultTupleSlot);
    return NULL;
}

static TupleTableSlot * setop_retrieve_hash_table ( SetOpState setopstate  )  [static]

Definition at line 423 of file nodeSetOp.c.

References ExecClearTuple(), ExecStoreMinimalTuple(), TupleHashEntryData::firstTuple, SetOpState::hashiter, NULL, SetOpState::numOutput, SetOpHashEntryData::pergroup, SetOpState::ps, PlanState::ps_ResultTupleSlot, ScanTupleHashTable, set_output_count(), SetOpState::setop_done, and SetOpHashEntryData::shared.

Referenced by ExecSetOp().

{
    SetOpHashEntry entry;
    TupleTableSlot *resultTupleSlot;

    /*
     * get state info from node
     */
    resultTupleSlot = setopstate->ps.ps_ResultTupleSlot;

    /*
     * We loop retrieving groups until we find one we should return
     */
    while (!setopstate->setop_done)
    {
        /*
         * Find the next entry in the hash table
         */
        entry = (SetOpHashEntry) ScanTupleHashTable(&setopstate->hashiter);
        if (entry == NULL)
        {
            /* No more entries in hashtable, so done */
            setopstate->setop_done = true;
            return NULL;
        }

        /*
         * See if we should emit any copies of this tuple, and if so return
         * the first copy.
         */
        set_output_count(setopstate, &entry->pergroup);

        if (setopstate->numOutput > 0)
        {
            setopstate->numOutput--;
            return ExecStoreMinimalTuple(entry->shared.firstTuple,
                                         resultTupleSlot,
                                         false);
        }
    }

    /* No more groups */
    ExecClearTuple(resultTupleSlot);
    return NULL;
}