Commit 2f35b4ef authored by Tom Lane's avatar Tom Lane

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.
parent c9476baf
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.107 2000/10/16 17:08:05 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.108 2000/10/26 21:34:44 tgl Exp $
* *
* NOTES * NOTES
* The PerformAddAttribute() code, like most of the relation * The PerformAddAttribute() code, like most of the relation
...@@ -111,7 +111,6 @@ PerformPortalFetch(char *name, ...@@ -111,7 +111,6 @@ PerformPortalFetch(char *name,
int feature; int feature;
QueryDesc *queryDesc; QueryDesc *queryDesc;
MemoryContext oldcontext; MemoryContext oldcontext;
Const limcount;
/* ---------------- /* ----------------
* sanity checks * sanity checks
...@@ -123,20 +122,6 @@ PerformPortalFetch(char *name, ...@@ -123,20 +122,6 @@ PerformPortalFetch(char *name,
return; return;
} }
/* ----------------
* Create a const node from the given count value
* ----------------
*/
memset(&limcount, 0, sizeof(limcount));
limcount.type = T_Const;
limcount.consttype = INT4OID;
limcount.constlen = sizeof(int4);
limcount.constvalue = Int32GetDatum(count);
limcount.constisnull = false;
limcount.constbyval = true;
limcount.constisset = false;
limcount.constiscast = false;
/* ---------------- /* ----------------
* get the portal from the portal name * get the portal from the portal name
* ---------------- * ----------------
...@@ -156,8 +141,7 @@ PerformPortalFetch(char *name, ...@@ -156,8 +141,7 @@ PerformPortalFetch(char *name,
oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
/* ---------------- /* ----------------
* setup "feature" to tell the executor what direction and * setup "feature" to tell the executor which direction to go in.
* how many tuples to fetch.
* ---------------- * ----------------
*/ */
if (forward) if (forward)
...@@ -166,7 +150,7 @@ PerformPortalFetch(char *name, ...@@ -166,7 +150,7 @@ PerformPortalFetch(char *name,
feature = EXEC_BACK; feature = EXEC_BACK;
/* ---------------- /* ----------------
* tell the destination to prepare to recieve some tuples * tell the destination to prepare to receive some tuples
* ---------------- * ----------------
*/ */
queryDesc = PortalGetQueryDesc(portal); queryDesc = PortalGetQueryDesc(portal);
...@@ -194,8 +178,7 @@ PerformPortalFetch(char *name, ...@@ -194,8 +178,7 @@ PerformPortalFetch(char *name,
* execute the portal fetch operation * execute the portal fetch operation
* ---------------- * ----------------
*/ */
ExecutorRun(queryDesc, PortalGetState(portal), feature, ExecutorRun(queryDesc, PortalGetState(portal), feature, (long) count);
(Node *) NULL, (Node *) &limcount);
if (dest == None) /* MOVE */ if (dest == None) /* MOVE */
pfree(queryDesc); pfree(queryDesc);
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994-5, Regents of the University of California * Portions Copyright (c) 1994-5, Regents of the University of California
* *
* $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.60 2000/10/05 19:11:26 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.61 2000/10/26 21:34:44 tgl Exp $
* *
*/ */
...@@ -217,6 +217,9 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es) ...@@ -217,6 +217,9 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
break; break;
} }
break; break;
case T_Limit:
pname = "Limit";
break;
case T_Hash: case T_Hash:
pname = "Hash"; pname = "Hash";
break; break;
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
# Makefile for executor # Makefile for executor
# #
# IDENTIFICATION # 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 \ ...@@ -17,8 +17,8 @@ OBJS = execAmi.o execFlatten.o execJunk.o execMain.o \
execUtils.o functions.o nodeAppend.o nodeAgg.o nodeHash.o \ execUtils.o functions.o nodeAppend.o nodeAgg.o nodeHash.o \
nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \ nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
nodeNestloop.o nodeResult.o nodeSeqscan.o nodeSetOp.o nodeSort.o \ nodeNestloop.o nodeResult.o nodeSeqscan.o nodeSetOp.o nodeSort.o \
nodeUnique.o nodeGroup.o spi.o nodeSubplan.o \ nodeUnique.o nodeLimit.o nodeGroup.o nodeSubplan.o \
nodeSubqueryscan.o nodeTidscan.o nodeSubqueryscan.o nodeTidscan.o spi.o
all: SUBSYS.o all: SUBSYS.o
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * 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 @@ ...@@ -37,6 +37,7 @@
#include "executor/nodeHashjoin.h" #include "executor/nodeHashjoin.h"
#include "executor/nodeIndexscan.h" #include "executor/nodeIndexscan.h"
#include "executor/nodeTidscan.h" #include "executor/nodeTidscan.h"
#include "executor/nodeLimit.h"
#include "executor/nodeMaterial.h" #include "executor/nodeMaterial.h"
#include "executor/nodeMergejoin.h" #include "executor/nodeMergejoin.h"
#include "executor/nodeNestloop.h" #include "executor/nodeNestloop.h"
...@@ -350,6 +351,10 @@ ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent) ...@@ -350,6 +351,10 @@ ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
ExecReScanSetOp((SetOp *) node, exprCtxt, parent); ExecReScanSetOp((SetOp *) node, exprCtxt, parent);
break; break;
case T_Limit:
ExecReScanLimit((Limit *) node, exprCtxt, parent);
break;
case T_Sort: case T_Sort:
ExecReScanSort((Sort *) node, exprCtxt, parent); ExecReScanSort((Sort *) node, exprCtxt, parent);
break; break;
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -52,11 +52,10 @@ static TupleDesc InitPlan(CmdType operation,
EState *estate); EState *estate);
static void EndPlan(Plan *plan, EState *estate); static void EndPlan(Plan *plan, EState *estate);
static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan, static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan,
CmdType operation, CmdType operation,
int offsetTuples, long numberTuples,
int numberTuples, ScanDirection direction,
ScanDirection direction, DestReceiver *destfunc);
DestReceiver *destfunc);
static void ExecRetrieve(TupleTableSlot *slot, static void ExecRetrieve(TupleTableSlot *slot,
DestReceiver *destfunc, DestReceiver *destfunc,
EState *estate); EState *estate);
...@@ -153,19 +152,18 @@ ExecutorStart(QueryDesc *queryDesc, EState *estate) ...@@ -153,19 +152,18 @@ ExecutorStart(QueryDesc *queryDesc, EState *estate)
* EXEC_RETONE: return one tuple but don't 'retrieve' it * EXEC_RETONE: return one tuple but don't 'retrieve' it
* used in postquel function processing * used in postquel function processing
* *
* Note: count = 0 is interpreted as "no limit".
*
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
TupleTableSlot * TupleTableSlot *
ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, long count)
Node *limoffset, Node *limcount)
{ {
CmdType operation; CmdType operation;
Plan *plan; Plan *plan;
TupleTableSlot *result; TupleTableSlot *result;
CommandDest dest; CommandDest dest;
DestReceiver *destfunc; DestReceiver *destfunc;
int offset = 0;
int count = 0;
/* /*
* sanity checks * sanity checks
...@@ -191,111 +189,21 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, ...@@ -191,111 +189,21 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature,
*/ */
(*destfunc->setup) (destfunc, (TupleDesc) NULL); (*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) switch (feature)
{ {
case EXEC_RUN: case EXEC_RUN:
result = ExecutePlan(estate, result = ExecutePlan(estate,
plan, plan,
operation, operation,
offset,
count, count,
ForwardScanDirection, ForwardScanDirection,
destfunc); destfunc);
break; break;
case EXEC_FOR: case EXEC_FOR:
result = ExecutePlan(estate, result = ExecutePlan(estate,
plan, plan,
operation, operation,
offset,
count, count,
ForwardScanDirection, ForwardScanDirection,
destfunc); destfunc);
...@@ -308,7 +216,6 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, ...@@ -308,7 +216,6 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature,
result = ExecutePlan(estate, result = ExecutePlan(estate,
plan, plan,
operation, operation,
offset,
count, count,
BackwardScanDirection, BackwardScanDirection,
destfunc); destfunc);
...@@ -322,14 +229,14 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, ...@@ -322,14 +229,14 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature,
result = ExecutePlan(estate, result = ExecutePlan(estate,
plan, plan,
operation, operation,
0,
ONE_TUPLE, ONE_TUPLE,
ForwardScanDirection, ForwardScanDirection,
destfunc); destfunc);
break; break;
default: default:
result = NULL;
elog(DEBUG, "ExecutorRun: Unknown feature %d", feature); elog(DEBUG, "ExecutorRun: Unknown feature %d", feature);
result = NULL;
break; break;
} }
...@@ -917,25 +824,22 @@ EndPlan(Plan *plan, EState *estate) ...@@ -917,25 +824,22 @@ EndPlan(Plan *plan, EState *estate)
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecutePlan * ExecutePlan
* *
* processes the query plan to retrieve 'tupleCount' tuples in the * processes the query plan to retrieve 'numberTuples' tuples in the
* direction specified. * direction specified.
* Retrieves all tuples if tupleCount is 0 * 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. * 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 * static TupleTableSlot *
ExecutePlan(EState *estate, ExecutePlan(EState *estate,
Plan *plan, Plan *plan,
CmdType operation, CmdType operation,
int offsetTuples, long numberTuples,
int numberTuples,
ScanDirection direction, ScanDirection direction,
DestReceiver *destfunc) DestReceiver *destfunc)
{ {
...@@ -943,7 +847,7 @@ ExecutePlan(EState *estate, ...@@ -943,7 +847,7 @@ ExecutePlan(EState *estate,
TupleTableSlot *slot; TupleTableSlot *slot;
ItemPointer tupleid = NULL; ItemPointer tupleid = NULL;
ItemPointerData tuple_ctid; ItemPointerData tuple_ctid;
int current_tuple_count; long current_tuple_count;
TupleTableSlot *result; TupleTableSlot *result;
/* /*
...@@ -990,17 +894,6 @@ lnext: ; ...@@ -990,17 +894,6 @@ lnext: ;
break; 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 * if we have a junk filter, then project a new tuple with the
* junk removed. * junk removed.
...@@ -1152,10 +1045,10 @@ lnext: ; ...@@ -1152,10 +1045,10 @@ lnext: ;
} }
/* /*
* check our tuple count.. if we've returned the proper number * check our tuple count.. if we've processed the proper number
* then return, else loop again and process more tuples.. * then quit, else loop again and process more tuples..
*/ */
current_tuple_count += 1; current_tuple_count++;
if (numberTuples == current_tuple_count) if (numberTuples == current_tuple_count)
break; break;
} }
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* *
* *
* IDENTIFICATION * 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 @@ ...@@ -83,6 +83,7 @@
#include "executor/nodeHashjoin.h" #include "executor/nodeHashjoin.h"
#include "executor/nodeIndexscan.h" #include "executor/nodeIndexscan.h"
#include "executor/nodeTidscan.h" #include "executor/nodeTidscan.h"
#include "executor/nodeLimit.h"
#include "executor/nodeMaterial.h" #include "executor/nodeMaterial.h"
#include "executor/nodeMergejoin.h" #include "executor/nodeMergejoin.h"
#include "executor/nodeNestloop.h" #include "executor/nodeNestloop.h"
...@@ -204,6 +205,10 @@ ExecInitNode(Plan *node, EState *estate, Plan *parent) ...@@ -204,6 +205,10 @@ ExecInitNode(Plan *node, EState *estate, Plan *parent)
result = ExecInitSetOp((SetOp *) node, estate, parent); result = ExecInitSetOp((SetOp *) node, estate, parent);
break; break;
case T_Limit:
result = ExecInitLimit((Limit *) node, estate, parent);
break;
case T_Group: case T_Group:
result = ExecInitGroup((Group *) node, estate, parent); result = ExecInitGroup((Group *) node, estate, parent);
break; break;
...@@ -331,6 +336,10 @@ ExecProcNode(Plan *node, Plan *parent) ...@@ -331,6 +336,10 @@ ExecProcNode(Plan *node, Plan *parent)
result = ExecSetOp((SetOp *) node); result = ExecSetOp((SetOp *) node);
break; break;
case T_Limit:
result = ExecLimit((Limit *) node);
break;
case T_Group: case T_Group:
result = ExecGroup((Group *) node); result = ExecGroup((Group *) node);
break; break;
...@@ -413,6 +422,9 @@ ExecCountSlotsNode(Plan *node) ...@@ -413,6 +422,9 @@ ExecCountSlotsNode(Plan *node)
case T_SetOp: case T_SetOp:
return ExecCountSlotsSetOp((SetOp *) node); return ExecCountSlotsSetOp((SetOp *) node);
case T_Limit:
return ExecCountSlotsLimit((Limit *) node);
case T_Group: case T_Group:
return ExecCountSlotsGroup((Group *) node); return ExecCountSlotsGroup((Group *) node);
...@@ -535,6 +547,10 @@ ExecEndNode(Plan *node, Plan *parent) ...@@ -535,6 +547,10 @@ ExecEndNode(Plan *node, Plan *parent)
ExecEndSetOp((SetOp *) node); ExecEndSetOp((SetOp *) node);
break; break;
case T_Limit:
ExecEndLimit((Limit *) node);
break;
case T_Group: case T_Group:
ExecEndGroup((Group *) node); ExecEndGroup((Group *) node);
break; break;
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -770,6 +770,14 @@ NodeGetResultTupleSlot(Plan *node)
} }
break; break;
case T_Limit:
{
LimitState *limitstate = ((Limit *) node)->limitstate;
slot = limitstate->cstate.cs_ResultTupleSlot;
}
break;
case T_MergeJoin: case T_MergeJoin:
{ {
MergeJoinState *mergestate = ((MergeJoin *) node)->mergestate; MergeJoinState *mergestate = ((MergeJoin *) node)->mergestate;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -135,9 +135,6 @@ init_execution_state(char *src, Oid *argOidVect, int nargs)
None); None);
estate = CreateExecutorState(); estate = CreateExecutorState();
if (queryTree->limitOffset != NULL || queryTree->limitCount != NULL)
elog(ERROR, "LIMIT clause from SQL functions not yet implemented");
if (nargs > 0) if (nargs > 0)
{ {
int i; int i;
...@@ -328,7 +325,7 @@ postquel_getnext(execution_state *es) ...@@ -328,7 +325,7 @@ postquel_getnext(execution_state *es)
feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN; 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 static void
......
/*-------------------------------------------------------------------------
*
* 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 @@ ...@@ -3,7 +3,7 @@
* spi.c * spi.c
* Server Programming Interface * 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) ...@@ -762,8 +762,6 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
bool isRetrieveIntoRelation = false; bool isRetrieveIntoRelation = false;
char *intoName = NULL; char *intoName = NULL;
int res; int res;
Const tcount_const;
Node *count = NULL;
switch (operation) switch (operation)
{ {
...@@ -798,39 +796,6 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount) ...@@ -798,39 +796,6 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
return SPI_ERROR_OPUNKNOWN; 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 */ if (state == NULL) /* plan preparation */
return res; return res;
#ifdef SPI_EXECUTOR_STATS #ifdef SPI_EXECUTOR_STATS
...@@ -848,7 +813,7 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount) ...@@ -848,7 +813,7 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
elog(FATAL, "SPI_select: retrieve into portal not implemented"); 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; _SPI_current->processed = state->es_processed;
if (operation == CMD_SELECT && queryDesc->dest == SPI) if (operation == CMD_SELECT && queryDesc->dest == SPI)
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.126 2000/10/18 16:16:04 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.127 2000/10/26 21:35:47 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -592,6 +592,31 @@ _copySetOp(SetOp *from) ...@@ -592,6 +592,31 @@ _copySetOp(SetOp *from)
return newnode; return newnode;
} }
/* ----------------
* _copyLimit
* ----------------
*/
static Limit *
_copyLimit(Limit *from)
{
Limit *newnode = makeNode(Limit);
/* ----------------
* copy node superclass fields
* ----------------
*/
CopyPlanFields((Plan *) from, (Plan *) newnode);
/* ----------------
* copy remainder of node
* ----------------
*/
Node_Copy(from, newnode, limitOffset);
Node_Copy(from, newnode, limitCount);
return newnode;
}
/* ---------------- /* ----------------
* _copyHash * _copyHash
* ---------------- * ----------------
...@@ -2567,6 +2592,9 @@ copyObject(void *from) ...@@ -2567,6 +2592,9 @@ copyObject(void *from)
case T_SetOp: case T_SetOp:
retval = _copySetOp(from); retval = _copySetOp(from);
break; break;
case T_Limit:
retval = _copyLimit(from);
break;
case T_Hash: case T_Hash:
retval = _copyHash(from); retval = _copyHash(from);
break; break;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.128 2000/10/05 19:11:27 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.129 2000/10/26 21:35:48 tgl Exp $
* *
* NOTES * NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which * Every (plan) node in POSTGRES has an associated "out" routine which
...@@ -623,6 +623,18 @@ _outSetOp(StringInfo str, SetOp *node) ...@@ -623,6 +623,18 @@ _outSetOp(StringInfo str, SetOp *node)
(int) node->flagColIdx); (int) node->flagColIdx);
} }
static void
_outLimit(StringInfo str, Limit *node)
{
appendStringInfo(str, " LIMIT ");
_outPlanInfo(str, (Plan *) node);
appendStringInfo(str, " :limitOffset ");
_outNode(str, node->limitOffset);
appendStringInfo(str, " :limitCount ");
_outNode(str, node->limitCount);
}
/* /*
* Hash is a subclass of Plan * Hash is a subclass of Plan
*/ */
...@@ -1559,6 +1571,9 @@ _outNode(StringInfo str, void *obj) ...@@ -1559,6 +1571,9 @@ _outNode(StringInfo str, void *obj)
case T_SetOp: case T_SetOp:
_outSetOp(str, obj); _outSetOp(str, obj);
break; break;
case T_Limit:
_outLimit(str, obj);
break;
case T_Hash: case T_Hash:
_outHash(str, obj); _outHash(str, obj);
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.44 2000/10/22 22:14:54 petere Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.45 2000/10/26 21:35:48 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -324,6 +324,8 @@ plannode_type(Plan *p) ...@@ -324,6 +324,8 @@ plannode_type(Plan *p)
return "UNIQUE"; return "UNIQUE";
case T_SetOp: case T_SetOp:
return "SETOP"; return "SETOP";
case T_Limit:
return "LIMIT";
case T_Hash: case T_Hash:
return "HASH"; return "HASH";
case T_Group: case T_Group:
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.98 2000/10/05 19:11:29 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.99 2000/10/26 21:36:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1651,6 +1651,27 @@ make_setop(SetOpCmd cmd, List *tlist, Plan *lefttree, ...@@ -1651,6 +1651,27 @@ make_setop(SetOpCmd cmd, List *tlist, Plan *lefttree,
return node; return node;
} }
Limit *
make_limit(List *tlist, Plan *lefttree,
Node *limitOffset, Node *limitCount)
{
Limit *node = makeNode(Limit);
Plan *plan = &node->plan;
copy_plan_costsize(plan, lefttree);
plan->state = (EState *) NULL;
plan->targetlist = tlist;
plan->qual = NIL;
plan->lefttree = lefttree;
plan->righttree = NULL;
node->limitOffset = limitOffset;
node->limitCount = limitCount;
return node;
}
Result * Result *
make_result(List *tlist, make_result(List *tlist,
Node *resconstantqual, Node *resconstantqual,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.92 2000/10/05 19:11:29 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.93 2000/10/26 21:36:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -341,8 +341,6 @@ is_simple_subquery(Query *subquery) ...@@ -341,8 +341,6 @@ is_simple_subquery(Query *subquery)
*/ */
if (subquery->rowMarks) if (subquery->rowMarks)
elog(ERROR, "FOR UPDATE is not supported in subselects"); elog(ERROR, "FOR UPDATE is not supported in subselects");
if (subquery->limitOffset || subquery->limitCount)
elog(ERROR, "LIMIT is not supported in subselects");
/* /*
* Can't currently pull up a query with setops. * Can't currently pull up a query with setops.
* Maybe after querytree redesign... * Maybe after querytree redesign...
...@@ -350,13 +348,16 @@ is_simple_subquery(Query *subquery) ...@@ -350,13 +348,16 @@ is_simple_subquery(Query *subquery)
if (subquery->setOperations) if (subquery->setOperations)
return false; return false;
/* /*
* Can't pull up a subquery involving grouping, aggregation, or sorting. * Can't pull up a subquery involving grouping, aggregation, sorting,
* or limiting.
*/ */
if (subquery->hasAggs || if (subquery->hasAggs ||
subquery->groupClause || subquery->groupClause ||
subquery->havingQual || subquery->havingQual ||
subquery->sortClause || subquery->sortClause ||
subquery->distinctClause) subquery->distinctClause ||
subquery->limitOffset ||
subquery->limitCount)
return false; return false;
/* /*
* Hack: don't try to pull up a subquery with an empty jointree. * Hack: don't try to pull up a subquery with an empty jointree.
...@@ -831,7 +832,7 @@ union_planner(Query *parse, ...@@ -831,7 +832,7 @@ union_planner(Query *parse,
} }
else else
{ {
/* It's a PARAM ... punt ... */ /* It's an expression ... punt ... */
tuple_fraction = 0.10; tuple_fraction = 0.10;
} }
} }
...@@ -839,9 +840,8 @@ union_planner(Query *parse, ...@@ -839,9 +840,8 @@ union_planner(Query *parse,
} }
else else
{ {
/* /*
* COUNT is a PARAM ... don't know exactly what the * COUNT is an expression ... don't know exactly what the
* limit will be, but for lack of a better idea assume * limit will be, but for lack of a better idea assume
* 10% of the plan's result is wanted. * 10% of the plan's result is wanted.
*/ */
...@@ -1024,7 +1024,7 @@ union_planner(Query *parse, ...@@ -1024,7 +1024,7 @@ union_planner(Query *parse,
} }
/* /*
* Finally, if there is a DISTINCT clause, add the UNIQUE node. * If there is a DISTINCT clause, add the UNIQUE node.
*/ */
if (parse->distinctClause) if (parse->distinctClause)
{ {
...@@ -1032,6 +1032,16 @@ union_planner(Query *parse, ...@@ -1032,6 +1032,16 @@ union_planner(Query *parse,
parse->distinctClause); parse->distinctClause);
} }
/*
* Finally, if there is a LIMIT/OFFSET clause, add the LIMIT node.
*/
if (parse->limitOffset || parse->limitCount)
{
result_plan = (Plan *) make_limit(tlist, result_plan,
parse->limitOffset,
parse->limitCount);
}
return result_plan; return result_plan;
} }
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.67 2000/10/05 19:11:29 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.68 2000/10/26 21:36:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -139,6 +139,7 @@ set_plan_references(Plan *plan) ...@@ -139,6 +139,7 @@ set_plan_references(Plan *plan)
case T_Sort: case T_Sort:
case T_Unique: case T_Unique:
case T_SetOp: case T_SetOp:
case T_Limit:
case T_Hash: case T_Hash:
/* /*
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.43 2000/10/05 19:11:29 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.44 2000/10/26 21:36:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -657,6 +657,7 @@ SS_finalize_plan(Plan *plan) ...@@ -657,6 +657,7 @@ SS_finalize_plan(Plan *plan)
case T_Sort: case T_Sort:
case T_Unique: case T_Unique:
case T_SetOp: case T_SetOp:
case T_Limit:
case T_Group: case T_Group:
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.54 2000/10/05 19:11:34 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.55 2000/10/26 21:36:50 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -304,12 +304,6 @@ DefineQueryRewrite(RuleStmt *stmt) ...@@ -304,12 +304,6 @@ DefineQueryRewrite(RuleStmt *stmt)
} }
} }
/*
* LIMIT in view is not supported
*/
if (query->limitOffset != NULL || query->limitCount != NULL)
elog(ERROR, "LIMIT clause not supported in views");
/* /*
* ... and finally the rule must be named _RETviewname. * ... and finally the rule must be named _RETviewname.
*/ */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.38 2000/08/22 04:06:20 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.39 2000/10/26 21:37:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -299,8 +299,7 @@ ProcessQuery(Query *parsetree, ...@@ -299,8 +299,7 @@ ProcessQuery(Query *parsetree,
* actually run the plan.. * actually run the plan..
* ---------------- * ----------------
*/ */
ExecutorRun(queryDesc, state, EXEC_RUN, ExecutorRun(queryDesc, state, EXEC_RUN, 0L);
parsetree->limitOffset, parsetree->limitCount);
/* save infos for EndCommand */ /* save infos for EndCommand */
UpdateCommandInfo(operation, state->es_lastoid, state->es_processed); UpdateCommandInfo(operation, state->es_lastoid, state->es_processed);
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* back to source text * back to source text
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.66 2000/10/05 21:52:08 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.67 2000/10/26 21:37:45 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -886,8 +886,8 @@ get_select_query_def(Query *query, deparse_context *context) ...@@ -886,8 +886,8 @@ get_select_query_def(Query *query, deparse_context *context)
/* ---------- /* ----------
* If the Query node has a setOperations tree, then it's the top * If the Query node has a setOperations tree, then it's the top
* level of a UNION/INTERSECT/EXCEPT query; only the ORDER BY field * level of a UNION/INTERSECT/EXCEPT query; only the ORDER BY and
* is interesting in the top query itself. * LIMIT fields are interesting in the top query itself.
* ---------- * ----------
*/ */
if (query->setOperations) if (query->setOperations)
...@@ -931,6 +931,18 @@ get_select_query_def(Query *query, deparse_context *context) ...@@ -931,6 +931,18 @@ get_select_query_def(Query *query, deparse_context *context)
sep = ", "; sep = ", ";
} }
} }
/* Add the LIMIT clause if given */
if (query->limitOffset != NULL)
{
appendStringInfo(buf, " OFFSET ");
get_rule_expr(query->limitOffset, context);
}
if (query->limitCount != NULL)
{
appendStringInfo(buf, " LIMIT ");
get_rule_expr(query->limitCount, context);
}
} }
static void static void
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: executor.h,v 1.51 2000/09/12 21:07:09 tgl Exp $ * $Id: executor.h,v 1.52 2000/10/26 21:38:03 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -54,7 +54,7 @@ extern HeapTuple ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot); ...@@ -54,7 +54,7 @@ extern HeapTuple ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot);
*/ */
extern TupleDesc ExecutorStart(QueryDesc *queryDesc, EState *estate); extern TupleDesc ExecutorStart(QueryDesc *queryDesc, EState *estate);
extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc, EState *estate, extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc, EState *estate,
int feature, Node *limoffset, Node *limcount); int feature, long count);
extern void ExecutorEnd(QueryDesc *queryDesc, EState *estate); extern void ExecutorEnd(QueryDesc *queryDesc, EState *estate);
extern void ExecConstraints(char *caller, Relation rel, extern void ExecConstraints(char *caller, Relation rel,
TupleTableSlot *slot, EState *estate); TupleTableSlot *slot, EState *estate);
......
/*-------------------------------------------------------------------------
*
* nodeLimit.h
*
*
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: nodeLimit.h,v 1.1 2000/10/26 21:38:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef NODELIMIT_H
#define NODELIMIT_H
#include "nodes/plannodes.h"
extern TupleTableSlot *ExecLimit(Limit *node);
extern bool ExecInitLimit(Limit *node, EState *estate, Plan *parent);
extern int ExecCountSlotsLimit(Limit *node);
extern void ExecEndLimit(Limit *node);
extern void ExecReScanLimit(Limit *node, ExprContext *exprCtxt, Plan *parent);
#endif /* NODELIMIT_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: execnodes.h,v 1.51 2000/10/05 19:11:36 tgl Exp $ * $Id: execnodes.h,v 1.52 2000/10/26 21:38:12 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -675,6 +675,28 @@ typedef struct SetOpState ...@@ -675,6 +675,28 @@ typedef struct SetOpState
MemoryContext tempContext; /* short-term context for comparisons */ MemoryContext tempContext; /* short-term context for comparisons */
} SetOpState; } SetOpState;
/* ----------------
* LimitState information
*
* Limit nodes are used to enforce LIMIT/OFFSET clauses.
* They just select the desired subrange of their subplan's output.
*
* offset is the number of initial tuples to skip (0 does nothing).
* count is the number of tuples to return after skipping the offset tuples.
* If no limit count was specified, count is undefined and noCount is true.
* ----------------
*/
typedef struct LimitState
{
CommonState cstate; /* its first field is NodeTag */
long offset; /* current OFFSET value */
long count; /* current COUNT, if any */
long position; /* 1-based index of last tuple fetched */
bool parmsSet; /* have we calculated offset/limit yet? */
bool noCount; /* if true, ignore count */
bool atEnd; /* if true, we've reached EOF of subplan */
} LimitState;
/* ---------------- /* ----------------
* HashState information * HashState information
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: nodes.h,v 1.79 2000/10/22 23:32:44 tgl Exp $ * $Id: nodes.h,v 1.80 2000/10/26 21:38:12 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -39,7 +39,7 @@ typedef enum NodeTag ...@@ -39,7 +39,7 @@ typedef enum NodeTag
T_NestLoop, T_NestLoop,
T_MergeJoin, T_MergeJoin,
T_HashJoin, T_HashJoin,
T_Noname_XXX, /* not used anymore; this tag# is available */ T_Limit,
T_Material, T_Material,
T_Sort, T_Sort,
T_Agg, T_Agg,
...@@ -122,6 +122,7 @@ typedef enum NodeTag ...@@ -122,6 +122,7 @@ typedef enum NodeTag
T_TidScanState, T_TidScanState,
T_SubqueryScanState, T_SubqueryScanState,
T_SetOpState, T_SetOpState,
T_LimitState,
/*--------------------- /*---------------------
* TAGS FOR MEMORY NODES (memnodes.h) * TAGS FOR MEMORY NODES (memnodes.h)
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: plannodes.h,v 1.44 2000/10/05 19:11:36 tgl Exp $ * $Id: plannodes.h,v 1.45 2000/10/26 21:38:12 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
* Sort SortState sortstate; * Sort SortState sortstate;
* Unique UniqueState uniquestate; * Unique UniqueState uniquestate;
* SetOp SetOpState setopstate; * SetOp SetOpState setopstate;
* Limit LimitState limitstate;
* Hash HashState hashstate; * Hash HashState hashstate;
* *
* ---------------------------------------------------------------- * ----------------------------------------------------------------
...@@ -375,6 +376,18 @@ typedef struct SetOp ...@@ -375,6 +376,18 @@ typedef struct SetOp
SetOpState *setopstate; SetOpState *setopstate;
} SetOp; } SetOp;
/* ----------------
* limit node
* ----------------
*/
typedef struct Limit
{
Plan plan;
Node *limitOffset; /* OFFSET parameter, or NULL if none */
Node *limitCount; /* COUNT parameter, or NULL if none */
LimitState *limitstate;
} Limit;
/* ---------------- /* ----------------
* hash build node * hash build node
* ---------------- * ----------------
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: planmain.h,v 1.46 2000/10/05 19:11:37 tgl Exp $ * $Id: planmain.h,v 1.47 2000/10/26 21:38:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -36,6 +36,8 @@ extern Group *make_group(List *tlist, bool tuplePerGroup, int ngrp, ...@@ -36,6 +36,8 @@ extern Group *make_group(List *tlist, bool tuplePerGroup, int ngrp,
AttrNumber *grpColIdx, Plan *lefttree); AttrNumber *grpColIdx, Plan *lefttree);
extern Material *make_material(List *tlist, Plan *lefttree); extern Material *make_material(List *tlist, Plan *lefttree);
extern Unique *make_unique(List *tlist, Plan *lefttree, List *distinctList); extern Unique *make_unique(List *tlist, Plan *lefttree, List *distinctList);
extern Limit *make_limit(List *tlist, Plan *lefttree,
Node *limitOffset, Node *limitCount);
extern SetOp *make_setop(SetOpCmd cmd, List *tlist, Plan *lefttree, extern SetOp *make_setop(SetOpCmd cmd, List *tlist, Plan *lefttree,
List *distinctList, AttrNumber flagColIdx); List *distinctList, AttrNumber flagColIdx);
extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan); extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment