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