1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-27 23:21:58 +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:
Tom Lane
2000-10-26 21:38:24 +00:00
parent c9476bafdb
commit 2f35b4efdb
26 changed files with 572 additions and 232 deletions

View File

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