aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2000-10-26 21:38:24 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2000-10-26 21:38:24 +0000
commit2f35b4efdbec6c161ca9bd491d6345134910c425 (patch)
tree2424351bcc12a8ddf2b716b28f53d2c37c79e507 /src
parentc9476bafdb1b97d0d21d92788f93298962145479 (diff)
downloadpostgresql-2f35b4efdbec6c161ca9bd491d6345134910c425.tar.gz
postgresql-2f35b4efdbec6c161ca9bd491d6345134910c425.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/backend/commands/command.c25
-rw-r--r--src/backend/commands/explain.c5
-rw-r--r--src/backend/executor/Makefile6
-rw-r--r--src/backend/executor/execAmi.c7
-rw-r--r--src/backend/executor/execMain.c147
-rw-r--r--src/backend/executor/execProcnode.c18
-rw-r--r--src/backend/executor/execTuples.c10
-rw-r--r--src/backend/executor/functions.c7
-rw-r--r--src/backend/executor/nodeLimit.c324
-rw-r--r--src/backend/executor/spi.c39
-rw-r--r--src/backend/nodes/copyfuncs.c30
-rw-r--r--src/backend/nodes/outfuncs.c17
-rw-r--r--src/backend/nodes/print.c4
-rw-r--r--src/backend/optimizer/plan/createplan.c23
-rw-r--r--src/backend/optimizer/plan/planner.c28
-rw-r--r--src/backend/optimizer/plan/setrefs.c3
-rw-r--r--src/backend/optimizer/plan/subselect.c3
-rw-r--r--src/backend/rewrite/rewriteDefine.c8
-rw-r--r--src/backend/tcop/pquery.c5
-rw-r--r--src/backend/utils/adt/ruleutils.c18
-rw-r--r--src/include/executor/executor.h4
-rw-r--r--src/include/executor/nodeLimit.h25
-rw-r--r--src/include/nodes/execnodes.h24
-rw-r--r--src/include/nodes/nodes.h5
-rw-r--r--src/include/nodes/plannodes.h15
-rw-r--r--src/include/optimizer/planmain.h4
26 files changed, 572 insertions, 232 deletions
diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c
index 67b5f1dc4cd..4446c9f5cb5 100644
--- a/src/backend/commands/command.c
+++ b/src/backend/commands/command.c
@@ -8,7 +8,7 @@
*
*
* 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
* The PerformAddAttribute() code, like most of the relation
@@ -111,7 +111,6 @@ PerformPortalFetch(char *name,
int feature;
QueryDesc *queryDesc;
MemoryContext oldcontext;
- Const limcount;
/* ----------------
* sanity checks
@@ -124,20 +123,6 @@ PerformPortalFetch(char *name,
}
/* ----------------
- * 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
* ----------------
*/
@@ -156,8 +141,7 @@ PerformPortalFetch(char *name,
oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
/* ----------------
- * setup "feature" to tell the executor what direction and
- * how many tuples to fetch.
+ * setup "feature" to tell the executor which direction to go in.
* ----------------
*/
if (forward)
@@ -166,7 +150,7 @@ PerformPortalFetch(char *name,
feature = EXEC_BACK;
/* ----------------
- * tell the destination to prepare to recieve some tuples
+ * tell the destination to prepare to receive some tuples
* ----------------
*/
queryDesc = PortalGetQueryDesc(portal);
@@ -194,8 +178,7 @@ PerformPortalFetch(char *name,
* execute the portal fetch operation
* ----------------
*/
- ExecutorRun(queryDesc, PortalGetState(portal), feature,
- (Node *) NULL, (Node *) &limcount);
+ ExecutorRun(queryDesc, PortalGetState(portal), feature, (long) count);
if (dest == None) /* MOVE */
pfree(queryDesc);
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index f98ca70514f..6976278c1d0 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* 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)
break;
}
break;
+ case T_Limit:
+ pname = "Limit";
+ break;
case T_Hash:
pname = "Hash";
break;
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 7c79df5904d..7d57beb59f2 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -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
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index 9d008494b30..cb47eda5c66 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -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;
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 5523256bbe8..a26acc9a763 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -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;
/*
@@ -991,17 +895,6 @@ lnext: ;
}
/*
- * 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;
}
diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
index 6269a7caa10..d7db099653d 100644
--- a/src/backend/executor/execProcnode.c
+++ b/src/backend/executor/execProcnode.c
@@ -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;
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index d1cdfabab3b..408716abf83 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -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;
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 58fb68a6113..e5a5e55ef8d 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -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
diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c
new file mode 100644
index 00000000000..c7bc666c2f9
--- /dev/null
+++ b/src/backend/executor/nodeLimit.c
@@ -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);
+}
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 1ab6ae67d50..07a05561a64 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -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)
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 3d740533ff0..e78de345d9c 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@@ -593,6 +593,31 @@ _copySetOp(SetOp *from)
}
/* ----------------
+ * _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
* ----------------
*/
@@ -2567,6 +2592,9 @@ copyObject(void *from)
case T_SetOp:
retval = _copySetOp(from);
break;
+ case T_Limit:
+ retval = _copyLimit(from);
+ break;
case T_Hash:
retval = _copyHash(from);
break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index cf8c90ecad6..16b64851097 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* 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
* Every (plan) node in POSTGRES has an associated "out" routine which
@@ -623,6 +623,18 @@ _outSetOp(StringInfo str, SetOp *node)
(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
*/
@@ -1559,6 +1571,9 @@ _outNode(StringInfo str, void *obj)
case T_SetOp:
_outSetOp(str, obj);
break;
+ case T_Limit:
+ _outLimit(str, obj);
+ break;
case T_Hash:
_outHash(str, obj);
break;
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index 44de7fc6e20..6a50709541b 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -8,7 +8,7 @@
*
*
* 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
* AUTHOR DATE MAJOR EVENT
@@ -324,6 +324,8 @@ plannode_type(Plan *p)
return "UNIQUE";
case T_SetOp:
return "SETOP";
+ case T_Limit:
+ return "LIMIT";
case T_Hash:
return "HASH";
case T_Group:
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index eb005121cd5..a865da61b92 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -10,7 +10,7 @@
*
*
* 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,
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 *
make_result(List *tlist,
Node *resconstantqual,
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index d73ca9a34ac..f9c70f7137d 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
*
*
* 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)
*/
if (subquery->rowMarks)
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.
* Maybe after querytree redesign...
@@ -350,13 +348,16 @@ is_simple_subquery(Query *subquery)
if (subquery->setOperations)
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 ||
subquery->groupClause ||
subquery->havingQual ||
subquery->sortClause ||
- subquery->distinctClause)
+ subquery->distinctClause ||
+ subquery->limitOffset ||
+ subquery->limitCount)
return false;
/*
* Hack: don't try to pull up a subquery with an empty jointree.
@@ -831,7 +832,7 @@ union_planner(Query *parse,
}
else
{
- /* It's a PARAM ... punt ... */
+ /* It's an expression ... punt ... */
tuple_fraction = 0.10;
}
}
@@ -839,9 +840,8 @@ union_planner(Query *parse,
}
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
* 10% of the plan's result is wanted.
*/
@@ -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)
{
@@ -1032,6 +1032,16 @@ union_planner(Query *parse,
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;
}
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 14c9dad3ef3..deb020e2565 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -9,7 +9,7 @@
*
*
* 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)
case T_Sort:
case T_Unique:
case T_SetOp:
+ case T_Limit:
case T_Hash:
/*
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 03e38371df5..296164acb89 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* 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)
case T_Sort:
case T_Unique:
case T_SetOp:
+ case T_Limit:
case T_Group:
break;
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index c08ddbc6782..a83e0202335 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -8,7 +8,7 @@
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@@ -305,12 +305,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.
*/
expected_name = MakeRetrieveViewRuleName(event_obj->relname);
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 172f6fe467c..62848d77348 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -8,7 +8,7 @@
*
*
* 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,
* actually run the plan..
* ----------------
*/
- ExecutorRun(queryDesc, state, EXEC_RUN,
- parsetree->limitOffset, parsetree->limitCount);
+ ExecutorRun(queryDesc, state, EXEC_RUN, 0L);
/* save infos for EndCommand */
UpdateCommandInfo(operation, state->es_lastoid, state->es_processed);
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 7ab3985f3e7..70dfe9706bc 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
* back to source text
*
* 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.
*
@@ -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
- * level of a UNION/INTERSECT/EXCEPT query; only the ORDER BY field
- * is interesting in the top query itself.
+ * level of a UNION/INTERSECT/EXCEPT query; only the ORDER BY and
+ * LIMIT fields are interesting in the top query itself.
* ----------
*/
if (query->setOperations)
@@ -931,6 +931,18 @@ get_select_query_def(Query *query, deparse_context *context)
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
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 5c330915e75..9fe59b031a7 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* 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);
*/
extern TupleDesc ExecutorStart(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 ExecConstraints(char *caller, Relation rel,
TupleTableSlot *slot, EState *estate);
diff --git a/src/include/executor/nodeLimit.h b/src/include/executor/nodeLimit.h
new file mode 100644
index 00000000000..4ed16545fcf
--- /dev/null
+++ b/src/include/executor/nodeLimit.h
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 06de4be54cb..14cd94baa07 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* 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
MemoryContext tempContext; /* short-term context for comparisons */
} 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
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 43bb8b733ee..b06335290f4 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* 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
T_NestLoop,
T_MergeJoin,
T_HashJoin,
- T_Noname_XXX, /* not used anymore; this tag# is available */
+ T_Limit,
T_Material,
T_Sort,
T_Agg,
@@ -122,6 +122,7 @@ typedef enum NodeTag
T_TidScanState,
T_SubqueryScanState,
T_SetOpState,
+ T_LimitState,
/*---------------------
* TAGS FOR MEMORY NODES (memnodes.h)
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index d8e3df4829a..177ab73a13b 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* 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 @@
* Sort SortState sortstate;
* Unique UniqueState uniquestate;
* SetOp SetOpState setopstate;
+ * Limit LimitState limitstate;
* Hash HashState hashstate;
*
* ----------------------------------------------------------------
@@ -376,6 +377,18 @@ typedef struct 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
* ----------------
*/
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 015590a5ee2..2d5de645046 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* 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,
AttrNumber *grpColIdx, Plan *lefttree);
extern Material *make_material(List *tlist, Plan *lefttree);
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,
List *distinctList, AttrNumber flagColIdx);
extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);