OSDN Git Service

Added LIMIT/OFFSET functionality including new regression test for it.
authorJan Wieck <JanWieck@Yahoo.com>
Mon, 8 Feb 1999 14:14:32 +0000 (14:14 +0000)
committerJan Wieck <JanWieck@Yahoo.com>
Mon, 8 Feb 1999 14:14:32 +0000 (14:14 +0000)
Removed CURRENT keyword for rule queries and changed rules regression
accordingly. CURRENT has beed announced to disappear in v6.5.

Jan

15 files changed:
src/backend/commands/command.c
src/backend/executor/execMain.c
src/backend/executor/functions.c
src/backend/executor/spi.c
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/backend/parser/keywords.c
src/backend/rewrite/rewriteDefine.c
src/backend/tcop/pquery.c
src/include/executor/executor.h
src/test/regress/expected/limit.out [new file with mode: 0644]
src/test/regress/expected/rules.out
src/test/regress/sql/limit.sql [new file with mode: 0644]
src/test/regress/sql/rules.sql
src/test/regress/sql/tests

index 1a65f4d..310325d 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.36 1999/02/03 21:16:02 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.37 1999/02/08 14:14:08 wieck Exp $
  *
  * NOTES
  *       The PortalExecutorHeapMemory crap needs to be eliminated
@@ -40,6 +40,7 @@
 #include "utils/portal.h"
 #include "utils/syscache.h"
 #include "miscadmin.h"
+#include "string.h"
 
 /* ----------------
  *             PortalExecutorHeapMemory stuff
@@ -102,6 +103,7 @@ PerformPortalFetch(char *name,
        int                     feature;
        QueryDesc  *queryDesc;
        MemoryContext context;
+       Const           limcount;
 
        /* ----------------
         *      sanity checks
@@ -114,6 +116,21 @@ PerformPortalFetch(char *name,
        }
 
        /* ----------------
+        *  Create a const node from the given count value
+        * ----------------
+        */
+       memset(&limcount, 0, sizeof(limcount));
+       limcount.type       = T_Const;
+       limcount.consttype  = INT4OID;
+       limcount.constlen   = sizeof(int4);
+       limcount.constvalue = (Datum)count;
+       limcount.constisnull    = FALSE;
+       limcount.constbyval = TRUE;
+       limcount.constisset = FALSE;
+       limcount.constiscast    = FALSE;
+
+
+       /* ----------------
         *      get the portal from the portal name
         * ----------------
         */
@@ -176,7 +193,8 @@ PerformPortalFetch(char *name,
        PortalExecutorHeapMemory = (MemoryContext)
                PortalGetHeapMemory(portal);
 
-       ExecutorRun(queryDesc, PortalGetState(portal), feature, count);
+       ExecutorRun(queryDesc, PortalGetState(portal), feature, 
+                               (Node *)NULL, (Node *)&limcount);
 
        if (dest == None)                       /* MOVE */
                pfree(queryDesc);
index 5ce9090..e11869c 100644 (file)
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.75 1999/02/07 16:17:11 wieck Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.76 1999/02/08 14:14:09 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -60,21 +60,27 @@ void ExecCheckPerms(CmdType operation, int resultRelation, List *rangeTable,
 
 
 /* decls for local routines only used within this module */
-static TupleDesc InitPlan(CmdType operation, Query *parseTree,
-                Plan *plan, EState *estate);
-static void EndPlan(Plan *plan, EState *estate);
+static TupleDesc InitPlan(CmdType operation,
+                                               Query *parseTree,
+                                               Plan *plan,
+                                               EState *estate);
+static void EndPlan(Plan *plan,
+                                               EState *estate);
 static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan,
-                       CmdType operation, int numberTuples, ScanDirection direction,
-                       DestReceiver *destfunc);
+                                               CmdType operation, 
+                                               int offsetTuples,
+                                               int numberTuples,
+                                               ScanDirection direction,
+                                               DestReceiver *destfunc);
 static void ExecRetrieve(TupleTableSlot *slot,
-                                                DestReceiver *destfunc,
-                                                EState *estate);
+                                               DestReceiver *destfunc,
+                                               EState *estate);
 static void ExecAppend(TupleTableSlot *slot, ItemPointer tupleid,
-                  EState *estate);
+                                               EState *estate);
 static void ExecDelete(TupleTableSlot *slot, ItemPointer tupleid,
-                  EState *estate);
+                                               EState *estate);
 static void ExecReplace(TupleTableSlot *slot, ItemPointer tupleid,
-                       EState *estate);
+                                               EState *estate);
 
 TupleTableSlot *EvalPlanQual(EState *estate, Index rti, ItemPointer tid);
 static TupleTableSlot *EvalPlanQualNext(EState *estate);
@@ -187,13 +193,16 @@ ExecutorStart(QueryDesc *queryDesc, EState *estate)
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
-ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
+ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, 
+                                       Node *limoffset, Node *limcount)
 {
        CmdType                 operation;
        Plan               *plan;
        TupleTableSlot *result;
        CommandDest             dest;
        DestReceiver   *destfunc;
+       int                             offset = 0;
+       int                             count = 0;
 
        /******************
         *      sanity checks
@@ -222,6 +231,96 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
         */
        (*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)
        {
 
@@ -229,7 +328,8 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
                        result = ExecutePlan(estate,
                                                                 plan,
                                                                 operation,
-                                                                ALL_TUPLES,
+                                                                offset,
+                                                                count,
                                                                 ForwardScanDirection,
                                                                 destfunc);
                        break;
@@ -237,6 +337,7 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
                        result = ExecutePlan(estate,
                                                                 plan,
                                                                 operation,
+                                                                offset,
                                                                 count,
                                                                 ForwardScanDirection,
                                                                 destfunc);
@@ -250,6 +351,7 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
                        result = ExecutePlan(estate,
                                                                 plan,
                                                                 operation,
+                                                                offset,
                                                                 count,
                                                                 BackwardScanDirection,
                                                                 destfunc);
@@ -264,6 +366,7 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
                        result = ExecutePlan(estate,
                                                                 plan,
                                                                 operation,
+                                                                0,
                                                                 ONE_TUPLE,
                                                                 ForwardScanDirection,
                                                                 destfunc);
@@ -784,6 +887,7 @@ static TupleTableSlot *
 ExecutePlan(EState *estate,
                        Plan *plan,
                        CmdType operation,
+                       int offsetTuples,
                        int numberTuples,
                        ScanDirection direction,
                        DestReceiver* destfunc)
@@ -846,6 +950,20 @@ lnext:;
                }
 
                /******************
+                *  For now we completely execute the plan and skip
+                *  result tuples if requested by LIMIT offset.
+                *  Finally we should try to do it in deeper levels
+                *  if possible (during index scan)
+                *  - Jan
+                ******************
+                */
+               if (offsetTuples > 0)
+               {
+                       --offsetTuples;
+                       continue;
+               }
+
+               /******************
                 *              if we have a junk filter, then project a new
                 *              tuple with the junk removed.
                 *
index 51ea665..7108a1c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.22 1999/02/03 21:16:12 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.23 1999/02/08 14:14:09 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -130,6 +130,9 @@ init_execution_state(FunctionCachePtr fcache,
                                                                         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;
@@ -199,7 +202,7 @@ postquel_getnext(execution_state *es)
 
        feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
 
-       return ExecutorRun(es->qd, es->estate, feature, 0);
+       return ExecutorRun(es->qd, es->estate, feature, (Node *)NULL, (Node *)NULL);
 }
 
 static void
index 7ca2a6c..397f68a 100644 (file)
@@ -3,7 +3,7 @@
  * spi.c--
  *                             Server Programming Interface
  *
- * $Id: spi.c,v 1.32 1999/01/27 16:15:20 wieck Exp $
+ * $Id: spi.c,v 1.33 1999/02/08 14:14:10 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -779,6 +779,8 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
        bool            isRetrieveIntoRelation = false;
        char       *intoName = NULL;
        int                     res;
+       Const           tcount_const;
+       Node            *count = NULL;
 
        switch (operation)
        {
@@ -813,6 +815,39 @@ _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
@@ -833,7 +868,7 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
                return SPI_OK_CURSOR;
        }
 
-       ExecutorRun(queryDesc, state, EXEC_FOR, tcount);
+       ExecutorRun(queryDesc, state, EXEC_FOR, parseTree->limitOffset, count);
 
        _SPI_current->processed = state->es_processed;
        if (operation == CMD_SELECT && queryDesc->dest == SPI)
index 6232a2d..ada18bb 100644 (file)
@@ -5,7 +5,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- *  $Id: analyze.c,v 1.97 1999/02/02 03:44:32 momjian Exp $
+ *  $Id: analyze.c,v 1.98 1999/02/08 14:14:11 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -205,7 +205,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
 
                case T_SelectStmt:
                        if (!((SelectStmt *) parseTree)->portalname)
+                       {
                                result = transformSelectStmt(pstate, (SelectStmt *) parseTree);
+                               result->limitOffset = ((SelectStmt *)parseTree)->limitOffset;
+                               result->limitCount = ((SelectStmt *)parseTree)->limitCount;
+                       }
                        else
                                result = transformCursorStmt(pstate, (SelectStmt *) parseTree);
                        break;
index e88b907..b649ceb 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.53 1999/02/07 19:02:19 wieck Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.54 1999/02/08 14:14:12 wieck Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -48,6 +48,7 @@
 #include "storage/lmgr.h"
 #include "utils/numeric.h"
 #include "parser/analyze.h"
+#include "catalog/pg_type.h"
 
 #ifdef MULTIBYTE
 #include "mb/pg_wchar.h"
@@ -168,7 +169,8 @@ Oid param_type(int t); /* used in parse_expr.c */
                sort_clause, sortby_list, index_params, index_list, name_list,
                from_clause, from_list, opt_array_bounds, nest_array_bounds,
                expr_list, attrs, res_target_list, res_target_list2,
-               def_list, opt_indirection, group_clause, TriggerFuncArgs
+               def_list, opt_indirection, group_clause, TriggerFuncArgs,
+               opt_select_limit
 
 %type <node>   func_return
 %type <boolean>        set_opt
@@ -197,6 +199,8 @@ Oid param_type(int t); /* used in parse_expr.c */
 
 %type <ival>   fetch_how_many
 
+%type <node>   select_limit_value, select_offset_value
+
 %type <list>   OptSeqList
 %type <defelt> OptSeqElem
 
@@ -306,9 +310,10 @@ Oid        param_type(int t); /* used in parse_expr.c */
                DATABASE, DELIMITERS, DO, EACH, ENCODING, EXPLAIN, EXTEND,
                FORWARD, FUNCTION, HANDLER,
                INCREMENT, INDEX, INHERITS, INSTEAD, ISNULL,
-               LANCOMPILER, LISTEN, LOAD, LOCATION, LOCK_P, MAXVALUE, MINVALUE, MOVE,
+               LANCOMPILER, LIMIT, LISTEN, LOAD, LOCATION, LOCK_P,
+               MAXVALUE, MINVALUE, MOVE,
                NEW, NOCREATEDB, NOCREATEUSER, NONE, NOTHING, NOTIFY, NOTNULL,
-               OIDS, OPERATOR, PASSWORD, PROCEDURAL,
+               OFFSET, OIDS, OPERATOR, PASSWORD, PROCEDURAL,
                RECIPE, RENAME, RESET, RETURNS, ROW, RULE,
                SEQUENCE, SERIAL, SETOF, SHOW, START, STATEMENT, STDIN, STDOUT, TRUSTED, 
                UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION
@@ -2731,7 +2736,7 @@ opt_of:  OF columnList
  * 
  * The rule returns a SelectStmt Node having the set operations attached to 
  * unionClause and intersectClause (NIL if no set operations were present) */ 
-SelectStmt:      select_w_o_sort sort_clause for_update_clause
+SelectStmt:      select_w_o_sort sort_clause for_update_clause opt_select_limit
                        {
                                /* There were no set operations, so just attach the sortClause */
                                if IsA($1, SelectStmt)
@@ -2739,6 +2744,8 @@ SelectStmt:         select_w_o_sort sort_clause for_update_clause
                                  SelectStmt *n = (SelectStmt *)$1;
                                  n->sortClause = $2;
                                  n->forUpdate = $3;
+                                 n->limitOffset = nth(0, $4);
+                                 n->limitCount = nth(1, $4);
                                  $$ = (Node *)n;
                 }
                                /* There were set operations: The root of the operator tree
@@ -2920,6 +2927,84 @@ OptUseOp:  USING Op                                                              { $$ = $2; }
                | /*EMPTY*/                                                             { $$ = "<"; /*default*/ }
                ;
 
+
+opt_select_limit:      LIMIT select_limit_value ',' select_offset_value
+                       { $$ = lappend(lappend(NIL, $4), $2); }
+               | LIMIT select_limit_value OFFSET select_offset_value
+                       { $$ = lappend(lappend(NIL, $4), $2); }
+               | LIMIT select_limit_value
+                       { $$ = lappend(lappend(NIL, NULL), $2); }
+               | OFFSET select_offset_value LIMIT select_limit_value
+                       { $$ = lappend(lappend(NIL, $2), $4); }
+               | OFFSET select_offset_value
+                       { $$ = lappend(lappend(NIL, $2), NULL); }
+               | /* EMPTY */
+                       { $$ = lappend(lappend(NIL, NULL), NULL); }
+               ;
+
+select_limit_value:            Iconst
+                       {
+                               Const   *n = makeNode(Const);
+
+                               if ($1 < 1)
+                                       elog(ERROR, "selection limit must be ALL or a positive integer value > 0");
+
+                               n->consttype    = INT4OID;
+                               n->constlen             = sizeof(int4);
+                               n->constvalue   = (Datum)$1;
+                               n->constisnull  = FALSE;
+                               n->constbyval   = TRUE;
+                               n->constisset   = FALSE;
+                               n->constiscast  = FALSE;
+                               $$ = (Node *)n;
+                       }
+               | ALL
+                       {
+                               Const   *n = makeNode(Const);
+
+                               n->consttype    = INT4OID;
+                               n->constlen             = sizeof(int4);
+                               n->constvalue   = (Datum)0;
+                               n->constisnull  = FALSE;
+                               n->constbyval   = TRUE;
+                               n->constisset   = FALSE;
+                               n->constiscast  = FALSE;
+                               $$ = (Node *)n;
+                       }
+               | PARAM
+                       {
+                               Param   *n = makeNode(Param);
+
+                               n->paramkind    = PARAM_NUM;
+                               n->paramid              = $1;
+                               n->paramtype    = INT4OID;
+                               $$ = (Node *)n;
+                       }
+               ;
+
+select_offset_value:   Iconst
+                       {
+                               Const   *n = makeNode(Const);
+
+                               n->consttype    = INT4OID;
+                               n->constlen             = sizeof(int4);
+                               n->constvalue   = (Datum)$1;
+                               n->constisnull  = FALSE;
+                               n->constbyval   = TRUE;
+                               n->constisset   = FALSE;
+                               n->constiscast  = FALSE;
+                               $$ = (Node *)n;
+                       }
+               | PARAM
+                       {
+                               Param   *n = makeNode(Param);
+
+                               n->paramkind    = PARAM_NUM;
+                               n->paramid              = $1;
+                               n->paramtype    = INT4OID;
+                               $$ = (Node *)n;
+                       }
+               ;
 /*
  *     jimmy bell-style recursive queries aren't supported in the
  *     current system.
index f1e5978..b9af71d 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.52 1999/02/02 03:44:42 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.53 1999/02/08 14:14:13 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -68,9 +68,6 @@ static ScanKeyword ScanKeywords[] = {
        {"createdb", CREATEDB},
        {"createuser", CREATEUSER},
        {"cross", CROSS},
-       {"current", CURRENT},           /* 6.4 to 6.5 is migration time! CURRENT
-                                                                * will be removed in 6.5! Use OLD keyword
-                                                                * in rules. Jan */
        {"current_date", CURRENT_DATE},
        {"current_time", CURRENT_TIME},
        {"current_timestamp", CURRENT_TIMESTAMP},
@@ -139,6 +136,7 @@ static ScanKeyword ScanKeywords[] = {
        {"left", LEFT},
        {"level", LEVEL},
        {"like", LIKE},
+       {"limit", LIMIT},
        {"listen", LISTEN},
        {"load", LOAD},
        {"local", LOCAL},
@@ -168,6 +166,7 @@ static ScanKeyword ScanKeywords[] = {
        {"nullif", NULLIF},
        {"numeric", NUMERIC},
        {"of", OF},
+       {"offset", OFFSET},
        {"oids", OIDS},
        {"old", CURRENT},
        {"on", ON},
index b20b6f6..a114540 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.23 1998/10/06 22:14:14 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.24 1999/02/08 14:14:13 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -312,6 +312,12 @@ DefineQueryRewrite(RuleStmt *stmt)
                heap_close(event_relation);
 
                /*
+                * 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.
                 */
                sprintf(expected_name, "_RET%s", event_obj->relname);
index 577a859..abe3c31 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.19 1998/09/02 23:05:34 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.20 1999/02/08 14:14:14 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,7 +40,8 @@
 #include "commands/command.h"
 
 static char *CreateOperationTag(int operationType);
-static void ProcessQueryDesc(QueryDesc *queryDesc);
+static void ProcessQueryDesc(QueryDesc *queryDesc, Node *limoffset,
+                                                                                                  Node *limcount);
 
 
 /* ----------------------------------------------------------------
@@ -205,7 +206,7 @@ ProcessPortal(char *portalName,
  * ----------------------------------------------------------------
  */
 static void
-ProcessQueryDesc(QueryDesc *queryDesc)
+ProcessQueryDesc(QueryDesc *queryDesc, Node *limoffset, Node *limcount)
 {
        Query      *parseTree;
        Plan       *plan;
@@ -330,7 +331,7 @@ ProcessQueryDesc(QueryDesc *queryDesc)
         *       actually run the plan..
         * ----------------
         */
-       ExecutorRun(queryDesc, state, EXEC_RUN, 0);
+       ExecutorRun(queryDesc, state, EXEC_RUN, limoffset, limcount);
 
        /* save infos for EndCommand */
        UpdateCommandInfo(operation, state->es_lastoid, state->es_processed);
@@ -373,5 +374,6 @@ ProcessQuery(Query *parsetree,
                print_plan(plan, parsetree);
        }
        else
-               ProcessQueryDesc(queryDesc);
+               ProcessQueryDesc(queryDesc, parsetree->limitOffset,
+                                                                       parsetree->limitCount);
 }
index 68d8989..2b015f4 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: executor.h,v 1.29 1999/02/07 16:17:12 wieck Exp $
+ * $Id: executor.h,v 1.30 1999/02/08 14:14:18 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -83,7 +83,8 @@ extern HeapTuple ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot);
  * prototypes from functions in execMain.c
  */
 extern TupleDesc ExecutorStart(QueryDesc *queryDesc, EState *estate);
-extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count);
+extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc, EState *estate, 
+                                                       int feature, Node *limoffset, Node *limcount);
 extern void ExecutorEnd(QueryDesc *queryDesc, EState *estate);
 extern void ExecConstraints(char *caller, Relation rel, HeapTuple tuple, 
                                                                                        EState *estate);
diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out
new file mode 100644 (file)
index 0000000..a46515d
--- /dev/null
@@ -0,0 +1,106 @@
+QUERY: SELECT ''::text AS two, unique1, unique2, stringu1
+               FROM onek WHERE unique1 > 50
+               ORDER BY unique1 LIMIT 2;
+two|unique1|unique2|stringu1
+---+-------+-------+--------
+   |     51|     76|ZBAAAA  
+   |     52|    985|ACAAAA  
+(2 rows)
+
+QUERY: SELECT ''::text AS five, unique1, unique2, stringu1
+               FROM onek WHERE unique1 > 60
+               ORDER BY unique1 LIMIT 5;
+five|unique1|unique2|stringu1
+----+-------+-------+--------
+    |     61|    560|JCAAAA  
+    |     62|    633|KCAAAA  
+    |     63|    296|LCAAAA  
+    |     64|    479|MCAAAA  
+    |     65|     64|NCAAAA  
+(5 rows)
+
+QUERY: SELECT ''::text AS two, unique1, unique2, stringu1
+               FROM onek WHERE unique1 > 60 AND unique1 < 63
+               ORDER BY unique1 LIMIT 5;
+two|unique1|unique2|stringu1
+---+-------+-------+--------
+   |     61|    560|JCAAAA  
+   |     62|    633|KCAAAA  
+(2 rows)
+
+QUERY: SELECT ''::text AS three, unique1, unique2, stringu1
+               FROM onek WHERE unique1 > 100
+               ORDER BY unique1 LIMIT 3 OFFSET 20;
+three|unique1|unique2|stringu1
+-----+-------+-------+--------
+     |    121|    700|REAAAA  
+     |    122|    519|SEAAAA  
+     |    123|    777|TEAAAA  
+(3 rows)
+
+QUERY: SELECT ''::text AS zero, unique1, unique2, stringu1
+               FROM onek WHERE unique1 < 50
+               ORDER BY unique1 DESC LIMIT 8 OFFSET 99;
+zero|unique1|unique2|stringu1
+----+-------+-------+--------
+(0 rows)
+
+QUERY: SELECT ''::text AS eleven, unique1, unique2, stringu1
+               FROM onek WHERE unique1 < 50
+               ORDER BY unique1 DESC LIMIT 20 OFFSET 39;
+eleven|unique1|unique2|stringu1
+------+-------+-------+--------
+      |     10|    520|KAAAAA  
+      |      9|     49|JAAAAA  
+      |      8|    653|IAAAAA  
+      |      7|    647|HAAAAA  
+      |      6|    978|GAAAAA  
+      |      5|    541|FAAAAA  
+      |      4|    833|EAAAAA  
+      |      3|    431|DAAAAA  
+      |      2|    326|CAAAAA  
+      |      1|    214|BAAAAA  
+      |      0|    998|AAAAAA  
+(11 rows)
+
+QUERY: SELECT ''::text AS ten, unique1, unique2, stringu1
+               FROM onek
+               ORDER BY unique1 OFFSET 990;
+ten|unique1|unique2|stringu1
+---+-------+-------+--------
+   |    990|    369|CMAAAA  
+   |    991|    426|DMAAAA  
+   |    992|    363|EMAAAA  
+   |    993|    661|FMAAAA  
+   |    994|    695|GMAAAA  
+   |    995|    144|HMAAAA  
+   |    996|    258|IMAAAA  
+   |    997|     21|JMAAAA  
+   |    998|    549|KMAAAA  
+   |    999|    152|LMAAAA  
+(10 rows)
+
+QUERY: SELECT ''::text AS five, unique1, unique2, stringu1
+               FROM onek
+               ORDER BY unique1 OFFSET 990 LIMIT 5;
+five|unique1|unique2|stringu1
+----+-------+-------+--------
+    |    990|    369|CMAAAA  
+    |    991|    426|DMAAAA  
+    |    992|    363|EMAAAA  
+    |    993|    661|FMAAAA  
+    |    994|    695|GMAAAA  
+(5 rows)
+
+QUERY: SELECT ''::text AS five, unique1, unique2, stringu1
+               FROM onek
+               ORDER BY unique1 LIMIT 5, 900;
+five|unique1|unique2|stringu1
+----+-------+-------+--------
+    |    900|    913|QIAAAA  
+    |    901|    931|RIAAAA  
+    |    902|    702|SIAAAA  
+    |    903|    641|TIAAAA  
+    |    904|    793|UIAAAA  
+(5 rows)
+
index 68dc16c..7fb9fd2 100644 (file)
@@ -6,39 +6,39 @@ QUERY: create rule rtest_v1_ins as on insert to rtest_v1 do instead
        insert into rtest_t1 values (new.a, new.b);
 QUERY: create rule rtest_v1_upd as on update to rtest_v1 do instead
        update rtest_t1 set a = new.a, b = new.b
-       where a = current.a;
+       where a = old.a;
 QUERY: create rule rtest_v1_del as on delete to rtest_v1 do instead
-       delete from rtest_t1 where a = current.a;
+       delete from rtest_t1 where a = old.a;
 QUERY: create table rtest_system (sysname text, sysdesc text);
 QUERY: create table rtest_interface (sysname text, ifname text);
 QUERY: create table rtest_person (pname text, pdesc text);
 QUERY: create table rtest_admin (pname text, sysname text);
 QUERY: create rule rtest_sys_upd as on update to rtest_system do (
        update rtest_interface set sysname = new.sysname
-               where sysname = current.sysname;
+               where sysname = old.sysname;
        update rtest_admin set sysname = new.sysname
-               where sysname = current.sysname
+               where sysname = old.sysname
        );
 QUERY: create rule rtest_sys_del as on delete to rtest_system do (
-       delete from rtest_interface where sysname = current.sysname;
-       delete from rtest_admin where sysname = current.sysname;
+       delete from rtest_interface where sysname = old.sysname;
+       delete from rtest_admin where sysname = old.sysname;
        );
 QUERY: create rule rtest_pers_upd as on update to rtest_person do
-       update rtest_admin set pname = new.pname where pname = current.pname;
+       update rtest_admin set pname = new.pname where pname = old.pname;
 QUERY: create rule rtest_pers_del as on delete to rtest_person do
