#include "postgres.h"#include "access/htup_details.h"#include "executor/executor.h"#include "executor/nodeSetOp.h"#include "utils/memutils.h"
Go to the source code of this file.
Data Structures | |
| struct | SetOpStatePerGroupData |
| struct | SetOpHashEntryData |
Typedefs | |
| typedef struct SetOpStatePerGroupData | SetOpStatePerGroupData |
| typedef struct SetOpHashEntryData * | SetOpHashEntry |
| typedef struct SetOpHashEntryData | SetOpHashEntryData |
Functions | |
| static TupleTableSlot * | setop_retrieve_direct (SetOpState *setopstate) |
| static void | setop_fill_hash_table (SetOpState *setopstate) |
| static TupleTableSlot * | setop_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) |
| TupleTableSlot * | ExecSetOp (SetOpState *node) |
| SetOpState * | ExecInitSetOp (SetOp *node, EState *estate, int eflags) |
| void | ExecEndSetOp (SetOpState *node) |
| void | ExecReScanSetOp (SetOpState *node) |
| typedef struct SetOpHashEntryData* SetOpHashEntry |
Definition at line 74 of file nodeSetOp.c.
| typedef struct SetOpHashEntryData SetOpHashEntryData |
| typedef struct SetOpStatePerGroupData SetOpStatePerGroupData |
| 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().
| static void build_hash_table | ( | SetOpState * | setopstate | ) | [static] |
Definition at line 132 of file nodeSetOp.c.
References Assert, BuildTupleHashTable(), SetOp::dupColIdx, SetOpState::eqfunctions, SetOpState::hashfunctions, SetOpState::hashtable, SetOp::numCols, SetOp::numGroups, PlanState::plan, SetOpState::ps, SETOP_HASHED, SetOp::strategy, SetOpState::tableContext, and SetOpState::tempContext.
Referenced by ExecInitSetOp(), and ExecReScanSetOp().
{
SetOp *node = (SetOp *) setopstate->ps.plan;
Assert(node->strategy == SETOP_HASHED);
Assert(node->numGroups > 0);
setopstate->hashtable = BuildTupleHashTable(node->numCols,
node->dupColIdx,
setopstate->eqfunctions,
setopstate->hashfunctions,
node->numGroups,
sizeof(SetOpHashEntryData),
setopstate->tableContext,
setopstate->tempContext);
}
| 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().
| 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;
}
1.7.1