]> granicus.if.org Git - postgresql/commitdiff
Re-implement LIMIT/OFFSET as a plan node type, instead of a hack in
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 26 Oct 2000 21:38:24 +0000 (21:38 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 26 Oct 2000 21:38:24 +0000 (21:38 +0000)
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.

26 files changed:
src/backend/commands/command.c
src/backend/commands/explain.c
src/backend/executor/Makefile
src/backend/executor/execAmi.c
src/backend/executor/execMain.c
src/backend/executor/execProcnode.c
src/backend/executor/execTuples.c
src/backend/executor/functions.c
src/backend/executor/nodeLimit.c [new file with mode: 0644]
src/backend/executor/spi.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/print.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/plan/subselect.c
src/backend/rewrite/rewriteDefine.c
src/backend/tcop/pquery.c
src/backend/utils/adt/ruleutils.c
src/include/executor/executor.h
src/include/executor/nodeLimit.h [new file with mode: 0644]
src/include/nodes/execnodes.h
src/include/nodes/nodes.h
src/include/nodes/plannodes.h
src/include/optimizer/planmain.h

index 67b5f1dc4cdb0740c5ee0804f9536ccbfd6cac2f..4446c9f5cb573c3bbbdebd7060cab1e41ef44388 100644 (file)
@@ -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
@@ -123,20 +122,6 @@ PerformPortalFetch(char *name,
                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
         * ----------------
@@ -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);
index f98ca70514ff4684ec3def0a6253b26c6c87b053..6976278c1d025f0d65f97db68d22cbe38b588aee 100644 (file)
@@ -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;
index 7c79df5904de827de40423f5428f36243c95e453..7d57beb59f2895edcf4c84a68847d9d5ec9e2105 100644 (file)
@@ -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
 
index 9d008494b30b15aa098a762c2ae36736c06288a0..cb47eda5c66fe2f21793c3b6e88396e09e0e39bf 100644 (file)
@@ -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;
index 5523256bbe80f40fe54c0e071d6262f316f9b3e5..a26acc9a7632ec593b1a86434ece99ca0f0b1653 100644 (file)
@@ -27,7 +27,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.130 2000/10/16 17:08:06 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.131 2000/10/26 21:35:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -52,11 +52,10 @@ static TupleDesc InitPlan(CmdType operation,
                 EState *estate);
 static void EndPlan(Plan *plan, EState *estate);
 static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan,
-                       CmdType operation,
-                       int offsetTuples,
-                       int numberTuples,
-                       ScanDirection direction,
-                       DestReceiver *destfunc);
+                                                                  CmdType operation,
+                                                                  long numberTuples,
+                                                                  ScanDirection direction,
+                                                                  DestReceiver *destfunc);
 static void ExecRetrieve(TupleTableSlot *slot,
                         DestReceiver *destfunc,
                         EState *estate);
@@ -153,19 +152,18 @@ ExecutorStart(QueryDesc *queryDesc, EState *estate)
  *                      EXEC_RETONE: return one tuple but don't 'retrieve' it
  *                                                used in postquel function processing
  *
