mirror of
https://github.com/postgres/postgres.git
synced 2025-06-13 07:41:39 +03:00
Phase 1 of read-only-plans project: cause executor state nodes to point
to plan nodes, not vice-versa. All executor state nodes now inherit from struct PlanState. Copying of plan trees has been simplified by not storing a list of SubPlans in Plan nodes (eliminating duplicate links). The executor still needs such a list, but it can build it during ExecutorStart since it has to scan the plan tree anyway. No initdb forced since no stored-on-disk structures changed, but you will need a full recompile because of node-numbering changes.
This commit is contained in:
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeLimit.c,v 1.11 2002/11/22 22:10:01 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeLimit.c,v 1.12 2002/12/05 15:50:33 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -24,7 +24,7 @@
|
||||
#include "executor/executor.h"
|
||||
#include "executor/nodeLimit.h"
|
||||
|
||||
static void recompute_limits(Limit *node);
|
||||
static void recompute_limits(LimitState *node);
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
@ -35,26 +35,24 @@ static void recompute_limits(Limit *node);
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleTableSlot * /* return: a tuple or NULL */
|
||||
ExecLimit(Limit *node)
|
||||
ExecLimit(LimitState *node)
|
||||
{
|
||||
LimitState *limitstate;
|
||||
ScanDirection direction;
|
||||
TupleTableSlot *resultTupleSlot;
|
||||
TupleTableSlot *slot;
|
||||
Plan *outerPlan;
|
||||
PlanState *outerPlan;
|
||||
|
||||
/*
|
||||
* get information from the node
|
||||
*/
|
||||
limitstate = node->limitstate;
|
||||
direction = node->plan.state->es_direction;
|
||||
outerPlan = outerPlan((Plan *) node);
|
||||
resultTupleSlot = limitstate->cstate.cs_ResultTupleSlot;
|
||||
direction = node->ps.state->es_direction;
|
||||
outerPlan = outerPlanState(node);
|
||||
resultTupleSlot = node->ps.ps_ResultTupleSlot;
|
||||
|
||||
/*
|
||||
* The main logic is a simple state machine.
|
||||
*/
|
||||
switch (limitstate->lstate)
|
||||
switch (node->lstate)
|
||||
{
|
||||
case LIMIT_INITIAL:
|
||||
/*
|
||||
@ -71,9 +69,9 @@ ExecLimit(Limit *node)
|
||||
/*
|
||||
* Check for empty window; if so, treat like empty subplan.
|
||||
*/
|
||||
if (limitstate->count <= 0 && !limitstate->noCount)
|
||||
if (node->count <= 0 && !node->noCount)
|
||||
{
|
||||
limitstate->lstate = LIMIT_EMPTY;
|
||||
node->lstate = LIMIT_EMPTY;
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
@ -81,24 +79,24 @@ ExecLimit(Limit *node)
|
||||
*/
|
||||
for (;;)
|
||||
{
|
||||
slot = ExecProcNode(outerPlan, (Plan *) node);
|
||||
slot = ExecProcNode(outerPlan);
|
||||
if (TupIsNull(slot))
|
||||
{
|
||||
/*
|
||||
* The subplan returns too few tuples for us to produce
|
||||
* any output at all.
|
||||
*/
|
||||
limitstate->lstate = LIMIT_EMPTY;
|
||||
node->lstate = LIMIT_EMPTY;
|
||||
return NULL;
|
||||
}
|
||||
limitstate->subSlot = slot;
|
||||
if (++limitstate->position > limitstate->offset)
|
||||
node->subSlot = slot;
|
||||
if (++node->position > node->offset)
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Okay, we have the first tuple of the window.
|
||||
*/
|
||||
limitstate->lstate = LIMIT_INWINDOW;
|
||||
node->lstate = LIMIT_INWINDOW;
|
||||
break;
|
||||
|
||||
case LIMIT_EMPTY:
|
||||
@ -117,23 +115,23 @@ ExecLimit(Limit *node)
|
||||
* advancing the subplan or the position variable; but
|
||||
* change the state machine state to record having done so.
|
||||
*/
|
||||
if (!limitstate->noCount &&
|
||||
limitstate->position >= limitstate->offset + limitstate->count)
|
||||
if (!node->noCount &&
|
||||
node->position >= node->offset + node->count)
|
||||
{
|
||||
limitstate->lstate = LIMIT_WINDOWEND;
|
||||
node->lstate = LIMIT_WINDOWEND;
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
* Get next tuple from subplan, if any.
|
||||
*/
|
||||
slot = ExecProcNode(outerPlan, (Plan *) node);
|
||||
slot = ExecProcNode(outerPlan);
|
||||
if (TupIsNull(slot))
|
||||
{
|
||||
limitstate->lstate = LIMIT_SUBPLANEOF;
|
||||
node->lstate = LIMIT_SUBPLANEOF;
|
||||
return NULL;
|
||||
}
|
||||
limitstate->subSlot = slot;
|
||||
limitstate->position++;
|
||||
node->subSlot = slot;
|
||||
node->position++;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -141,19 +139,19 @@ ExecLimit(Limit *node)
|
||||
* Backwards scan, so check for stepping off start of window.
|
||||
* As above, change only state-machine status if so.
|
||||
*/
|
||||
if (limitstate->position <= limitstate->offset + 1)
|
||||
if (node->position <= node->offset + 1)
|
||||
{
|
||||
limitstate->lstate = LIMIT_WINDOWSTART;
|
||||
node->lstate = LIMIT_WINDOWSTART;
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
* Get previous tuple from subplan; there should be one!
|
||||
*/
|
||||
slot = ExecProcNode(outerPlan, (Plan *) node);
|
||||
slot = ExecProcNode(outerPlan);
|
||||
if (TupIsNull(slot))
|
||||
elog(ERROR, "ExecLimit: subplan failed to run backwards");
|
||||
limitstate->subSlot = slot;
|
||||
limitstate->position--;
|
||||
node->subSlot = slot;
|
||||
node->position--;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -164,11 +162,11 @@ ExecLimit(Limit *node)
|
||||
* Backing up from subplan EOF, so re-fetch previous tuple;
|
||||
* there should be one! Note previous tuple must be in window.
|
||||
*/
|
||||
slot = ExecProcNode(outerPlan, (Plan *) node);
|
||||
slot = ExecProcNode(outerPlan);
|
||||
if (TupIsNull(slot))
|
||||
elog(ERROR, "ExecLimit: subplan failed to run backwards");
|
||||
limitstate->subSlot = slot;
|
||||
limitstate->lstate = LIMIT_INWINDOW;
|
||||
node->subSlot = slot;
|
||||
node->lstate = LIMIT_INWINDOW;
|
||||
/* position does not change 'cause we didn't advance it before */
|
||||
break;
|
||||
|
||||
@ -179,8 +177,8 @@ ExecLimit(Limit *node)
|
||||
* Backing up from window end: simply re-return the last
|
||||
* tuple fetched from the subplan.
|
||||
*/
|
||||
slot = limitstate->subSlot;
|
||||
limitstate->lstate = LIMIT_INWINDOW;
|
||||
slot = node->subSlot;
|
||||
node->lstate = LIMIT_INWINDOW;
|
||||
/* position does not change 'cause we didn't advance it before */
|
||||
break;
|
||||
|
||||
@ -191,14 +189,14 @@ ExecLimit(Limit *node)
|
||||
* Advancing after having backed off window start: simply
|
||||
* re-return the last tuple fetched from the subplan.
|
||||
*/
|
||||
slot = limitstate->subSlot;
|
||||
limitstate->lstate = LIMIT_INWINDOW;
|
||||
slot = node->subSlot;
|
||||
node->lstate = LIMIT_INWINDOW;
|
||||
/* position does not change 'cause we didn't change it before */
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "ExecLimit: impossible state %d",
|
||||
(int) limitstate->lstate);
|
||||
(int) node->lstate);
|
||||
slot = NULL; /* keep compiler quiet */
|
||||
break;
|
||||
}
|
||||
@ -220,55 +218,54 @@ ExecLimit(Limit *node)
|
||||
* This is also a handy place to reset the current-position state info.
|
||||
*/
|
||||
static void
|
||||
recompute_limits(Limit *node)
|
||||
recompute_limits(LimitState *node)
|
||||
{
|
||||
LimitState *limitstate = node->limitstate;
|
||||
ExprContext *econtext = limitstate->cstate.cs_ExprContext;
|
||||
ExprContext *econtext = node->ps.ps_ExprContext;
|
||||
bool isNull;
|
||||
|
||||
if (node->limitOffset)
|
||||
{
|
||||
limitstate->offset =
|
||||
node->offset =
|
||||
DatumGetInt32(ExecEvalExprSwitchContext(node->limitOffset,
|
||||
econtext,
|
||||
&isNull,
|
||||
NULL));
|
||||
/* Interpret NULL offset as no offset */
|
||||
if (isNull)
|
||||
limitstate->offset = 0;
|
||||
else if (limitstate->offset < 0)
|
||||
limitstate->offset = 0;
|
||||
node->offset = 0;
|
||||
else if (node->offset < 0)
|
||||
node->offset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No OFFSET supplied */
|
||||
limitstate->offset = 0;
|
||||
node->offset = 0;
|
||||
}
|
||||
|
||||
if (node->limitCount)
|
||||
{
|
||||
limitstate->noCount = false;
|
||||
limitstate->count =
|
||||
node->noCount = false;
|
||||
node->count =
|
||||
DatumGetInt32(ExecEvalExprSwitchContext(node->limitCount,
|
||||
econtext,
|
||||
&isNull,
|
||||
NULL));
|
||||
/* Interpret NULL count as no count (LIMIT ALL) */
|
||||
if (isNull)
|
||||
limitstate->noCount = true;
|
||||
else if (limitstate->count < 0)
|
||||
limitstate->count = 0;
|
||||
node->noCount = true;
|
||||
else if (node->count < 0)
|
||||
node->count = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No COUNT supplied */
|
||||
limitstate->count = 0;
|
||||
limitstate->noCount = true;
|
||||
node->count = 0;
|
||||
node->noCount = true;
|
||||
}
|
||||
|
||||
/* Reset position to start-of-scan */
|
||||
limitstate->position = 0;
|
||||
limitstate->subSlot = NULL;
|
||||
node->position = 0;
|
||||
node->subSlot = NULL;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
@ -278,22 +275,19 @@ recompute_limits(Limit *node)
|
||||
* the node's subplan.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
bool /* return: initialization status */
|
||||
ExecInitLimit(Limit *node, EState *estate, Plan *parent)
|
||||
LimitState *
|
||||
ExecInitLimit(Limit *node, EState *estate)
|
||||
{
|
||||
LimitState *limitstate;
|
||||
Plan *outerPlan;
|
||||
|
||||
/*
|
||||
* assign execution state to node
|
||||
*/
|
||||
node->plan.state = estate;
|
||||
|
||||
/*
|
||||
* create new LimitState for node
|
||||
* create state structure
|
||||
*/
|
||||
limitstate = makeNode(LimitState);
|
||||
node->limitstate = limitstate;
|
||||
limitstate->ps.plan = (Plan *) node;
|
||||
limitstate->ps.state = estate;
|
||||
|
||||
limitstate->lstate = LIMIT_INITIAL;
|
||||
|
||||
/*
|
||||
@ -302,29 +296,37 @@ ExecInitLimit(Limit *node, EState *estate, Plan *parent)
|
||||
* Limit nodes never call ExecQual or ExecProject, but they need an
|
||||
* exprcontext anyway to evaluate the limit/offset parameters in.
|
||||
*/
|
||||
ExecAssignExprContext(estate, &limitstate->cstate);
|
||||
ExecAssignExprContext(estate, &limitstate->ps);
|
||||
|
||||
/*
|
||||
* initialize child expressions
|
||||
*/
|
||||
limitstate->limitOffset = ExecInitExpr(node->limitOffset,
|
||||
(PlanState *) limitstate);
|
||||
limitstate->limitCount = ExecInitExpr(node->limitCount,
|
||||
(PlanState *) limitstate);
|
||||
|
||||
#define LIMIT_NSLOTS 1
|
||||
|
||||
/*
|
||||
* Tuple table initialization
|
||||
*/
|
||||
ExecInitResultTupleSlot(estate, &limitstate->cstate);
|
||||
ExecInitResultTupleSlot(estate, &limitstate->ps);
|
||||
|
||||
/*
|
||||
* then initialize outer plan
|
||||
*/
|
||||
outerPlan = outerPlan((Plan *) node);
|
||||
ExecInitNode(outerPlan, estate, (Plan *) node);
|
||||
outerPlan = outerPlan(node);
|
||||
outerPlanState(limitstate) = ExecInitNode(outerPlan, estate);
|
||||
|
||||
/*
|
||||
* limit nodes do no projections, so initialize projection info for
|
||||
* this node appropriately
|
||||
*/
|
||||
ExecAssignResultTypeFromOuterPlan((Plan *) node, &limitstate->cstate);
|
||||
limitstate->cstate.cs_ProjInfo = NULL;
|
||||
ExecAssignResultTypeFromOuterPlan(&limitstate->ps);
|
||||
limitstate->ps.ps_ProjInfo = NULL;
|
||||
|
||||
return TRUE;
|
||||
return limitstate;
|
||||
}
|
||||
|
||||
int
|
||||
@ -343,33 +345,29 @@ ExecCountSlotsLimit(Limit *node)
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecEndLimit(Limit *node)
|
||||
ExecEndLimit(LimitState *node)
|
||||
{
|
||||
LimitState *limitstate = node->limitstate;
|
||||
ExecFreeExprContext(&node->ps);
|
||||
|
||||
ExecFreeExprContext(&limitstate->cstate);
|
||||
|
||||
ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
|
||||
ExecEndNode(outerPlanState(node));
|
||||
|
||||
/* clean up tuple table */
|
||||
ExecClearTuple(limitstate->cstate.cs_ResultTupleSlot);
|
||||
ExecClearTuple(node->ps.ps_ResultTupleSlot);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ExecReScanLimit(Limit *node, ExprContext *exprCtxt, Plan *parent)
|
||||
ExecReScanLimit(LimitState *node, ExprContext *exprCtxt)
|
||||
{
|
||||
LimitState *limitstate = node->limitstate;
|
||||
|
||||
/* resetting lstate will force offset/limit recalculation */
|
||||
limitstate->lstate = LIMIT_INITIAL;
|
||||
node->lstate = LIMIT_INITIAL;
|
||||
|
||||
ExecClearTuple(limitstate->cstate.cs_ResultTupleSlot);
|
||||
ExecClearTuple(node->ps.ps_ResultTupleSlot);
|
||||
|
||||
/*
|
||||
* if chgParam of subnode is not null then plan will be re-scanned by
|
||||
* first ExecProcNode.
|
||||
*/
|
||||
if (((Plan *) node)->lefttree->chgParam == NULL)
|
||||
ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
|
||||
if (((PlanState *) node)->lefttree->chgParam == NULL)
|
||||
ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
|
||||
}
|
||||
|
Reference in New Issue
Block a user