aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/commands/command.c22
-rw-r--r--src/backend/executor/execMain.c144
-rw-r--r--src/backend/executor/functions.c7
-rw-r--r--src/backend/executor/spi.c39
-rw-r--r--src/backend/parser/analyze.c6
-rw-r--r--src/backend/parser/gram.y95
-rw-r--r--src/backend/parser/keywords.c7
-rw-r--r--src/backend/rewrite/rewriteDefine.c8
-rw-r--r--src/backend/tcop/pquery.c12
-rw-r--r--src/include/executor/executor.h5
-rw-r--r--src/test/regress/expected/limit.out106
-rw-r--r--src/test/regress/expected/rules.out26
-rw-r--r--src/test/regress/sql/limit.sql31
-rw-r--r--src/test/regress/sql/rules.sql26
-rw-r--r--src/test/regress/sql/tests1
15 files changed, 472 insertions, 63 deletions
diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c
index 1a65f4d4d0d..310325d2f81 100644
--- a/src/backend/commands/command.c
+++ b/src/backend/commands/command.c
@@ -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);
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 5ce90902b4c..e11869ce783 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -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.
*
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 51ea6655e9d..7108a1cab52 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.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
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 7ca2a6c21e5..397f68a4055 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -3,7 +3,7 @@
* spi.c--
* Server Programming Interface
*
- * $Id: spi.c,v 1.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)
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 6232a2d067d..ada18bb71d3 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -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;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index e88b9073084..b649ceba36c 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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.
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index f1e59781ff3..b9af71dc46b 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -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},
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index b20b6f69d32..a1145402d86 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -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);
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 577a8590779..abe3c31a9cc 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -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);
}
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 68d89897d09..2b015f4c8b6 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -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
index 00000000000..a46515d3343
--- /dev/null
+++ b/src/test/regress/expected/limit.out
@@ -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)
+
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 68dc16c8690..7fb9fd25aa6 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -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
index 00000000000..eb3bf0e7933
--- /dev/null
+++ b/src/test/regress/sql/limit.sql
@@ -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;
diff --git a/src/test/regress/sql/rules.sql b/src/test/regress/sql/rules.sql
index e148489d9a8..319a2066522 100644
--- a/src/test/regress/sql/rules.sql
+++ b/src/test/regress/sql/rules.sql
@@ -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
diff --git a/src/test/regress/sql/tests b/src/test/regress/sql/tests
index ddc2365d2b4..44529af2b27 100644
--- a/src/test/regress/sql/tests
+++ b/src/test/regress/sql/tests
@@ -62,6 +62,7 @@ select_views
alter_table
portals_p2
rules
+limit
install_plpgsql
plpgsql
temp