-       delete from rtest_admin where pname = current.pname;
+       delete from rtest_admin where pname = old.pname;
 QUERY: create table rtest_emp (ename char(20), salary money);
 QUERY: create table rtest_emplog (ename char(20), who name, action char(10), newsal money, oldsal money);
 QUERY: create table rtest_empmass (ename char(20), salary money);
 QUERY: create rule rtest_emp_ins as on insert to rtest_emp do
        insert into rtest_emplog values (new.ename, current_user,
                        'hired', new.salary, '0.00');
-QUERY: create rule rtest_emp_upd as on update to rtest_emp where new.salary != current.salary do
+QUERY: create rule rtest_emp_upd as on update to rtest_emp where new.salary != old.salary do
        insert into rtest_emplog values (new.ename, current_user,
-                       'honored', new.salary, current.salary);
+                       'honored', new.salary, old.salary);
 QUERY: create rule rtest_emp_del as on delete to rtest_emp do
-       insert into rtest_emplog values (current.ename, current_user,
-                       'fired', '0.00', current.salary);
+       insert into rtest_emplog values (old.ename, current_user,
+                       'fired', '0.00', old.salary);
 QUERY: create table rtest_t4 (a int4, b text);
 QUERY: create table rtest_t5 (a int4, b text);
 QUERY: create table rtest_t6 (a int4, b text);
@@ -131,7 +131,7 @@ a| b
 QUERY: delete from rtest_v1 where b = 12;
 QUERY: select * from rtest_v1;
 ** Remember the delete rule on rtest_v1: It says
-** DO INSTEAD DELETE FROM rtest_t1 WHERE a = current.a
+** DO INSTEAD DELETE FROM rtest_t1 WHERE a = old.a
 ** So this time both rows with a = 2 must get deleted
 a| b
 -+--
diff --git a/src/test/regress/sql/limit.sql b/src/test/regress/sql/limit.sql
new file mode 100644 (file)
index 0000000..eb3bf0e
--- /dev/null
@@ -0,0 +1,31 @@
+--
+-- Check the LIMIT/OFFSET feature of SELECT
+--
+
+SELECT ''::text AS two, unique1, unique2, stringu1 
+               FROM onek WHERE unique1 > 50 
+               ORDER BY unique1 LIMIT 2;
+SELECT ''::text AS five, unique1, unique2, stringu1 
+               FROM onek WHERE unique1 > 60 
+               ORDER BY unique1 LIMIT 5;
+SELECT ''::text AS two, unique1, unique2, stringu1 
+               FROM onek WHERE unique1 > 60 AND unique1 < 63
+               ORDER BY unique1 LIMIT 5;
+SELECT ''::text AS three, unique1, unique2, stringu1 
+               FROM onek WHERE unique1 > 100 
+               ORDER BY unique1 LIMIT 3 OFFSET 20;
+SELECT ''::text AS zero, unique1, unique2, stringu1 
+               FROM onek WHERE unique1 < 50 
+               ORDER BY unique1 DESC LIMIT 8 OFFSET 99;
+SELECT ''::text AS eleven, unique1, unique2, stringu1 
+               FROM onek WHERE unique1 < 50 
+               ORDER BY unique1 DESC LIMIT 20 OFFSET 39;
+SELECT ''::text AS ten, unique1, unique2, stringu1 
+               FROM onek
+               ORDER BY unique1 OFFSET 990;
+SELECT ''::text AS five, unique1, unique2, stringu1 
+               FROM onek
+               ORDER BY unique1 OFFSET 990 LIMIT 5;
+SELECT ''::text AS five, unique1, unique2, stringu1 
+               FROM onek
+               ORDER BY unique1 LIMIT 5, 900;
index e148489..319a206 100644 (file)
@@ -16,9 +16,9 @@ create rule rtest_v1_ins as on insert to rtest_v1 do instead
        insert into rtest_t1 values (new.a, new.b);
 create rule rtest_v1_upd as on update to rtest_v1 do instead
        update rtest_t1 set a = new.a, b = new.b
