#include "postgres.h"
#include "executor/execdebug.h"
#include "executor/nodeRecursiveunion.h"
#include "miscadmin.h"
#include "utils/memutils.h"
Go to the source code of this file.
Data Structures | |
struct | RUHashEntryData |
Typedefs | |
typedef struct RUHashEntryData * | RUHashEntry |
typedef struct RUHashEntryData | RUHashEntryData |
Functions | |
static void | build_hash_table (RecursiveUnionState *rustate) |
TupleTableSlot * | ExecRecursiveUnion (RecursiveUnionState *node) |
RecursiveUnionState * | ExecInitRecursiveUnion (RecursiveUnion *node, EState *estate, int eflags) |
void | ExecEndRecursiveUnion (RecursiveUnionState *node) |
void | ExecReScanRecursiveUnion (RecursiveUnionState *node) |
typedef struct RUHashEntryData* RUHashEntry |
Definition at line 27 of file nodeRecursiveunion.c.
typedef struct RUHashEntryData RUHashEntryData |
static void build_hash_table | ( | RecursiveUnionState * | rustate | ) | [static] |
Definition at line 39 of file nodeRecursiveunion.c.
References Assert, BuildTupleHashTable(), RecursiveUnion::dupColIdx, RecursiveUnionState::eqfunctions, RecursiveUnionState::hashfunctions, RecursiveUnionState::hashtable, RecursiveUnion::numCols, RecursiveUnion::numGroups, PlanState::plan, RecursiveUnionState::ps, RecursiveUnionState::tableContext, and RecursiveUnionState::tempContext.
Referenced by ExecInitRecursiveUnion(), and ExecReScanRecursiveUnion().
{ RecursiveUnion *node = (RecursiveUnion *) rustate->ps.plan; Assert(node->numCols > 0); Assert(node->numGroups > 0); rustate->hashtable = BuildTupleHashTable(node->numCols, node->dupColIdx, rustate->eqfunctions, rustate->hashfunctions, node->numGroups, sizeof(RUHashEntryData), rustate->tableContext, rustate->tempContext); }
void ExecEndRecursiveUnion | ( | RecursiveUnionState * | node | ) |
Definition at line 274 of file nodeRecursiveunion.c.
References ExecClearTuple(), ExecEndNode(), innerPlanState, RecursiveUnionState::intermediate_table, MemoryContextDelete(), outerPlanState, RecursiveUnionState::ps, PlanState::ps_ResultTupleSlot, RecursiveUnionState::tableContext, RecursiveUnionState::tempContext, tuplestore_end(), and RecursiveUnionState::working_table.
Referenced by ExecEndNode().
{ /* Release tuplestores */ tuplestore_end(node->working_table); tuplestore_end(node->intermediate_table); /* free subsidiary stuff including hashtable */ if (node->tempContext) MemoryContextDelete(node->tempContext); if (node->tableContext) MemoryContextDelete(node->tableContext); /* * clean out the upper tuple table */ ExecClearTuple(node->ps.ps_ResultTupleSlot); /* * close down subplans */ ExecEndNode(outerPlanState(node)); ExecEndNode(innerPlanState(node)); }
RecursiveUnionState* ExecInitRecursiveUnion | ( | RecursiveUnion * | node, | |
EState * | estate, | |||
int | eflags | |||
) |
Definition at line 165 of file nodeRecursiveunion.c.
References ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), Assert, build_hash_table(), CurrentMemoryContext, RecursiveUnion::dupOperators, RecursiveUnionState::eqfunctions, EState::es_param_exec_vals, EXEC_FLAG_BACKWARD, EXEC_FLAG_MARK, ExecAssignResultTypeFromTL(), ExecInitNode(), ExecInitResultTupleSlot(), ParamExecData::execPlan, execTuplesHashPrepare(), RecursiveUnionState::hashfunctions, RecursiveUnionState::hashtable, innerPlan, innerPlanState, RecursiveUnionState::intermediate_empty, RecursiveUnionState::intermediate_table, ParamExecData::isnull, makeNode, NIL, NULL, RecursiveUnion::numCols, outerPlan, outerPlanState, RecursiveUnion::plan, PlanState::plan, PointerGetDatum, RecursiveUnionState::ps, PlanState::ps_ProjInfo, Plan::qual, RecursiveUnionState::recursing, PlanState::state, RecursiveUnionState::tableContext, RecursiveUnionState::tempContext, tuplestore_begin_heap(), ParamExecData::value, work_mem, RecursiveUnionState::working_table, and RecursiveUnion::wtParam.
Referenced by ExecInitNode().
{ RecursiveUnionState *rustate; ParamExecData *prmdata; /* check for unsupported flags */ Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK))); /* * create state structure */ rustate = makeNode(RecursiveUnionState); rustate->ps.plan = (Plan *) node; rustate->ps.state = estate; rustate->eqfunctions = NULL; rustate->hashfunctions = NULL; rustate->hashtable = NULL; rustate->tempContext = NULL; rustate->tableContext = NULL; /* initialize processing state */ rustate->recursing = false; rustate->intermediate_empty = true; rustate->working_table = tuplestore_begin_heap(false, false, work_mem); rustate->intermediate_table = tuplestore_begin_heap(false, false, work_mem); /* * If hashing, we need a per-tuple memory context for comparisons, and 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 when rescanning. */ if (node->numCols > 0) { rustate->tempContext = AllocSetContextCreate(CurrentMemoryContext, "RecursiveUnion", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); rustate->tableContext = AllocSetContextCreate(CurrentMemoryContext, "RecursiveUnion hash table", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); } /* * Make the state structure available to descendant WorkTableScan nodes * via the Param slot reserved for it. */ prmdata = &(estate->es_param_exec_vals[node->wtParam]); Assert(prmdata->execPlan == NULL); prmdata->value = PointerGetDatum(rustate); prmdata->isnull = false; /* * Miscellaneous initialization * * RecursiveUnion plans don't have expression contexts because they never * call ExecQual or ExecProject. */ Assert(node->plan.qual == NIL); /* * RecursiveUnion nodes still have Result slots, which hold pointers to * tuples, so we have to initialize them. */ ExecInitResultTupleSlot(estate, &rustate->ps); /* * Initialize result tuple type and projection info. (Note: we have to * set up the result type before initializing child nodes, because * nodeWorktablescan.c expects it to be valid.) */ ExecAssignResultTypeFromTL(&rustate->ps); rustate->ps.ps_ProjInfo = NULL; /* * initialize child nodes */ outerPlanState(rustate) = ExecInitNode(outerPlan(node), estate, eflags); innerPlanState(rustate) = ExecInitNode(innerPlan(node), estate, eflags); /* * If hashing, precompute fmgr lookup data for inner loop, and create the * hash table. */ if (node->numCols > 0) { execTuplesHashPrepare(node->numCols, node->dupOperators, &rustate->eqfunctions, &rustate->hashfunctions); build_hash_table(rustate); } return rustate; }
TupleTableSlot* ExecRecursiveUnion | ( | RecursiveUnionState * | node | ) |
Definition at line 76 of file nodeRecursiveunion.c.
References bms_add_member(), PlanState::chgParam, ExecProcNode(), RecursiveUnionState::hashtable, innerPlan, innerPlanState, RecursiveUnionState::intermediate_empty, RecursiveUnionState::intermediate_table, LookupTupleHashEntry(), MemoryContextReset(), RecursiveUnion::numCols, outerPlan, outerPlanState, PlanState::plan, RecursiveUnionState::ps, RecursiveUnionState::recursing, RecursiveUnionState::tempContext, TupIsNull, tuplestore_begin_heap(), tuplestore_end(), tuplestore_puttupleslot(), work_mem, RecursiveUnionState::working_table, and RecursiveUnion::wtParam.
Referenced by ExecProcNode().
{ PlanState *outerPlan = outerPlanState(node); PlanState *innerPlan = innerPlanState(node); RecursiveUnion *plan = (RecursiveUnion *) node->ps.plan; TupleTableSlot *slot; bool isnew; /* 1. Evaluate non-recursive term */ if (!node->recursing) { for (;;) { slot = ExecProcNode(outerPlan); if (TupIsNull(slot)) break; if (plan->numCols > 0) { /* Find or build hashtable entry for this tuple's group */ LookupTupleHashEntry(node->hashtable, slot, &isnew); /* Must reset temp context after each hashtable lookup */ MemoryContextReset(node->tempContext); /* Ignore tuple if already seen */ if (!isnew) continue; } /* Each non-duplicate tuple goes to the working table ... */ tuplestore_puttupleslot(node->working_table, slot); /* ... and to the caller */ return slot; } node->recursing = true; } /* 2. Execute recursive term */ for (;;) { slot = ExecProcNode(innerPlan); if (TupIsNull(slot)) { /* Done if there's nothing in the intermediate table */ if (node->intermediate_empty) break; /* done with old working table ... */ tuplestore_end(node->working_table); /* intermediate table becomes working table */ node->working_table = node->intermediate_table; /* create new empty intermediate table */ node->intermediate_table = tuplestore_begin_heap(false, false, work_mem); node->intermediate_empty = true; /* reset the recursive term */ innerPlan->chgParam = bms_add_member(innerPlan->chgParam, plan->wtParam); /* and continue fetching from recursive term */ continue; } if (plan->numCols > 0) { /* Find or build hashtable entry for this tuple's group */ LookupTupleHashEntry(node->hashtable, slot, &isnew); /* Must reset temp context after each hashtable lookup */ MemoryContextReset(node->tempContext); /* Ignore tuple if already seen */ if (!isnew) continue; } /* Else, tuple is good; stash it in intermediate table ... */ node->intermediate_empty = false; tuplestore_puttupleslot(node->intermediate_table, slot); /* ... and return it */ return slot; } return NULL; }
void ExecReScanRecursiveUnion | ( | RecursiveUnionState * | node | ) |
Definition at line 305 of file nodeRecursiveunion.c.
References bms_add_member(), build_hash_table(), PlanState::chgParam, ExecReScan(), innerPlan, innerPlanState, RecursiveUnionState::intermediate_empty, RecursiveUnionState::intermediate_table, MemoryContextResetAndDeleteChildren(), NULL, RecursiveUnion::numCols, outerPlan, outerPlanState, PlanState::plan, RecursiveUnionState::ps, RecursiveUnionState::recursing, RecursiveUnionState::tableContext, tuplestore_clear(), RecursiveUnionState::working_table, and RecursiveUnion::wtParam.
Referenced by ExecReScan().
{ PlanState *outerPlan = outerPlanState(node); PlanState *innerPlan = innerPlanState(node); RecursiveUnion *plan = (RecursiveUnion *) node->ps.plan; /* * Set recursive term's chgParam to tell it that we'll modify the working * table and therefore it has to rescan. */ innerPlan->chgParam = bms_add_member(innerPlan->chgParam, plan->wtParam); /* * if chgParam of subnode is not null then plan will be re-scanned by * first ExecProcNode. Because of above, we only have to do this to the * non-recursive term. */ if (outerPlan->chgParam == NULL) ExecReScan(outerPlan); /* Release any hashtable storage */ if (node->tableContext) MemoryContextResetAndDeleteChildren(node->tableContext); /* And rebuild empty hashtable if needed */ if (plan->numCols > 0) build_hash_table(node); /* reset processing state */ node->recursing = false; node->intermediate_empty = true; tuplestore_clear(node->working_table); tuplestore_clear(node->intermediate_table); }