Commit be948af2 authored by Jan Wieck's avatar Jan Wieck

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
parent 54e5d256
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* The PortalExecutorHeapMemory crap needs to be eliminated * The PortalExecutorHeapMemory crap needs to be eliminated
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include "utils/portal.h" #include "utils/portal.h"
#include "utils/syscache.h" #include "utils/syscache.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "string.h"
/* ---------------- /* ----------------
* PortalExecutorHeapMemory stuff * PortalExecutorHeapMemory stuff
...@@ -102,6 +103,7 @@ PerformPortalFetch(char *name, ...@@ -102,6 +103,7 @@ PerformPortalFetch(char *name,
int feature; int feature;
QueryDesc *queryDesc; QueryDesc *queryDesc;
MemoryContext context; MemoryContext context;
Const limcount;
/* ---------------- /* ----------------
* sanity checks * sanity checks
...@@ -113,6 +115,21 @@ PerformPortalFetch(char *name, ...@@ -113,6 +115,21 @@ PerformPortalFetch(char *name,
return; 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 * get the portal from the portal name
* ---------------- * ----------------
...@@ -176,7 +193,8 @@ PerformPortalFetch(char *name, ...@@ -176,7 +193,8 @@ PerformPortalFetch(char *name,
PortalExecutorHeapMemory = (MemoryContext) PortalExecutorHeapMemory = (MemoryContext)
PortalGetHeapMemory(portal); PortalGetHeapMemory(portal);
ExecutorRun(queryDesc, PortalGetState(portal), feature, count); ExecutorRun(queryDesc, PortalGetState(portal), feature,
(Node *)NULL, (Node *)&limcount);
if (dest == None) /* MOVE */ if (dest == None) /* MOVE */
pfree(queryDesc); pfree(queryDesc);
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -60,21 +60,27 @@ void ExecCheckPerms(CmdType operation, int resultRelation, List *rangeTable,
/* decls for local routines only used within this module */ /* decls for local routines only used within this module */
static TupleDesc InitPlan(CmdType operation, Query *parseTree, static TupleDesc InitPlan(CmdType operation,
Plan *plan, EState *estate); Query *parseTree,
static void EndPlan(Plan *plan, EState *estate); Plan *plan,
EState *estate);
static void EndPlan(Plan *plan,
EState *estate);
static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan, static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan,
CmdType operation, int numberTuples, ScanDirection direction, CmdType operation,
DestReceiver *destfunc); int offsetTuples,
int numberTuples,
ScanDirection direction,
DestReceiver *destfunc);
static void ExecRetrieve(TupleTableSlot *slot, static void ExecRetrieve(TupleTableSlot *slot,
DestReceiver *destfunc, DestReceiver *destfunc,
EState *estate); EState *estate);
static void ExecAppend(TupleTableSlot *slot, ItemPointer tupleid, static void ExecAppend(TupleTableSlot *slot, ItemPointer tupleid,
EState *estate); EState *estate);
static void ExecDelete(TupleTableSlot *slot, ItemPointer tupleid, static void ExecDelete(TupleTableSlot *slot, ItemPointer tupleid,
EState *estate); EState *estate);
static void ExecReplace(TupleTableSlot *slot, ItemPointer tupleid, static void ExecReplace(TupleTableSlot *slot, ItemPointer tupleid,
EState *estate); EState *estate);
TupleTableSlot *EvalPlanQual(EState *estate, Index rti, ItemPointer tid); TupleTableSlot *EvalPlanQual(EState *estate, Index rti, ItemPointer tid);
static TupleTableSlot *EvalPlanQualNext(EState *estate); static TupleTableSlot *EvalPlanQualNext(EState *estate);
...@@ -187,13 +193,16 @@ ExecutorStart(QueryDesc *queryDesc, EState *estate) ...@@ -187,13 +193,16 @@ ExecutorStart(QueryDesc *queryDesc, EState *estate)
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
TupleTableSlot * TupleTableSlot *
ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count) ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature,
Node *limoffset, Node *limcount)
{ {
CmdType operation; CmdType operation;
Plan *plan; Plan *plan;
TupleTableSlot *result; TupleTableSlot *result;
CommandDest dest; CommandDest dest;
DestReceiver *destfunc; DestReceiver *destfunc;
int offset = 0;
int count = 0;
/****************** /******************
* sanity checks * sanity checks
...@@ -222,6 +231,96 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count) ...@@ -222,6 +231,96 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
*/ */
(*destfunc->setup) (destfunc, (TupleDesc) NULL); (*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) switch (feature)
{ {
...@@ -229,7 +328,8 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count) ...@@ -229,7 +328,8 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
result = ExecutePlan(estate, result = ExecutePlan(estate,
plan, plan,
operation, operation,
ALL_TUPLES, offset,
count,
ForwardScanDirection, ForwardScanDirection,
destfunc); destfunc);
break; break;
...@@ -237,6 +337,7 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count) ...@@ -237,6 +337,7 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
result = ExecutePlan(estate, result = ExecutePlan(estate,
plan, plan,
operation, operation,
offset,
count, count,
ForwardScanDirection, ForwardScanDirection,
destfunc); destfunc);
...@@ -250,6 +351,7 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count) ...@@ -250,6 +351,7 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
result = ExecutePlan(estate, result = ExecutePlan(estate,
plan, plan,
operation, operation,
offset,
count, count,
BackwardScanDirection, BackwardScanDirection,
destfunc); destfunc);
...@@ -264,6 +366,7 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count) ...@@ -264,6 +366,7 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
result = ExecutePlan(estate, result = ExecutePlan(estate,
plan, plan,
operation, operation,
0,
ONE_TUPLE, ONE_TUPLE,
ForwardScanDirection, ForwardScanDirection,
destfunc); destfunc);
...@@ -784,6 +887,7 @@ static TupleTableSlot * ...@@ -784,6 +887,7 @@ static TupleTableSlot *
ExecutePlan(EState *estate, ExecutePlan(EState *estate,
Plan *plan, Plan *plan,
CmdType operation, CmdType operation,
int offsetTuples,
int numberTuples, int numberTuples,
ScanDirection direction, ScanDirection direction,
DestReceiver* destfunc) DestReceiver* destfunc)
...@@ -845,6 +949,20 @@ lnext:; ...@@ -845,6 +949,20 @@ lnext:;
break; 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 * if we have a junk filter, then project a new
* tuple with the junk removed. * tuple with the junk removed.
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -130,6 +130,9 @@ init_execution_state(FunctionCachePtr fcache,
None); None);
estate = CreateExecutorState(); estate = CreateExecutorState();
if (queryTree->limitOffset != NULL || queryTree->limitCount != NULL)
elog(ERROR, "LIMIT clause from SQL functions not yet implemented");
if (nargs > 0) if (nargs > 0)
{ {
int i; int i;
...@@ -199,7 +202,7 @@ postquel_getnext(execution_state *es) ...@@ -199,7 +202,7 @@ postquel_getnext(execution_state *es)
feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN; 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 static void
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* spi.c-- * spi.c--
* Server Programming Interface * 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) ...@@ -779,6 +779,8 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
bool isRetrieveIntoRelation = false; bool isRetrieveIntoRelation = false;
char *intoName = NULL; char *intoName = NULL;
int res; int res;
Const tcount_const;
Node *count = NULL;
switch (operation) switch (operation)
{ {
...@@ -813,6 +815,39 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount) ...@@ -813,6 +815,39 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
return SPI_ERROR_OPUNKNOWN; 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 */ if (state == NULL) /* plan preparation */
return res; return res;
#ifdef SPI_EXECUTOR_STATS #ifdef SPI_EXECUTOR_STATS
...@@ -833,7 +868,7 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount) ...@@ -833,7 +868,7 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
return SPI_OK_CURSOR; return SPI_OK_CURSOR;
} }
ExecutorRun(queryDesc, state, EXEC_FOR, tcount); ExecutorRun(queryDesc, state, EXEC_FOR, parseTree->limitOffset, count);
_SPI_current->processed = state->es_processed; _SPI_current->processed = state->es_processed;
if (operation == CMD_SELECT && queryDesc->dest == SPI) if (operation == CMD_SELECT && queryDesc->dest == SPI)
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * 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) ...@@ -205,7 +205,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
case T_SelectStmt: case T_SelectStmt:
if (!((SelectStmt *) parseTree)->portalname) if (!((SelectStmt *) parseTree)->portalname)
{
result = transformSelectStmt(pstate, (SelectStmt *) parseTree); result = transformSelectStmt(pstate, (SelectStmt *) parseTree);
result->limitOffset = ((SelectStmt *)parseTree)->limitOffset;
result->limitCount = ((SelectStmt *)parseTree)->limitCount;
}
else else
result = transformCursorStmt(pstate, (SelectStmt *) parseTree); result = transformCursorStmt(pstate, (SelectStmt *) parseTree);
break; break;
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "utils/numeric.h" #include "utils/numeric.h"
#include "parser/analyze.h" #include "parser/analyze.h"
#include "catalog/pg_type.h"
#ifdef MULTIBYTE #ifdef MULTIBYTE
#include "mb/pg_wchar.h" #include "mb/pg_wchar.h"
...@@ -168,7 +169,8 @@ Oid param_type(int t); /* used in parse_expr.c */ ...@@ -168,7 +169,8 @@ Oid param_type(int t); /* used in parse_expr.c */
sort_clause, sortby_list, index_params, index_list, name_list, sort_clause, sortby_list, index_params, index_list, name_list,
from_clause, from_list, opt_array_bounds, nest_array_bounds, from_clause, from_list, opt_array_bounds, nest_array_bounds,
expr_list, attrs, res_target_list, res_target_list2, 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 <node> func_return
%type <boolean> set_opt %type <boolean> set_opt
...@@ -197,6 +199,8 @@ Oid param_type(int t); /* used in parse_expr.c */ ...@@ -197,6 +199,8 @@ Oid param_type(int t); /* used in parse_expr.c */
%type <ival> fetch_how_many %type <ival> fetch_how_many
%type <node> select_limit_value, select_offset_value
%type <list> OptSeqList %type <list> OptSeqList
%type <defelt> OptSeqElem %type <defelt> OptSeqElem
...@@ -306,9 +310,10 @@ Oid param_type(int t); /* used in parse_expr.c */ ...@@ -306,9 +310,10 @@ Oid param_type(int t); /* used in parse_expr.c */
DATABASE, DELIMITERS, DO, EACH, ENCODING, EXPLAIN, EXTEND, DATABASE, DELIMITERS, DO, EACH, ENCODING, EXPLAIN, EXTEND,
FORWARD, FUNCTION, HANDLER, FORWARD, FUNCTION, HANDLER,
INCREMENT, INDEX, INHERITS, INSTEAD, ISNULL, 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, NEW, NOCREATEDB, NOCREATEUSER, NONE, NOTHING, NOTIFY, NOTNULL,
OIDS, OPERATOR, PASSWORD, PROCEDURAL, OFFSET, OIDS, OPERATOR, PASSWORD, PROCEDURAL,
RECIPE, RENAME, RESET, RETURNS, ROW, RULE, RECIPE, RENAME, RESET, RETURNS, ROW, RULE,
SEQUENCE, SERIAL, SETOF, SHOW, START, STATEMENT, STDIN, STDOUT, TRUSTED, SEQUENCE, SERIAL, SETOF, SHOW, START, STATEMENT, STDIN, STDOUT, TRUSTED,
UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION
...@@ -2731,7 +2736,7 @@ opt_of: OF columnList ...@@ -2731,7 +2736,7 @@ opt_of: OF columnList
* *
* The rule returns a SelectStmt Node having the set operations attached to * The rule returns a SelectStmt Node having the set operations attached to
* unionClause and intersectClause (NIL if no set operations were present) */ * 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 */ /* There were no set operations, so just attach the sortClause */
if IsA($1, SelectStmt) if IsA($1, SelectStmt)
...@@ -2739,6 +2744,8 @@ SelectStmt: select_w_o_sort sort_clause for_update_clause ...@@ -2739,6 +2744,8 @@ SelectStmt: select_w_o_sort sort_clause for_update_clause
SelectStmt *n = (SelectStmt *)$1; SelectStmt *n = (SelectStmt *)$1;
n->sortClause = $2; n->sortClause = $2;
n->forUpdate = $3; n->forUpdate = $3;
n->limitOffset = nth(0, $4);
n->limitCount = nth(1, $4);
$$ = (Node *)n; $$ = (Node *)n;
} }
/* There were set operations: The root of the operator tree /* There were set operations: The root of the operator tree
...@@ -2920,6 +2927,84 @@ OptUseOp: USING Op { $$ = $2; } ...@@ -2920,6 +2927,84 @@ OptUseOp: USING Op { $$ = $2; }
| /*EMPTY*/ { $$ = "<"; /*default*/ } | /*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 * jimmy bell-style recursive queries aren't supported in the
* current system. * current system.
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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[] = { ...@@ -68,9 +68,6 @@ static ScanKeyword ScanKeywords[] = {
{"createdb", CREATEDB}, {"createdb", CREATEDB},
{"createuser", CREATEUSER}, {"createuser", CREATEUSER},
{"cross", CROSS}, {"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_date", CURRENT_DATE},
{"current_time", CURRENT_TIME}, {"current_time", CURRENT_TIME},
{"current_timestamp", CURRENT_TIMESTAMP}, {"current_timestamp", CURRENT_TIMESTAMP},
...@@ -139,6 +136,7 @@ static ScanKeyword ScanKeywords[] = { ...@@ -139,6 +136,7 @@ static ScanKeyword ScanKeywords[] = {
{"left", LEFT}, {"left", LEFT},
{"level", LEVEL}, {"level", LEVEL},
{"like", LIKE}, {"like", LIKE},
{"limit", LIMIT},
{"listen", LISTEN}, {"listen", LISTEN},
{"load", LOAD}, {"load", LOAD},
{"local", LOCAL}, {"local", LOCAL},
...@@ -168,6 +166,7 @@ static ScanKeyword ScanKeywords[] = { ...@@ -168,6 +166,7 @@ static ScanKeyword ScanKeywords[] = {
{"nullif", NULLIF}, {"nullif", NULLIF},
{"numeric", NUMERIC}, {"numeric", NUMERIC},
{"of", OF}, {"of", OF},
{"offset", OFFSET},
{"oids", OIDS}, {"oids", OIDS},
{"old", CURRENT}, {"old", CURRENT},
{"on", ON}, {"on", ON},
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -311,6 +311,12 @@ DefineQueryRewrite(RuleStmt *stmt)
heap_close(event_relation); 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. * ... and finally the rule must be named _RETviewname.
*/ */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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 @@ ...@@ -40,7 +40,8 @@
#include "commands/command.h" #include "commands/command.h"
static char *CreateOperationTag(int operationType); 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, ...@@ -205,7 +206,7 @@ ProcessPortal(char *portalName,
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
static void static void
ProcessQueryDesc(QueryDesc *queryDesc) ProcessQueryDesc(QueryDesc *queryDesc, Node *limoffset, Node *limcount)
{ {
Query *parseTree; Query *parseTree;
Plan *plan; Plan *plan;
...@@ -330,7 +331,7 @@ ProcessQueryDesc(QueryDesc *queryDesc) ...@@ -330,7 +331,7 @@ ProcessQueryDesc(QueryDesc *queryDesc)
* actually run the plan.. * actually run the plan..
* ---------------- * ----------------
*/ */
ExecutorRun(queryDesc, state, EXEC_RUN, 0); ExecutorRun(queryDesc, state, EXEC_RUN, limoffset, limcount);
/* save infos for EndCommand */ /* save infos for EndCommand */
UpdateCommandInfo(operation, state->es_lastoid, state->es_processed); UpdateCommandInfo(operation, state->es_lastoid, state->es_processed);
...@@ -373,5 +374,6 @@ ProcessQuery(Query *parsetree, ...@@ -373,5 +374,6 @@ ProcessQuery(Query *parsetree,
print_plan(plan, parsetree); print_plan(plan, parsetree);
} }
else else
ProcessQueryDesc(queryDesc); ProcessQueryDesc(queryDesc, parsetree->limitOffset,
parsetree->limitCount);
} }
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * 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); ...@@ -83,7 +83,8 @@ extern HeapTuple ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot);
* prototypes from functions in execMain.c * prototypes from functions in execMain.c
*/ */
extern TupleDesc ExecutorStart(QueryDesc *queryDesc, EState *estate); 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 ExecutorEnd(QueryDesc *queryDesc, EState *estate);
extern void ExecConstraints(char *caller, Relation rel, HeapTuple tuple, extern void ExecConstraints(char *caller, Relation rel, HeapTuple tuple,
EState *estate); EState *estate);
......
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 ...@@ -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); insert into rtest_t1 values (new.a, new.b);
QUERY: create rule rtest_v1_upd as on update to rtest_v1 do instead QUERY: create rule rtest_v1_upd as on update to rtest_v1 do instead
update rtest_t1 set a = new.a, b = new.b 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 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_system (sysname text, sysdesc text);
QUERY: create table rtest_interface (sysname text, ifname text); QUERY: create table rtest_interface (sysname text, ifname text);
QUERY: create table rtest_person (pname text, pdesc text); QUERY: create table rtest_person (pname text, pdesc text);
QUERY: create table rtest_admin (pname text, sysname text); QUERY: create table rtest_admin (pname text, sysname text);
QUERY: create rule rtest_sys_upd as on update to rtest_system do ( QUERY: create rule rtest_sys_upd as on update to rtest_system do (
update rtest_interface set sysname = new.sysname update rtest_interface set sysname = new.sysname
where sysname = current.sysname; where sysname = old.sysname;
update rtest_admin set sysname = new.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 ( QUERY: create rule rtest_sys_del as on delete to rtest_system do (
delete from rtest_interface where sysname = current.sysname; delete from rtest_interface where sysname = old.sysname;
delete from rtest_admin where sysname = current.sysname; delete from rtest_admin where sysname = old.sysname;
); );
QUERY: create rule rtest_pers_upd as on update to rtest_person do 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 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_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_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 table rtest_empmass (ename char(20), salary money);
QUERY: create rule rtest_emp_ins as on insert to rtest_emp do QUERY: create rule rtest_emp_ins as on insert to rtest_emp do
insert into rtest_emplog values (new.ename, current_user, insert into rtest_emplog values (new.ename, current_user,
'hired', new.salary, '0.00'); '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, 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 QUERY: create rule rtest_emp_del as on delete to rtest_emp do
insert into rtest_emplog values (current.ename, current_user, insert into rtest_emplog values (old.ename, current_user,
'fired', '0.00', current.salary); 'fired', '0.00', old.salary);
QUERY: create table rtest_t4 (a int4, b text); QUERY: create table rtest_t4 (a int4, b text);
QUERY: create table rtest_t5 (a int4, b text); QUERY: create table rtest_t5 (a int4, b text);
QUERY: create table rtest_t6 (a int4, b text); QUERY: create table rtest_t6 (a int4, b text);
...@@ -131,7 +131,7 @@ a| b ...@@ -131,7 +131,7 @@ a| b
QUERY: delete from rtest_v1 where b = 12; QUERY: delete from rtest_v1 where b = 12;
QUERY: select * from rtest_v1; QUERY: select * from rtest_v1;
** Remember the delete rule on rtest_v1: It says ** 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 ** So this time both rows with a = 2 must get deleted
a| b a| b
-+-- -+--
......
--
-- 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 ...@@ -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); insert into rtest_t1 values (new.a, new.b);
create rule rtest_v1_upd as on update to rtest_v1 do instead create rule rtest_v1_upd as on update to rtest_v1 do instead
update rtest_t1 set a = new.a, b = new.b 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 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 -- Tables and rules for the constraint update/delete test
...@@ -35,21 +35,21 @@ create table rtest_admin (pname text, sysname text); ...@@ -35,21 +35,21 @@ create table rtest_admin (pname text, sysname text);
create rule rtest_sys_upd as on update to rtest_system do ( create rule rtest_sys_upd as on update to rtest_system do (
update rtest_interface set sysname = new.sysname update rtest_interface set sysname = new.sysname
where sysname = current.sysname; where sysname = old.sysname;
update rtest_admin set sysname = new.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 ( create rule rtest_sys_del as on delete to rtest_system do (
delete from rtest_interface where sysname = current.sysname; delete from rtest_interface where sysname = old.sysname;
delete from rtest_admin where sysname = current.sysname; delete from rtest_admin where sysname = old.sysname;
); );
create rule rtest_pers_upd as on update to rtest_person do 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 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 -- Tables and rules for the logging test
...@@ -62,13 +62,13 @@ create rule rtest_emp_ins as on insert to rtest_emp do ...@@ -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, insert into rtest_emplog values (new.ename, current_user,
'hired', new.salary, '0.00'); '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, 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 create rule rtest_emp_del as on delete to rtest_emp do
insert into rtest_emplog values (current.ename, current_user, insert into rtest_emplog values (old.ename, current_user,
'fired', '0.00', current.salary); 'fired', '0.00', old.salary);
-- --
-- Tables and rules for the multiple cascaded qualified instead -- Tables and rules for the multiple cascaded qualified instead
...@@ -177,7 +177,7 @@ insert into rtest_v1 values (2, 12); ...@@ -177,7 +177,7 @@ insert into rtest_v1 values (2, 12);
insert into rtest_v1 values (2, 13); insert into rtest_v1 values (2, 13);
select * from rtest_v1; select * from rtest_v1;
** Remember the delete rule on rtest_v1: It says ** 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 ** So this time both rows with a = 2 must get deleted
\p \p
\r \r
......
...@@ -62,6 +62,7 @@ select_views ...@@ -62,6 +62,7 @@ select_views
alter_table alter_table
portals_p2 portals_p2
rules rules
limit
install_plpgsql install_plpgsql
plpgsql plpgsql
temp temp
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment