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
This commit is contained in:
Jan Wieck 1999-02-08 14:14:32 +00:00
parent 54e5d25666
commit be948af2e8
15 changed files with 472 additions and 63 deletions

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

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

View 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

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

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

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

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

View 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 $
*
*-------------------------------------------------------------------------
*/
@ -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.
*/

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

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

View File

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

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

View File

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

View 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

View File

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