You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
postgres/src/backend/executor/nodeRecursiveunion.c

226 lines
6.1 KiB

/*-------------------------------------------------------------------------
*
* nodeRecursiveunion.c
* routines to handle RecursiveUnion nodes.
*
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeRecursiveunion.c,v 1.1 2008/10/04 21:56:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "executor/execdebug.h"
#include "executor/nodeRecursiveunion.h"
#include "miscadmin.h"
/* ----------------------------------------------------------------
* ExecRecursiveUnion(node)
*
* Scans the recursive query sequentially and returns the next
* qualifying tuple.
*
* 1. evaluate non recursive term and assign the result to RT
*
* 2. execute recursive terms
*
* 2.1 WT := RT
* 2.2 while WT is not empty repeat 2.3 to 2.6. if WT is empty returns RT
* 2.3 replace the name of recursive term with WT
* 2.4 evaluate the recursive term and store into WT
* 2.5 append WT to RT
* 2.6 go back to 2.2
* ----------------------------------------------------------------
*/
TupleTableSlot *
ExecRecursiveUnion(RecursiveUnionState *node)
{
PlanState *outerPlan = outerPlanState(node);
PlanState *innerPlan = innerPlanState(node);
RecursiveUnion *plan = (RecursiveUnion *) node->ps.plan;
TupleTableSlot *slot;
/* 1. Evaluate non-recursive term */
if (!node->recursing)
{
slot = ExecProcNode(outerPlan);
if (!TupIsNull(slot))
{
tuplestore_puttupleslot(node->working_table, slot);
return slot;
}
node->recursing = true;
}
retry:
/* 2. Execute recursive term */
slot = ExecProcNode(innerPlan);
if (TupIsNull(slot))
{
if (node->intermediate_empty)
return NULL;
/* 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;
/* and reset the inner plan */
innerPlan->chgParam = bms_add_member(innerPlan->chgParam,
plan->wtParam);
goto retry;
}
else
{
node->intermediate_empty = false;
tuplestore_puttupleslot(node->intermediate_table, slot);
}
return slot;
}
/* ----------------------------------------------------------------
* ExecInitRecursiveUnionScan
* ----------------------------------------------------------------
*/
RecursiveUnionState *
ExecInitRecursiveUnion(RecursiveUnion *node, EState *estate, int eflags)
{
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;
/* 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);
/*
* 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);
#define RECURSIVEUNION_NSLOTS 1
/*
* 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);
return rustate;
}
int
ExecCountSlotsRecursiveUnion(RecursiveUnion *node)
{
return ExecCountSlotsNode(outerPlan(node)) +
ExecCountSlotsNode(innerPlan(node)) +
RECURSIVEUNION_NSLOTS;
}
/* ----------------------------------------------------------------
* ExecEndRecursiveUnionScan
*
* frees any storage allocated through C routines.
* ----------------------------------------------------------------
*/
void
ExecEndRecursiveUnion(RecursiveUnionState *node)
{
/* Release tuplestores */
tuplestore_end(node->working_table);
tuplestore_end(node->intermediate_table);
/*
* clean out the upper tuple table
*/
ExecClearTuple(node->ps.ps_ResultTupleSlot);
/*
* close down subplans
*/
ExecEndNode(outerPlanState(node));
ExecEndNode(innerPlanState(node));
}
/* ----------------------------------------------------------------
* ExecRecursiveUnionReScan
*
* Rescans the relation.
* ----------------------------------------------------------------
*/
void
ExecRecursiveUnionReScan(RecursiveUnionState *node, ExprContext *exprCtxt)
{
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, exprCtxt);
/* reset processing state */
node->recursing = false;
node->intermediate_empty = true;
tuplestore_clear(node->working_table);
tuplestore_clear(node->intermediate_table);
}