Added LIMIT/OFFSET functionality including new regression test for it.

Removed CURRENT keyword for rule queries and changed rules regression
accordingly. CURRENT has beed announced to disappear in v6.5.

Jan
REL6_5_PATCHES
Jan Wieck 27 years ago
parent 54e5d25666
commit be948af2e8
  1. 22
      src/backend/commands/command.c
  2. 144
      src/backend/executor/execMain.c
  3. 7
      src/backend/executor/functions.c
  4. 39
      src/backend/executor/spi.c
  5. 6
      src/backend/parser/analyze.c
  6. 95
      src/backend/parser/gram.y
  7. 7
      src/backend/parser/keywords.c
  8. 8
      src/backend/rewrite/rewriteDefine.c
  9. 12
      src/backend/tcop/pquery.c
  10. 5
      src/include/executor/executor.h
  11. 106
      src/test/regress/expected/limit.out
  12. 26
      src/test/regress/expected/rules.out
  13. 31
      src/test/regress/sql/limit.sql
  14. 26
      src/test/regress/sql/rules.sql
  15. 1
      src/test/regress/sql/tests

@ -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
@ -113,6 +115,21 @@ 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 = (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);

@ -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)
@ -845,6 +949,20 @@ 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.

@ -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

@ -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)

@ -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;

@ -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.

@ -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},

@ -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 $
*
*-------------------------------------------------------------------------
*/
@ -311,6 +311,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.
*/

@ -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);
}

@ -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);

@ -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)

@ -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
-+--

@ -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;

@ -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

@ -62,6 +62,7 @@ select_views
alter_table
portals_p2
rules
limit
install_plpgsql
plpgsql
temp

Loading…
Cancel
Save