+ *             Note: count = 0 is interpreted as "no limit".
+ *
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
-ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature,
-                       Node *limoffset, Node *limcount)
+ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, long count)
 {
        CmdType         operation;
        Plan       *plan;
        TupleTableSlot *result;
        CommandDest dest;
        DestReceiver *destfunc;
-       int                     offset = 0;
-       int                     count = 0;
 
        /*
         * sanity checks
@@ -191,111 +189,21 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature,
         */
        (*destfunc->setup) (destfunc, (TupleDesc) NULL);
 
-       /*
-        * if given get the offset of the LIMIT clause
-        */
-       if (limoffset != NULL)
-       {
-               Const      *coffset;
-               Param      *poffset;
-               ParamListInfo paramLI;
-               int                     i;
-
-               switch (nodeTag(limoffset))
-               {
-                       case T_Const:
-                               coffset = (Const *) limoffset;
-                               offset = (int) (coffset->constvalue);
-                               break;
-
-                       case T_Param:
-                               poffset = (Param *) limoffset;
-                               paramLI = estate->es_param_list_info;
-
-                               if (paramLI == NULL)
-                                       elog(ERROR, "parameter for limit offset not in executor state");
-                               for (i = 0; paramLI[i].kind != PARAM_INVALID; i++)
-                               {
-                                       if (paramLI[i].kind == PARAM_NUM && paramLI[i].id == poffset->paramid)
-                                               break;
-                               }
-                               if (paramLI[i].kind == PARAM_INVALID)
-                                       elog(ERROR, "parameter for limit offset not in executor state");
-                               if (paramLI[i].isnull)
-                                       elog(ERROR, "limit offset cannot be NULL value");
-                               offset = (int) (paramLI[i].value);
-
-                               break;
-
-                       default:
-                               elog(ERROR, "unexpected node type %d as limit offset", nodeTag(limoffset));
-               }
-
-               if (offset < 0)
-                       elog(ERROR, "limit offset cannot be negative");
-       }
-
-       /*
-        * if given get the count of the LIMIT clause
-        */
-       if (limcount != NULL)
-       {
-               Const      *ccount;
-               Param      *pcount;
-               ParamListInfo paramLI;
-               int                     i;
-
-               switch (nodeTag(limcount))
-               {
-                       case T_Const:
-                               ccount = (Const *) limcount;
-                               count = (int) (ccount->constvalue);
-                               break;
-
-                       case T_Param:
-                               pcount = (Param *) limcount;
-                               paramLI = estate->es_param_list_info;
-
-                               if (paramLI == NULL)
-                                       elog(ERROR, "parameter for limit count not in executor state");
-                               for (i = 0; paramLI[i].kind != PARAM_INVALID; i++)
-                               {
-                                       if (paramLI[i].kind == PARAM_NUM && paramLI[i].id == pcount->paramid)
-                                               break;
-                               }
-                               if (paramLI[i].kind == PARAM_INVALID)
-                                       elog(ERROR, "parameter for limit count not in executor state");
-                               if (paramLI[i].isnull)
-                                       elog(ERROR, "limit count cannot be NULL value");
-                               count = (int) (paramLI[i].value);
-
-                               break;
-
-                       default:
-                               elog(ERROR, "unexpected node type %d as limit count", nodeTag(limcount));
-               }
-
-               if (count < 0)
-                       elog(ERROR, "limit count cannot be negative");
-       }
-
        switch (feature)
        {
-
                case EXEC_RUN:
                        result = ExecutePlan(estate,
                                                                 plan,
                                                                 operation,
-                                                                offset,
                                                                 count,
                                                                 ForwardScanDirection,
                                                                 destfunc);
                        break;
+
                case EXEC_FOR:
                        result = ExecutePlan(estate,
                                                                 plan,
                                                                 operation,
-                                                                offset,
                                                                 count,
                                                                 ForwardScanDirection,
                                                                 destfunc);
@@ -308,7 +216,6 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature,
                        result = ExecutePlan(estate,
                                                                 plan,
                                                                 operation,
-                                                                offset,
                                                                 count,
                                                                 BackwardScanDirection,
                                                                 destfunc);
@@ -322,14 +229,14 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature,
                        result = ExecutePlan(estate,
                                                                 plan,
                                                                 operation,
-                                                                0,
                                                                 ONE_TUPLE,
                                                                 ForwardScanDirection,
                                                                 destfunc);
                        break;
+
                default:
-                       result = NULL;
                        elog(DEBUG, "ExecutorRun: Unknown feature %d", feature);
+                       result = NULL;
                        break;
        }
 
@@ -917,25 +824,22 @@ EndPlan(Plan *plan, EState *estate)
 /* ----------------------------------------------------------------
  *             ExecutePlan
  *
- *             processes the query plan to retrieve 'tupleCount' tuples in the
+ *             processes the query plan to retrieve 'numberTuples' tuples in the
  *             direction specified.
  *             Retrieves all tuples if tupleCount is 0
  *
- *             result is either a slot containing a tuple in the case
+ *             result is either a slot containing the last tuple in the case
  *             of a RETRIEVE or NULL otherwise.
  *
+ * Note: the ctid attribute is a 'junk' attribute that is removed before the
+ * user can see it
  * ----------------------------------------------------------------
  */
-
-/* the ctid attribute is a 'junk' attribute that is removed before the
-   user can see it*/
-
 static TupleTableSlot *
 ExecutePlan(EState *estate,
                        Plan *plan,
                        CmdType operation,
-                       int offsetTuples,
-                       int numberTuples,
+                       long numberTuples,
                        ScanDirection direction,
                        DestReceiver *destfunc)
 {
@@ -943,7 +847,7 @@ ExecutePlan(EState *estate,
        TupleTableSlot *slot;
        ItemPointer tupleid = NULL;
        ItemPointerData tuple_ctid;
-       int                     current_tuple_count;
+       long            current_tuple_count;
        TupleTableSlot *result;
 
        /*
@@ -990,17 +894,6 @@ lnext:     ;
                        break;
                }
 
-               /*
-                * For now we completely execute the plan and skip result tuples
-                * if requested by LIMIT offset. Finally we should try to do it in
-                * deeper levels if possible (during index scan) - Jan
-                */
-               if (offsetTuples > 0)
-               {
-                       --offsetTuples;
-                       continue;
-               }
-
                /*
                 * if we have a junk filter, then project a new tuple with the
                 * junk removed.
@@ -1152,10 +1045,10 @@ lnext:  ;
                }
 
                /*
-                * check our tuple count.. if we've returned the proper number
-                * then return, else loop again and process more tuples..
+                * check our tuple count.. if we've processed the proper number
+                * then quit, else loop again and process more tuples..
                 */
-               current_tuple_count += 1;
+               current_tuple_count++;
                if (numberTuples == current_tuple_count)
                        break;
        }
index 6269a7caa10381898cdb3d4298e4620c2da79597..d7db099653dccaf66af1f4ee5fcc8696e6b8d7aa 100644 (file)
@@ -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;
index d1cdfabab3b2cc6793a041a3f00541524d5bc7ed..408716abf834088b5cd43d0d2d2e7e01f9c09a39 100644 (file)
@@ -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;
index 58fb68a6113b241f9a5867661039817125c5a680..e5a5e55ef8d0204eba5e72dd353f12ca00a79ea3 100644 (file)
@@ -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 (file)
index 0000000..c7bc666
--- /dev/null
@@ -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);
+}
index 1ab6ae67d508b19f21dd2508e7ac10c636daebf5..07a05561a64701c8e13959ee0a3c9941b04d09b3 100644 (file)
@@ -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)
index 3d740533ff009237849a91e0853539eae9e53ac9..e78de345d9cc201526470a4ef08ec93348e946df 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -592,6 +592,31 @@ _copySetOp(SetOp *from)
        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
  * ----------------
