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