-       where a = current.a;
+       where a = old.a;
 create rule rtest_v1_del as on delete to rtest_v1 do instead
-       delete from rtest_t1 where a = current.a;
+       delete from rtest_t1 where a = old.a;
 
 --
 -- Tables and rules for the constraint update/delete test
@@ -35,21 +35,21 @@ create table rtest_admin (pname text, sysname text);
 
 create rule rtest_sys_upd as on update to rtest_system do (
        update rtest_interface set sysname = new.sysname 
-               where sysname = current.sysname;
+               where sysname = old.sysname;
        update rtest_admin set sysname = new.sysname 
-               where sysname = current.sysname
+               where sysname = old.sysname
        );
 
 create rule rtest_sys_del as on delete to rtest_system do (
-       delete from rtest_interface where sysname = current.sysname;
-       delete from rtest_admin where sysname = current.sysname;
+       delete from rtest_interface where sysname = old.sysname;
+       delete from rtest_admin where sysname = old.sysname;
        );
 
 create rule rtest_pers_upd as on update to rtest_person do 
-       update rtest_admin set pname = new.pname where pname = current.pname;
+       update rtest_admin set pname = new.pname where pname = old.pname;
 
 create rule rtest_pers_del as on delete to rtest_person do 
-       delete from rtest_admin where pname = current.pname;
+       delete from rtest_admin where pname = old.pname;
 
 --
 -- Tables and rules for the logging test