@@ -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;
index cf8c90ecad64d1849d3160243d2f2a49f828c371..16b64851097948502771c885d0f653a30c03d9cd 100644 (file)
@@ -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;
index 44de7fc6e2091d5f5c9c335fcb0a4950aa90736c..6a50709541bf235bca58054551aef8043a1953b3 100644 (file)
@@ -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:
index eb005121cd55f05cd0aa6eb95e6ce295493af7b4..a865da61b92b9d2d68c2e5bd796c051d78fa9c91 100644 (file)
@@ -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,
index d73ca9a34ac1b4f4121f1a8779c3b1ddf1e3ff07..f9c70f7137d4ea3527036d4a8567c24109fc3b19 100644 (file)
@@ -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;
 }
 
index 14c9dad3ef321cdd7968614ef1b573cda178850f..deb020e2565b5451a84e8c4f65041801a9ba4acd 100644 (file)
@@ -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:
 
                        /*
index 03e38371df5e7bec60ba03234ea6ec152f32444e..296164acb8916d9eaa4ec30436663363bad338b8 100644 (file)
@@ -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;
 
index c08ddbc6782b6888f72116b0705b8b0bc01fc7a0..a83e0202335625a7f79b0dcc2e4e48f829e021db 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -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.
                 */
index 172f6fe467c7bf18e374cc7f637f789aee971e42..62848d773480629d99ef935f332565fe9405a1b1 100644 (file)
@@ -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);
index 7ab3985f3e72cb984d81f737ee00f0386d6c5d41..70dfe9706bc34c6046a88ab39e7e35a69fb4ccde 100644 (file)
@@ -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
index 5c330915e754454de0b10c9c778e7b3025792729..9fe59b031a76f34e4dacd6be68f6bc2a10495460 100644 (file)
@@ -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 (file)
index 0000000..4ed1654
--- /dev/null
@@ -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 */
index 06de4be54cb5c805bfb21e64627ac194ef107a0c..14cd94baa076e6aef2c1947b42fea6e11279ba38 100644 (file)
@@ -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
index 43bb8b733ee3800a76d425d516914adfbfa955af..b06335290f451143ae566cde781566eb0fb577be 100644 (file)
@@ -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)
index d8e3df4829aaf1c223ee958c946a00d0172d5946..177ab73a13bb0149b1bc4178be8b3de99cb3a0d3 100644 (file)
@@ -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;
  *
  * ----------------------------------------------------------------
@@ -375,6 +376,18 @@ typedef struct SetOp
        SetOpState *setopstate;
 } 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
  * ----------------
index 015590a5ee2a6214f0c4e11672f7eaa3baec1dd4..2d5de6450463b415d8ad0946c32af28dde99f348 100644 (file)
@@ -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);