mirror of
https://github.com/postgres/postgres.git
synced 2025-07-03 20:02:46 +03:00
Re-implement LIMIT/OFFSET as a plan node type, instead of a hack in
ExecutorRun. This allows LIMIT to work in a view. Also, LIMIT in a cursor declaration will behave in a reasonable fashion, whereas before it was overridden by the FETCH count.
This commit is contained in:
@ -4,7 +4,7 @@
|
||||
# Makefile for executor
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.15 2000/10/05 19:11:26 tgl Exp $
|
||||
# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.16 2000/10/26 21:35:15 tgl Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@ -17,8 +17,8 @@ OBJS = execAmi.o execFlatten.o execJunk.o execMain.o \
|
||||
execUtils.o functions.o nodeAppend.o nodeAgg.o nodeHash.o \
|
||||
nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
|
||||
nodeNestloop.o nodeResult.o nodeSeqscan.o nodeSetOp.o nodeSort.o \
|
||||
nodeUnique.o nodeGroup.o spi.o nodeSubplan.o \
|
||||
nodeSubqueryscan.o nodeTidscan.o
|
||||
nodeUnique.o nodeLimit.o nodeGroup.o nodeSubplan.o \
|
||||
nodeSubqueryscan.o nodeTidscan.o spi.o
|
||||
|
||||
all: SUBSYS.o
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: execAmi.c,v 1.53 2000/10/05 19:11:26 tgl Exp $
|
||||
* $Id: execAmi.c,v 1.54 2000/10/26 21:35:15 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -37,6 +37,7 @@
|
||||
#include "executor/nodeHashjoin.h"
|
||||
#include "executor/nodeIndexscan.h"
|
||||
#include "executor/nodeTidscan.h"
|
||||
#include "executor/nodeLimit.h"
|
||||
#include "executor/nodeMaterial.h"
|
||||
#include "executor/nodeMergejoin.h"
|
||||
#include "executor/nodeNestloop.h"
|
||||
@ -350,6 +351,10 @@ ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
|
||||
ExecReScanSetOp((SetOp *) node, exprCtxt, parent);
|
||||
break;
|
||||
|
||||
case T_Limit:
|
||||
ExecReScanLimit((Limit *) node, exprCtxt, parent);
|
||||
break;
|
||||
|
||||
case T_Sort:
|
||||
ExecReScanSort((Sort *) node, exprCtxt, parent);
|
||||
break;
|
||||
|
@ -27,7 +27,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.130 2000/10/16 17:08:06 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.131 2000/10/26 21:35:15 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -52,11 +52,10 @@ static TupleDesc InitPlan(CmdType operation,
|
||||
EState *estate);
|
||||
static void EndPlan(Plan *plan, EState *estate);
|
||||
static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan,
|
||||
CmdType operation,
|
||||
int offsetTuples,
|
||||
int numberTuples,
|
||||
ScanDirection direction,
|
||||
DestReceiver *destfunc);
|
||||
CmdType operation,
|
||||
long numberTuples,
|
||||
ScanDirection direction,
|
||||
DestReceiver *destfunc);
|
||||
static void ExecRetrieve(TupleTableSlot *slot,
|
||||
DestReceiver *destfunc,
|
||||
EState *estate);
|
||||
@ -153,19 +152,18 @@ ExecutorStart(QueryDesc *queryDesc, EState *estate)
|
||||
* EXEC_RETONE: return one tuple but don't 'retrieve' it
|
||||
* used in postquel function processing
|
||||
*
|
||||
* Note: count = 0 is interpreted as "no limit".
|
||||
*
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleTableSlot *
|
||||
ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature,
|
||||
Node *limoffset, Node *limcount)
|
||||
ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, long count)
|
||||
{
|
||||
CmdType operation;
|
||||
Plan *plan;
|
||||
TupleTableSlot *result;
|
||||
CommandDest dest;
|
||||
DestReceiver *destfunc;
|
||||
int offset = 0;
|
||||
int count = 0;
|
||||
|
||||
/*
|
||||
* sanity checks
|
||||
@ -191,111 +189,21 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature,
|
||||
*/
|
||||
(*destfunc->setup) (destfunc, (TupleDesc) NULL);
|
||||
|
||||
/*
|
||||
* if given get the offset of the LIMIT clause
|
||||
*/
|
||||
if (limoffset != NULL)
|
||||
{
|
||||
Const *coffset;
|
||||
Param *poffset;
|
||||
ParamListInfo paramLI;
|
||||
int i;
|
||||
|
||||
switch (nodeTag(limoffset))
|
||||
{
|
||||
case T_Const:
|
||||
coffset = (Const *) limoffset;
|
||||
offset = (int) (coffset->constvalue);
|
||||
break;
|
||||
|
||||
case T_Param:
|
||||
poffset = (Param *) limoffset;
|
||||
paramLI = estate->es_param_list_info;
|
||||
|
||||
if (paramLI == NULL)
|
||||
elog(ERROR, "parameter for limit offset not in executor state");
|
||||
for (i = 0; paramLI[i].kind != PARAM_INVALID; i++)
|
||||
{
|
||||
if (paramLI[i].kind == PARAM_NUM && paramLI[i].id == poffset->paramid)
|
||||
break;
|
||||
}
|
||||
if (paramLI[i].kind == PARAM_INVALID)
|
||||
elog(ERROR, "parameter for limit offset not in executor state");
|
||||
if (paramLI[i].isnull)
|
||||
elog(ERROR, "limit offset cannot be NULL value");
|
||||
offset = (int) (paramLI[i].value);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "unexpected node type %d as limit offset", nodeTag(limoffset));
|
||||
}
|
||||
|
||||
if (offset < 0)
|
||||
elog(ERROR, "limit offset cannot be negative");
|
||||
}
|
||||
|
||||
/*
|
||||
* if given get the count of the LIMIT clause
|
||||
*/
|
||||
if (limcount != NULL)
|
||||
{
|
||||
Const *ccount;
|
||||
Param *pcount;
|
||||
ParamListInfo paramLI;
|
||||
int i;
|
||||
|
||||
switch (nodeTag(limcount))
|
||||
{
|
||||
case T_Const:
|
||||
ccount = (Const *) limcount;
|
||||
count = (int) (ccount->constvalue);
|
||||
break;
|
||||
|
||||
case T_Param:
|
||||
pcount = (Param *) limcount;
|
||||
paramLI = estate->es_param_list_info;
|
||||
|
||||
if (paramLI == NULL)
|
||||
elog(ERROR, "parameter for limit count not in executor state");
|
||||
for (i = 0; paramLI[i].kind != PARAM_INVALID; i++)
|
||||
{
|
||||
if (paramLI[i].kind == PARAM_NUM && paramLI[i].id == pcount->paramid)
|
||||
break;
|
||||
}
|
||||
if (paramLI[i].kind == PARAM_INVALID)
|
||||
elog(ERROR, "parameter for limit count not in executor state");
|
||||
if (paramLI[i].isnull)
|
||||
elog(ERROR, "limit count cannot be NULL value");
|
||||
count = (int) (paramLI[i].value);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "unexpected node type %d as limit count", nodeTag(limcount));
|
||||
}
|
||||
|
||||
if (count < 0)
|
||||
elog(ERROR, "limit count cannot be negative");
|
||||
}
|
||||
|
||||
switch (feature)
|
||||
{
|
||||
|
||||
case EXEC_RUN:
|
||||
result = ExecutePlan(estate,
|
||||
plan,
|
||||
operation,
|
||||
offset,
|
||||
count,
|
||||
ForwardScanDirection,
|
||||
destfunc);
|
||||
break;
|
||||
|
||||
case EXEC_FOR:
|
||||
result = ExecutePlan(estate,
|
||||
plan,
|
||||
operation,
|
||||
offset,
|
||||
count,
|
||||
ForwardScanDirection,
|
||||
destfunc);
|
||||
@ -308,7 +216,6 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature,
|
||||
result = ExecutePlan(estate,
|
||||
plan,
|
||||
operation,
|
||||
offset,
|
||||
count,
|
||||
BackwardScanDirection,
|
||||
destfunc);
|
||||
@ -322,14 +229,14 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature,
|
||||
result = ExecutePlan(estate,
|
||||
plan,
|
||||
operation,
|
||||
0,
|
||||
ONE_TUPLE,
|
||||
ForwardScanDirection,
|
||||
destfunc);
|
||||
break;
|
||||
|
||||
default:
|
||||
result = NULL;
|
||||
elog(DEBUG, "ExecutorRun: Unknown feature %d", feature);
|
||||
result = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -917,25 +824,22 @@ EndPlan(Plan *plan, EState *estate)
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecutePlan
|
||||
*
|
||||
* processes the query plan to retrieve 'tupleCount' tuples in the
|
||||
* processes the query plan to retrieve 'numberTuples' tuples in the
|
||||
* direction specified.
|
||||
* Retrieves all tuples if tupleCount is 0
|
||||
*
|
||||
* result is either a slot containing a tuple in the case
|
||||
* result is either a slot containing the last tuple in the case
|
||||
* of a RETRIEVE or NULL otherwise.
|
||||
*
|
||||
* Note: the ctid attribute is a 'junk' attribute that is removed before the
|
||||
* user can see it
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* the ctid attribute is a 'junk' attribute that is removed before the
|
||||
user can see it*/
|
||||
|
||||
static TupleTableSlot *
|
||||
ExecutePlan(EState *estate,
|
||||
Plan *plan,
|
||||
CmdType operation,
|
||||
int offsetTuples,
|
||||
int numberTuples,
|
||||
long numberTuples,
|
||||
ScanDirection direction,
|
||||
DestReceiver *destfunc)
|
||||
{
|
||||
@ -943,7 +847,7 @@ ExecutePlan(EState *estate,
|
||||
TupleTableSlot *slot;
|
||||
ItemPointer tupleid = NULL;
|
||||
ItemPointerData tuple_ctid;
|
||||
int current_tuple_count;
|
||||
long current_tuple_count;
|
||||
TupleTableSlot *result;
|
||||
|
||||
/*
|
||||
@ -990,17 +894,6 @@ lnext: ;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* For now we completely execute the plan and skip result tuples
|
||||
* if requested by LIMIT offset. Finally we should try to do it in
|
||||
* deeper levels if possible (during index scan) - Jan
|
||||
*/
|
||||
if (offsetTuples > 0)
|
||||
{
|
||||
--offsetTuples;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* if we have a junk filter, then project a new tuple with the
|
||||
* junk removed.
|
||||
@ -1152,10 +1045,10 @@ lnext: ;
|
||||
}
|
||||
|
||||
/*
|
||||
* check our tuple count.. if we've returned the proper number
|
||||
* then return, else loop again and process more tuples..
|
||||
* check our tuple count.. if we've processed the proper number
|
||||
* then quit, else loop again and process more tuples..
|
||||
*/
|
||||
current_tuple_count += 1;
|
||||
current_tuple_count++;
|
||||
if (numberTuples == current_tuple_count)
|
||||
break;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.21 2000/10/05 19:11:26 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.22 2000/10/26 21:35:15 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -83,6 +83,7 @@
|
||||
#include "executor/nodeHashjoin.h"
|
||||
#include "executor/nodeIndexscan.h"
|
||||
#include "executor/nodeTidscan.h"
|
||||
#include "executor/nodeLimit.h"
|
||||
#include "executor/nodeMaterial.h"
|
||||
#include "executor/nodeMergejoin.h"
|
||||
#include "executor/nodeNestloop.h"
|
||||
@ -204,6 +205,10 @@ ExecInitNode(Plan *node, EState *estate, Plan *parent)
|
||||
result = ExecInitSetOp((SetOp *) node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_Limit:
|
||||
result = ExecInitLimit((Limit *) node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_Group:
|
||||
result = ExecInitGroup((Group *) node, estate, parent);
|
||||
break;
|
||||
@ -331,6 +336,10 @@ ExecProcNode(Plan *node, Plan *parent)
|
||||
result = ExecSetOp((SetOp *) node);
|
||||
break;
|
||||
|
||||
case T_Limit:
|
||||
result = ExecLimit((Limit *) node);
|
||||
break;
|
||||
|
||||
case T_Group:
|
||||
result = ExecGroup((Group *) node);
|
||||
break;
|
||||
@ -413,6 +422,9 @@ ExecCountSlotsNode(Plan *node)
|
||||
case T_SetOp:
|
||||
return ExecCountSlotsSetOp((SetOp *) node);
|
||||
|
||||
case T_Limit:
|
||||
return ExecCountSlotsLimit((Limit *) node);
|
||||
|
||||
case T_Group:
|
||||
return ExecCountSlotsGroup((Group *) node);
|
||||
|
||||
@ -535,6 +547,10 @@ ExecEndNode(Plan *node, Plan *parent)
|
||||
ExecEndSetOp((SetOp *) node);
|
||||
break;
|
||||
|
||||
case T_Limit:
|
||||
ExecEndLimit((Limit *) node);
|
||||
break;
|
||||
|
||||
case T_Group:
|
||||
ExecEndGroup((Group *) node);
|
||||
break;
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.41 2000/10/05 19:11:26 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.42 2000/10/26 21:35:15 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -770,6 +770,14 @@ NodeGetResultTupleSlot(Plan *node)
|
||||
}
|
||||
break;
|
||||
|
||||
case T_Limit:
|
||||
{
|
||||
LimitState *limitstate = ((Limit *) node)->limitstate;
|
||||
|
||||
slot = limitstate->cstate.cs_ResultTupleSlot;
|
||||
}
|
||||
break;
|
||||
|
||||
case T_MergeJoin:
|
||||
{
|
||||
MergeJoinState *mergestate = ((MergeJoin *) node)->mergestate;
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.38 2000/08/24 03:29:03 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.39 2000/10/26 21:35:15 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -135,9 +135,6 @@ init_execution_state(char *src, Oid *argOidVect, int nargs)
|
||||
None);
|
||||
estate = CreateExecutorState();
|
||||
|
||||
if (queryTree->limitOffset != NULL || queryTree->limitCount != NULL)
|
||||
elog(ERROR, "LIMIT clause from SQL functions not yet implemented");
|
||||
|
||||
if (nargs > 0)
|
||||
{
|
||||
int i;
|
||||
@ -328,7 +325,7 @@ postquel_getnext(execution_state *es)
|
||||
|
||||
feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
|
||||
|
||||
return ExecutorRun(es->qd, es->estate, feature, (Node *) NULL, (Node *) NULL);
|
||||
return ExecutorRun(es->qd, es->estate, feature, 0L);
|
||||
}
|
||||
|
||||
static void
|
||||
|
324
src/backend/executor/nodeLimit.c
Normal file
324
src/backend/executor/nodeLimit.c
Normal file
@ -0,0 +1,324 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeLimit.c
|
||||
* Routines to handle limiting of query results where appropriate
|
||||
*
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeLimit.c,v 1.1 2000/10/26 21:35:15 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/*
|
||||
* INTERFACE ROUTINES
|
||||
* ExecLimit - extract a limited range of tuples
|
||||
* ExecInitLimit - initialize node and subnodes..
|
||||
* ExecEndLimit - shutdown node and subnodes
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "executor/executor.h"
|
||||
#include "executor/nodeLimit.h"
|
||||
|
||||
static void recompute_limits(Limit *node);
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecLimit
|
||||
*
|
||||
* This is a very simple node which just performs LIMIT/OFFSET
|
||||
* filtering on the stream of tuples returned by a subplan.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleTableSlot * /* return: a tuple or NULL */
|
||||
ExecLimit(Limit *node)
|
||||
{
|
||||
LimitState *limitstate;
|
||||
ScanDirection direction;
|
||||
TupleTableSlot *resultTupleSlot;
|
||||
TupleTableSlot *slot;
|
||||
Plan *outerPlan;
|
||||
long netlimit;
|
||||
|
||||
/* ----------------
|
||||
* get information from the node
|
||||
* ----------------
|
||||
*/
|
||||
limitstate = node->limitstate;
|
||||
direction = node->plan.state->es_direction;
|
||||
outerPlan = outerPlan((Plan *) node);
|
||||
resultTupleSlot = limitstate->cstate.cs_ResultTupleSlot;
|
||||
|
||||
/* ----------------
|
||||
* If first call for this scan, compute limit/offset.
|
||||
* (We can't do this any earlier, because parameters from upper nodes
|
||||
* may not be set until now.)
|
||||
* ----------------
|
||||
*/
|
||||
if (! limitstate->parmsSet)
|
||||
recompute_limits(node);
|
||||
netlimit = limitstate->offset + limitstate->count;
|
||||
|
||||
/* ----------------
|
||||
* now loop, returning only desired tuples.
|
||||
* ----------------
|
||||
*/
|
||||
for (;;)
|
||||
{
|
||||
/*----------------
|
||||
* If we have reached the subplan EOF or the limit, just quit.
|
||||
*
|
||||
* NOTE: when scanning forwards, we must fetch one tuple beyond the
|
||||
* COUNT limit before we can return NULL, else the subplan won't be
|
||||
* properly positioned to start going backwards. Hence test here
|
||||
* is for position > netlimit not position >= netlimit.
|
||||
*
|
||||
* Similarly, when scanning backwards, we must re-fetch the last
|
||||
* tuple in the offset region before we can return NULL. Otherwise
|
||||
* we won't be correctly aligned to start going forward again. So,
|
||||
* although you might think we can quit when position = offset + 1,
|
||||
* we have to fetch a subplan tuple first, and then exit when
|
||||
* position = offset.
|
||||
*----------------
|
||||
*/
|
||||
if (ScanDirectionIsForward(direction))
|
||||
{
|
||||
if (limitstate->atEnd)
|
||||
return NULL;
|
||||
if (! limitstate->noCount && limitstate->position > netlimit)
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (limitstate->position <= limitstate->offset)
|
||||
return NULL;
|
||||
}
|
||||
/* ----------------
|
||||
* fetch a tuple from the outer subplan
|
||||
* ----------------
|
||||
*/
|
||||
slot = ExecProcNode(outerPlan, (Plan *) node);
|
||||
if (TupIsNull(slot))
|
||||
{
|
||||
/*
|
||||
* We are at start or end of the subplan. Update local state
|
||||
* appropriately, but always return NULL.
|
||||
*/
|
||||
if (ScanDirectionIsForward(direction))
|
||||
{
|
||||
Assert(! limitstate->atEnd);
|
||||
/* must bump position to stay in sync for backwards fetch */
|
||||
limitstate->position++;
|
||||
limitstate->atEnd = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
limitstate->position = 0;
|
||||
limitstate->atEnd = false;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
* We got the next subplan tuple successfully, so adjust state.
|
||||
*/
|
||||
if (ScanDirectionIsForward(direction))
|
||||
limitstate->position++;
|
||||
else
|
||||
{
|
||||
limitstate->position--;
|
||||
Assert(limitstate->position > 0);
|
||||
}
|
||||
limitstate->atEnd = false;
|
||||
|
||||
/* ----------------
|
||||
* Now, is this a tuple we want? If not, loop around to fetch
|
||||
* another tuple from the subplan.
|
||||
* ----------------
|
||||
*/
|
||||
if (limitstate->position > limitstate->offset &&
|
||||
(limitstate->noCount || limitstate->position <= netlimit))
|
||||
break;
|
||||
}
|
||||
|
||||
ExecStoreTuple(slot->val,
|
||||
resultTupleSlot,
|
||||
InvalidBuffer,
|
||||
false); /* tuple does not belong to slot */
|
||||
|
||||
return resultTupleSlot;
|
||||
}
|
||||
|
||||
/*
|
||||
* Evaluate the limit/offset expressions --- done at start of each scan.
|
||||
*
|
||||
* This is also a handy place to reset the current-position state info.
|
||||
*/
|
||||
static void
|
||||
recompute_limits(Limit *node)
|
||||
{
|
||||
LimitState *limitstate = node->limitstate;
|
||||
ExprContext *econtext = limitstate->cstate.cs_ExprContext;
|
||||
bool isNull;
|
||||
|
||||
if (node->limitOffset)
|
||||
{
|
||||
limitstate->offset = DatumGetInt32(ExecEvalExpr(node->limitOffset,
|
||||
econtext,
|
||||
&isNull,
|
||||
NULL));
|
||||
/* Interpret NULL offset as no offset */
|
||||
if (isNull)
|
||||
limitstate->offset = 0;
|
||||
else if (limitstate->offset < 0)
|
||||
limitstate->offset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No OFFSET supplied */
|
||||
limitstate->offset = 0;
|
||||
}
|
||||
|
||||
if (node->limitCount)
|
||||
{
|
||||
limitstate->count = DatumGetInt32(ExecEvalExpr(node->limitCount,
|
||||
econtext,
|
||||
&isNull,
|
||||
NULL));
|
||||
/* Interpret NULL count as no count */
|
||||
if (isNull)
|
||||
limitstate->noCount = true;
|
||||
else
|
||||
{
|
||||
/* Currently, LIMIT 0 is specified as meaning no limit.
|
||||
* I think this is pretty bogus, but ...
|
||||
*/
|
||||
if (limitstate->count <= 0)
|
||||
limitstate->noCount = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No COUNT supplied */
|
||||
limitstate->count = 0;
|
||||
limitstate->noCount = true;
|
||||
}
|
||||
|
||||
/* Reset position data to start-of-scan */
|
||||
limitstate->position = 0;
|
||||
limitstate->atEnd = false;
|
||||
|
||||
/* Set flag that params are computed */
|
||||
limitstate->parmsSet = true;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecInitLimit
|
||||
*
|
||||
* This initializes the limit node state structures and
|
||||
* the node's subplan.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
bool /* return: initialization status */
|
||||
ExecInitLimit(Limit *node, EState *estate, Plan *parent)
|
||||
{
|
||||
LimitState *limitstate;
|
||||
Plan *outerPlan;
|
||||
|
||||
/* ----------------
|
||||
* assign execution state to node
|
||||
* ----------------
|
||||
*/
|
||||
node->plan.state = estate;
|
||||
|
||||
/* ----------------
|
||||
* create new LimitState for node
|
||||
* ----------------
|
||||
*/
|
||||
limitstate = makeNode(LimitState);
|
||||
node->limitstate = limitstate;
|
||||
limitstate->parmsSet = false;
|
||||
|
||||
/* ----------------
|
||||
* Miscellaneous initialization
|
||||
*
|
||||
* Limit nodes never call ExecQual or ExecProject, but they need
|
||||
* an exprcontext anyway to evaluate the limit/offset parameters in.
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignExprContext(estate, &limitstate->cstate);
|
||||
|
||||
#define LIMIT_NSLOTS 1
|
||||
/* ------------
|
||||
* Tuple table initialization
|
||||
* ------------
|
||||
*/
|
||||
ExecInitResultTupleSlot(estate, &limitstate->cstate);
|
||||
|
||||
/* ----------------
|
||||
* then initialize outer plan
|
||||
* ----------------
|
||||
*/
|
||||
outerPlan = outerPlan((Plan *) node);
|
||||
ExecInitNode(outerPlan, estate, (Plan *) node);
|
||||
|
||||
/* ----------------
|
||||
* limit nodes do no projections, so initialize
|
||||
* projection info for this node appropriately
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignResultTypeFromOuterPlan((Plan *) node, &limitstate->cstate);
|
||||
limitstate->cstate.cs_ProjInfo = NULL;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
ExecCountSlotsLimit(Limit *node)
|
||||
{
|
||||
return ExecCountSlotsNode(outerPlan(node)) +
|
||||
ExecCountSlotsNode(innerPlan(node)) +
|
||||
LIMIT_NSLOTS;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecEndLimit
|
||||
*
|
||||
* This shuts down the subplan and frees resources allocated
|
||||
* to this node.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecEndLimit(Limit *node)
|
||||
{
|
||||
LimitState *limitstate = node->limitstate;
|
||||
|
||||
ExecFreeExprContext(&limitstate->cstate);
|
||||
|
||||
ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
|
||||
|
||||
/* clean up tuple table */
|
||||
ExecClearTuple(limitstate->cstate.cs_ResultTupleSlot);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ExecReScanLimit(Limit *node, ExprContext *exprCtxt, Plan *parent)
|
||||
{
|
||||
LimitState *limitstate = node->limitstate;
|
||||
|
||||
ExecClearTuple(limitstate->cstate.cs_ResultTupleSlot);
|
||||
|
||||
/* force recalculation of limit expressions on first call */
|
||||
limitstate->parmsSet = false;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
* spi.c
|
||||
* Server Programming Interface
|
||||
*
|
||||
* $Id: spi.c,v 1.47 2000/06/28 03:31:34 tgl Exp $
|
||||
* $Id: spi.c,v 1.48 2000/10/26 21:35:15 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -762,8 +762,6 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
|
||||
bool isRetrieveIntoRelation = false;
|
||||
char *intoName = NULL;
|
||||
int res;
|
||||
Const tcount_const;
|
||||
Node *count = NULL;
|
||||
|
||||
switch (operation)
|
||||
{
|
||||
@ -798,39 +796,6 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
|
||||
return SPI_ERROR_OPUNKNOWN;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* Get the query LIMIT tuple count
|
||||
* ----------------
|
||||
*/
|
||||
if (parseTree->limitCount != NULL)
|
||||
{
|
||||
/* ----------------
|
||||
* A limit clause in the parsetree overrides the
|
||||
* tcount parameter
|
||||
* ----------------
|
||||
*/
|
||||
count = parseTree->limitCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* ----------------
|
||||
* No LIMIT clause in parsetree. Use a local Const node
|
||||
* to put tcount into it
|
||||
* ----------------
|
||||
*/
|
||||
memset(&tcount_const, 0, sizeof(tcount_const));
|
||||
tcount_const.type = T_Const;
|
||||
tcount_const.consttype = INT4OID;
|
||||
tcount_const.constlen = sizeof(int4);
|
||||
tcount_const.constvalue = (Datum) tcount;
|
||||
tcount_const.constisnull = FALSE;
|
||||
tcount_const.constbyval = TRUE;
|
||||
tcount_const.constisset = FALSE;
|
||||
tcount_const.constiscast = FALSE;
|
||||
|
||||
count = (Node *) &tcount_const;
|
||||
}
|
||||
|
||||
if (state == NULL) /* plan preparation */
|
||||
return res;
|
||||
#ifdef SPI_EXECUTOR_STATS
|
||||
@ -848,7 +813,7 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
|
||||
elog(FATAL, "SPI_select: retrieve into portal not implemented");
|
||||
}
|
||||
|
||||
ExecutorRun(queryDesc, state, EXEC_FOR, parseTree->limitOffset, count);
|
||||
ExecutorRun(queryDesc, state, EXEC_FOR, (long) tcount);
|
||||
|
||||
_SPI_current->processed = state->es_processed;
|
||||
if (operation == CMD_SELECT && queryDesc->dest == SPI)
|
||||
|
Reference in New Issue
Block a user