@@ -62,13 +62,13 @@ create rule rtest_emp_ins as on insert to rtest_emp do
        insert into rtest_emplog values (new.ename, current_user,
                        'hired', new.salary, '0.00');
 
-create rule rtest_emp_upd as on update to rtest_emp where new.salary != current.salary do
+create rule rtest_emp_upd as on update to rtest_emp where new.salary != old.salary do
        insert into rtest_emplog values (new.ename, current_user,
-                       'honored', new.salary, current.salary);
+                       'honored', new.salary, old.salary);
 
 create rule rtest_emp_del as on delete to rtest_emp do
-       insert into rtest_emplog values (current.ename, current_user,
-                       'fired', '0.00', current.salary);
+       insert into rtest_emplog values (old.ename, current_user,
+                       'fired', '0.00', old.salary);
 
 --
 -- Tables and rules for the multiple cascaded qualified instead
@@ -177,7 +177,7 @@ insert into rtest_v1 values (2, 12);
 insert into rtest_v1 values (2, 13);
 select * from rtest_v1;
 ** Remember the delete rule on rtest_v1: It says
-** DO INSTEAD DELETE FROM rtest_t1 WHERE a = current.a
+** DO INSTEAD DELETE FROM rtest_t1 WHERE a = old.a
 ** So this time both rows with a = 2 must get deleted
 \p
 \r
index ddc2365..44529af 100644 (file)
@@ -62,6 +62,7 @@ select_views
 alter_table
 portals_p2
 rules
+limit
 install_plpgsql
 plpgsql
 temp