Re-implement LIMIT/OFFSET as a plan node type, instead of a hack in
authorTom Lane
Thu, 26 Oct 2000 21:38:24 +0000 (21:38 +0000)
committerTom Lane
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);