1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-10-31 18:11:01 +03:00

Allow expressions (including variables) in LIMIT and OFFSET clauses. Ticket #1096. (CVS 2316)

FossilOrigin-Name: 515e5033a5482f55e7edb66d69ff3da7e234ff2e
This commit is contained in:
danielk1977
2005-02-05 12:48:48 +00:00
parent 99ba19ea08
commit a2dc3b1a5e
10 changed files with 177 additions and 113 deletions

View File

@@ -1,5 +1,5 @@
C Add\sa\snumeric\sversion\snumber.\sTicket\s#1097.\s(CVS\s2315) C Allow\sexpressions\s(including\svariables)\sin\sLIMIT\sand\sOFFSET\sclauses.\sTicket\s#1096.\s(CVS\s2316)
D 2005-02-05T07:33:34 D 2005-02-05T12:48:48
F Makefile.in d928187101fa3d78426cf48ca30e39d0fb714e57 F Makefile.in d928187101fa3d78426cf48ca30e39d0fb714e57
F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457 F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457
F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1 F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1
@@ -35,7 +35,7 @@ F src/build.c fcb437bcda09a57b3fe898dff5ff558e7536621b
F src/date.c f3d1f5cd1503dabf426a198f3ebef5afbc122a7f F src/date.c f3d1f5cd1503dabf426a198f3ebef5afbc122a7f
F src/delete.c 4b94395b52a8f7785acd71135c2ce54f3f5550b3 F src/delete.c 4b94395b52a8f7785acd71135c2ce54f3f5550b3
F src/experimental.c 8cc66b2be6a011055d75ef19ed2584bcfbb585ad F src/experimental.c 8cc66b2be6a011055d75ef19ed2584bcfbb585ad
F src/expr.c 2ed29dc4f8feeb55eafa40d41941ad7909666863 F src/expr.c 1b6b6b16bcb6a6dcc4a5df451d9e652f84b269ae
F src/func.c f096b6771cc0aaa11790aca95773a50a8f74ba73 F src/func.c f096b6771cc0aaa11790aca95773a50a8f74ba73
F src/hash.c 2b1b13f7400e179631c83a1be0c664608c8f021f F src/hash.c 2b1b13f7400e179631c83a1be0c664608c8f021f
F src/hash.h 1b0c445e1c89ff2aaad9b4605ba61375af001e84 F src/hash.h 1b0c445e1c89ff2aaad9b4605ba61375af001e84
@@ -53,14 +53,14 @@ F src/os_win.c bddeae1c3299be0fbe47077dd4e98b786a067f71
F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
F src/pager.c d21565d0e844712809140632062a7b72b768fdff F src/pager.c d21565d0e844712809140632062a7b72b768fdff
F src/pager.h 9eba8c53dd91eae7f3f90743b2ee242da02a9862 F src/pager.h 9eba8c53dd91eae7f3f90743b2ee242da02a9862
F src/parse.y 7a4965d65c6c8a1f5012bf365c54c8dea09a3921 F src/parse.y ee046c1ea30425a817285e52fb1993c2f955e766
F src/pragma.c 572d7dd4f9c9d020ca2967a1c3ec02c3126e9631 F src/pragma.c 572d7dd4f9c9d020ca2967a1c3ec02c3126e9631
F src/printf.c 3d20b21cfecadacecac3fb7274e746cb81d3d357 F src/printf.c 3d20b21cfecadacecac3fb7274e746cb81d3d357
F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3 F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3
F src/select.c 16c750c000f0d6aa543c778e2c752154b7272336 F src/select.c 6217e1f72cee7e173b21b252fd42a052f3b4decc
F src/shell.c 3cb0ef124ed9cd582ce89aec59ff7c659bc6e61b F src/shell.c 3cb0ef124ed9cd582ce89aec59ff7c659bc6e61b
F src/sqlite.h.in c85f6bad9ca7de29f505fe886646cfff7df4c55e F src/sqlite.h.in c85f6bad9ca7de29f505fe886646cfff7df4c55e
F src/sqliteInt.h f10da39b6407af63002401c7f9cee4a1313f4d2d F src/sqliteInt.h 58e9365c0f575ef42973439caf3bbab450ad700b
F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9 F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9
F src/tclsqlite.c 101994a2c4c0eaa69f1de9bfe4a02167f6049e7d F src/tclsqlite.c 101994a2c4c0eaa69f1de9bfe4a02167f6049e7d
F src/test1.c feac8a742aca920c8ab18a43b3208ae3a834fe9d F src/test1.c feac8a742aca920c8ab18a43b3208ae3a834fe9d
@@ -73,8 +73,8 @@ F src/trigger.c 038c8e128d4551cd016426cd11bbf5c478816481
F src/update.c b6f4668c11059f86b71581187d09197fa28ec4be F src/update.c b6f4668c11059f86b71581187d09197fa28ec4be
F src/utf.c bda5eb85039ef16f2d17004c1e18c96e1ab0a80c F src/utf.c bda5eb85039ef16f2d17004c1e18c96e1ab0a80c
F src/util.c 1b7b9a127b66743ab6cba8d44597aeb570723c99 F src/util.c 1b7b9a127b66743ab6cba8d44597aeb570723c99
F src/vacuum.c 14d1c346234fc64b326c19ea1ffe8f9e4c73d19a F src/vacuum.c 4dbe45a5c41674a04ac45a7586031583386ab119
F src/vdbe.c d2c29d2ada955818afa910c3204e5a5d145ceaea F src/vdbe.c 5acf43749f44b0813d47f4b1801538f4aaa7ddbb
F src/vdbe.h bb9186484f749a839c6c43953e79a6530253f7cd F src/vdbe.h bb9186484f749a839c6c43953e79a6530253f7cd
F src/vdbeInt.h e80721cd8ff611789e20743eec43363a9fb5a48e F src/vdbeInt.h e80721cd8ff611789e20743eec43363a9fb5a48e
F src/vdbeapi.c 467caa6e6fb9247528b1c7ab9132ae1b4748e8ac F src/vdbeapi.c 467caa6e6fb9247528b1c7ab9132ae1b4748e8ac
@@ -113,7 +113,7 @@ F test/collate4.test b8668612691c4dcf90f67a8df1eeb1544e7fdaf8
F test/collate5.test 581775b94604b7435dc6a5c6e72fbbf7d69e3830 F test/collate5.test 581775b94604b7435dc6a5c6e72fbbf7d69e3830
F test/collate6.test 6c9470d1606ee3e564675b229653e320c49ec638 F test/collate6.test 6c9470d1606ee3e564675b229653e320c49ec638
F test/conflict.test c5b849b01cfbe0a4f63a90cba6f68e2fe3a75f87 F test/conflict.test c5b849b01cfbe0a4f63a90cba6f68e2fe3a75f87
F test/corrupt.test 34e031add52cb1f50aff722f7d4ebd7b972637d3 F test/corrupt.test c34304baf2f027e05942af2efeb26844adca9a53
F test/corrupt2.test 88342570828f2b8cbbd8369eff3891f5c0bdd5ba F test/corrupt2.test 88342570828f2b8cbbd8369eff3891f5c0bdd5ba
F test/crash.test f38b980a0508655d08c957a6dd27d66bca776504 F test/crash.test f38b980a0508655d08c957a6dd27d66bca776504
F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2 F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2
@@ -143,7 +143,7 @@ F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0
F test/join4.test cc6cafe85e11aacacd0abcd247a46bed251308f8 F test/join4.test cc6cafe85e11aacacd0abcd247a46bed251308f8
F test/lastinsert.test b6a1db3e1ce2d3f0d6afe99d445084f543b6feaa F test/lastinsert.test b6a1db3e1ce2d3f0d6afe99d445084f543b6feaa
F test/laststmtchanges.test 07cbdabc52407c29e40abc25050f2434f044a6b1 F test/laststmtchanges.test 07cbdabc52407c29e40abc25050f2434f044a6b1
F test/limit.test f833e610ab26c618487c36f62baf6458f9284ce6 F test/limit.test 270b076f31c5c32f7187de5727e74da4de43e477
F test/lock.test a19aab9a963273fe61c1058e3d1b648d6a0a2425 F test/lock.test a19aab9a963273fe61c1058e3d1b648d6a0a2425
F test/lock2.test 59c3dd7d9b24d1bf7ec91b2d1541c37e97939d5f F test/lock2.test 59c3dd7d9b24d1bf7ec91b2d1541c37e97939d5f
F test/lock3.test 615111293cf32aa2ed16d01c6611737651c96fb9 F test/lock3.test 615111293cf32aa2ed16d01c6611737651c96fb9
@@ -270,7 +270,7 @@ F www/tclsqlite.tcl e73f8f8e5f20e8277619433f7970060ab01088fc
F www/vdbe.tcl 095f106d93875c94b47367384ebc870517431618 F www/vdbe.tcl 095f106d93875c94b47367384ebc870517431618
F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0 F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0
F www/whentouse.tcl 3e522a06ad41992023c80ca29a048ae2331ca5bd F www/whentouse.tcl 3e522a06ad41992023c80ca29a048ae2331ca5bd
P 6d91a1e91bf0e8b4a0f5f78d079031f3ee69603b P a9c33a804d572dd9df15be2029637b10e5a65cc4
R 752d291cdd1da93d3f1d71a58abca891 R 0ba525d8e5e37833bac3706da6208349
U danielk1977 U danielk1977
Z bc43e9a2df7fa6f5203156118f2ad652 Z 5a32f92efa7eeaf1966c77064b18ce7a

View File

@@ -1 +1 @@
a9c33a804d572dd9df15be2029637b10e5a65cc4 515e5033a5482f55e7edb66d69ff3da7e234ff2e

View File

@@ -12,7 +12,7 @@
** This file contains routines used for analyzing expressions and ** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite. ** for generating VDBE code that evaluates expressions in SQLite.
** **
** $Id: expr.c,v 1.191 2005/02/04 04:07:17 danielk1977 Exp $ ** $Id: expr.c,v 1.192 2005/02/05 12:48:48 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include <ctype.h> #include <ctype.h>
@@ -511,8 +511,8 @@ Select *sqlite3SelectDup(Select *p){
pNew->pOrderBy = sqlite3ExprListDup(p->pOrderBy); pNew->pOrderBy = sqlite3ExprListDup(p->pOrderBy);
pNew->op = p->op; pNew->op = p->op;
pNew->pPrior = sqlite3SelectDup(p->pPrior); pNew->pPrior = sqlite3SelectDup(p->pPrior);
pNew->nLimit = p->nLimit; pNew->pLimit = sqlite3ExprDup(p->pLimit);
pNew->nOffset = p->nOffset; pNew->pOffset = sqlite3ExprDup(p->pOffset);
pNew->iLimit = -1; pNew->iLimit = -1;
pNew->iOffset = -1; pNew->iOffset = -1;
pNew->ppOpenTemp = 0; pNew->ppOpenTemp = 0;

View File

@@ -14,7 +14,7 @@
** the parser. Lemon will also generate a header file containing ** the parser. Lemon will also generate a header file containing
** numeric codes for all of the tokens. ** numeric codes for all of the tokens.
** **
** @(#) $Id: parse.y,v 1.164 2005/02/04 04:07:17 danielk1977 Exp $ ** @(#) $Id: parse.y,v 1.165 2005/02/05 12:48:48 danielk1977 Exp $
*/ */
%token_prefix TK_ %token_prefix TK_
%token_type {Token} %token_type {Token}
@@ -39,8 +39,8 @@
** LIMIT clause of a SELECT statement. ** LIMIT clause of a SELECT statement.
*/ */
struct LimitVal { struct LimitVal {
int limit; /* The LIMIT value. -1 if there is no limit */ Expr *pLimit; /* The LIMIT expression. NULL if there is no limit */
int offset; /* The OFFSET. 0 if there is none */ Expr *pOffset; /* The OFFSET expression. NULL if there is none */
}; };
/* /*
@@ -343,7 +343,7 @@ multiselect_op(A) ::= EXCEPT(OP). {A = @OP;}
%endif // SQLITE_OMIT_COMPOUND_SELECT %endif // SQLITE_OMIT_COMPOUND_SELECT
oneselect(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y) oneselect(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y)
groupby_opt(P) having_opt(Q) orderby_opt(Z) limit_opt(L). { groupby_opt(P) having_opt(Q) orderby_opt(Z) limit_opt(L). {
A = sqlite3SelectNew(W,X,Y,P,Q,Z,D,L.limit,L.offset); A = sqlite3SelectNew(W,X,Y,P,Q,Z,D,L.pLimit,L.pOffset);
} }
// The "distinct" nonterminal is true (1) if the DISTINCT keyword is // The "distinct" nonterminal is true (1) if the DISTINCT keyword is
@@ -442,7 +442,7 @@ seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) on_opt(N) using_opt(U). {
%destructor seltablist_paren {sqlite3SelectDelete($$);} %destructor seltablist_paren {sqlite3SelectDelete($$);}
seltablist_paren(A) ::= select(S). {A = S;} seltablist_paren(A) ::= select(S). {A = S;}
seltablist_paren(A) ::= seltablist(F). { seltablist_paren(A) ::= seltablist(F). {
A = sqlite3SelectNew(0,F,0,0,0,0,0,-1,0); A = sqlite3SelectNew(0,F,0,0,0,0,0,0,0);
} }
%endif // SQLITE_OMIT_SUBQUERY %endif // SQLITE_OMIT_SUBQUERY
@@ -513,12 +513,16 @@ having_opt(A) ::= . {A = 0;}
having_opt(A) ::= HAVING expr(X). {A = X;} having_opt(A) ::= HAVING expr(X). {A = X;}
%type limit_opt {struct LimitVal} %type limit_opt {struct LimitVal}
limit_opt(A) ::= . {A.limit = -1; A.offset = 0;} %destructor limit_opt {
limit_opt(A) ::= LIMIT signed(X). {A.limit = X; A.offset = 0;} sqlite3ExprDelete($$.pLimit);
limit_opt(A) ::= LIMIT signed(X) OFFSET signed(Y). sqlite3ExprDelete($$.pOffset);
{A.limit = X; A.offset = Y;} }
limit_opt(A) ::= LIMIT signed(X) COMMA signed(Y). limit_opt(A) ::= . {A.pLimit = 0; A.pOffset = 0;}
{A.limit = Y; A.offset = X;} limit_opt(A) ::= LIMIT expr(X). {A.pLimit = X; A.pOffset = 0;}
limit_opt(A) ::= LIMIT expr(X) OFFSET expr(Y).
{A.pLimit = X; A.pOffset = Y;}
limit_opt(A) ::= LIMIT expr(X) COMMA expr(Y).
{A.pOffset = X; A.pLimit = Y;}
/////////////////////////// The DELETE statement ///////////////////////////// /////////////////////////// The DELETE statement /////////////////////////////
// //
@@ -726,7 +730,7 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
expr(A) ::= expr(X) in_op(N) nm(Y) dbnm(Z). [IN] { expr(A) ::= expr(X) in_op(N) nm(Y) dbnm(Z). [IN] {
SrcList *pSrc = sqlite3SrcListAppend(0,&Y,&Z); SrcList *pSrc = sqlite3SrcListAppend(0,&Y,&Z);
A = sqlite3Expr(TK_IN, X, 0, 0); A = sqlite3Expr(TK_IN, X, 0, 0);
if( A ) A->pSelect = sqlite3SelectNew(0,pSrc,0,0,0,0,0,-1,0); if( A ) A->pSelect = sqlite3SelectNew(0,pSrc,0,0,0,0,0,0,0);
if( N ) A = sqlite3Expr(TK_NOT, A, 0, 0); if( N ) A = sqlite3Expr(TK_NOT, A, 0, 0);
sqlite3ExprSpan(A,&X->span,Z.z?&Z:&Y); sqlite3ExprSpan(A,&X->span,Z.z?&Z:&Y);
} }

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite. ** to handle SELECT statements in SQLite.
** **
** $Id: select.c,v 1.238 2005/02/04 04:07:17 danielk1977 Exp $ ** $Id: select.c,v 1.239 2005/02/05 12:48:48 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@@ -29,11 +29,12 @@ Select *sqlite3SelectNew(
Expr *pHaving, /* the HAVING clause */ Expr *pHaving, /* the HAVING clause */
ExprList *pOrderBy, /* the ORDER BY clause */ ExprList *pOrderBy, /* the ORDER BY clause */
int isDistinct, /* true if the DISTINCT keyword is present */ int isDistinct, /* true if the DISTINCT keyword is present */
int nLimit, /* LIMIT value. -1 means not used */ Expr *pLimit, /* LIMIT value. NULL means not used */
int nOffset /* OFFSET value. 0 means no offset */ Expr *pOffset /* OFFSET value. NULL means no offset */
){ ){
Select *pNew; Select *pNew;
pNew = sqliteMalloc( sizeof(*pNew) ); pNew = sqliteMalloc( sizeof(*pNew) );
assert( !pOffset || pLimit ); /* Can't have OFFSET without LIMIT. */
if( pNew==0 ){ if( pNew==0 ){
sqlite3ExprListDelete(pEList); sqlite3ExprListDelete(pEList);
sqlite3SrcListDelete(pSrc); sqlite3SrcListDelete(pSrc);
@@ -41,6 +42,8 @@ Select *sqlite3SelectNew(
sqlite3ExprListDelete(pGroupBy); sqlite3ExprListDelete(pGroupBy);
sqlite3ExprDelete(pHaving); sqlite3ExprDelete(pHaving);
sqlite3ExprListDelete(pOrderBy); sqlite3ExprListDelete(pOrderBy);
sqlite3ExprDelete(pLimit);
sqlite3ExprDelete(pOffset);
}else{ }else{
if( pEList==0 ){ if( pEList==0 ){
pEList = sqlite3ExprListAppend(0, sqlite3Expr(TK_ALL,0,0,0), 0); pEList = sqlite3ExprListAppend(0, sqlite3Expr(TK_ALL,0,0,0), 0);
@@ -53,8 +56,8 @@ Select *sqlite3SelectNew(
pNew->pOrderBy = pOrderBy; pNew->pOrderBy = pOrderBy;
pNew->isDistinct = isDistinct; pNew->isDistinct = isDistinct;
pNew->op = TK_SELECT; pNew->op = TK_SELECT;
pNew->nLimit = nLimit; pNew->pLimit = pLimit;
pNew->nOffset = nOffset; pNew->pOffset = pOffset;
pNew->iLimit = -1; pNew->iLimit = -1;
pNew->iOffset = -1; pNew->iOffset = -1;
} }
@@ -308,21 +311,11 @@ void sqlite3SelectDelete(Select *p){
sqlite3ExprDelete(p->pHaving); sqlite3ExprDelete(p->pHaving);
sqlite3ExprListDelete(p->pOrderBy); sqlite3ExprListDelete(p->pOrderBy);
sqlite3SelectDelete(p->pPrior); sqlite3SelectDelete(p->pPrior);
sqlite3ExprDelete(p->pLimit);
sqlite3ExprDelete(p->pOffset);
sqliteFree(p); sqliteFree(p);
} }
/*
** Delete the aggregate information from the parse structure.
*/
#if 0
static void sqliteAggregateInfoReset(Parse *pParse){
sqliteFree(pParse->aAgg);
pParse->aAgg = 0;
pParse->nAgg = 0;
pParse->useAgg = 0;
}
#endif
/* /*
** Insert code into "v" that will push the record on the top of the ** Insert code into "v" that will push the record on the top of the
** stack into the sorter. ** stack into the sorter.
@@ -347,9 +340,10 @@ static void codeLimiter(
int nPop /* Number of times to pop stack when jumping */ int nPop /* Number of times to pop stack when jumping */
){ ){
if( p->iOffset>=0 ){ if( p->iOffset>=0 ){
int addr = sqlite3VdbeCurrentAddr(v) + 2; int addr = sqlite3VdbeCurrentAddr(v) + 3;
if( nPop>0 ) addr++; if( nPop>0 ) addr++;
sqlite3VdbeAddOp(v, OP_MemIncr, p->iOffset, addr); sqlite3VdbeAddOp(v, OP_MemIncr, p->iOffset, 0);
sqlite3VdbeAddOp(v, OP_IfMemPos, p->iOffset, addr);
if( nPop>0 ){ if( nPop>0 ){
sqlite3VdbeAddOp(v, OP_Pop, nPop, 0); sqlite3VdbeAddOp(v, OP_Pop, nPop, 0);
} }
@@ -1274,12 +1268,12 @@ Vdbe *sqlite3GetVdbe(Parse *pParse){
/* /*
** Compute the iLimit and iOffset fields of the SELECT based on the ** Compute the iLimit and iOffset fields of the SELECT based on the
** nLimit and nOffset fields. nLimit and nOffset hold the integers ** pLimit and pOffset expressions. nLimit and nOffset hold the expressions
** that appear in the original SQL statement after the LIMIT and OFFSET ** that appear in the original SQL statement after the LIMIT and OFFSET
** keywords. Or that hold -1 and 0 if those keywords are omitted. ** keywords. Or NULL if those keywords are omitted. iLimit and iOffset
** iLimit and iOffset are the integer memory register numbers for ** are the integer memory register numbers for counters used to compute
** counters used to compute the limit and offset. If there is no ** the limit and offset. If there is no limit and/or offset, then
** limit and/or offset, then iLimit and iOffset are negative. ** iLimit and iOffset are negative.
** **
** This routine changes the values if iLimit and iOffset only if ** This routine changes the values if iLimit and iOffset only if
** a limit or offset is defined by nLimit and nOffset. iLimit and ** a limit or offset is defined by nLimit and nOffset. iLimit and
@@ -1292,28 +1286,29 @@ Vdbe *sqlite3GetVdbe(Parse *pParse){
*/ */
static void computeLimitRegisters(Parse *pParse, Select *p){ static void computeLimitRegisters(Parse *pParse, Select *p){
/* /*
** If the comparison is p->nLimit>0 then "LIMIT 0" shows
** all rows. It is the same as no limit. If the comparision is
** p->nLimit>=0 then "LIMIT 0" show no rows at all.
** "LIMIT -1" always shows all rows. There is some ** "LIMIT -1" always shows all rows. There is some
** contraversy about what the correct behavior should be. ** contraversy about what the correct behavior should be.
** The current implementation interprets "LIMIT 0" to mean ** The current implementation interprets "LIMIT 0" to mean
** no rows. ** no rows.
*/ */
if( p->nLimit>=0 ){ if( p->pLimit ){
int iMem = pParse->nMem++; int iMem = pParse->nMem++;
Vdbe *v = sqlite3GetVdbe(pParse); Vdbe *v = sqlite3GetVdbe(pParse);
if( v==0 ) return; if( v==0 ) return;
sqlite3VdbeAddOp(v, OP_Integer, -p->nLimit, 0); sqlite3ExprCode(pParse, p->pLimit);
sqlite3VdbeAddOp(v, OP_MustBeInt, 0, 0);
sqlite3VdbeAddOp(v, OP_Negative, 0, 0);
sqlite3VdbeAddOp(v, OP_MemStore, iMem, 1); sqlite3VdbeAddOp(v, OP_MemStore, iMem, 1);
VdbeComment((v, "# LIMIT counter")); VdbeComment((v, "# LIMIT counter"));
p->iLimit = iMem; p->iLimit = iMem;
} }
if( p->nOffset>0 ){ if( p->pOffset ){
int iMem = pParse->nMem++; int iMem = pParse->nMem++;
Vdbe *v = sqlite3GetVdbe(pParse); Vdbe *v = sqlite3GetVdbe(pParse);
if( v==0 ) return; if( v==0 ) return;
sqlite3VdbeAddOp(v, OP_Integer, -p->nOffset, 0); sqlite3ExprCode(pParse, p->pOffset);
sqlite3VdbeAddOp(v, OP_MustBeInt, 0, 0);
sqlite3VdbeAddOp(v, OP_Negative, 0, 0);
sqlite3VdbeAddOp(v, OP_MemStore, iMem, 1); sqlite3VdbeAddOp(v, OP_MemStore, iMem, 1);
VdbeComment((v, "# OFFSET counter")); VdbeComment((v, "# OFFSET counter"));
p->iOffset = iMem; p->iOffset = iMem;
@@ -1463,7 +1458,7 @@ static int multiSelect(
rc = 1; rc = 1;
goto multi_select_end; goto multi_select_end;
} }
if( pPrior->nLimit>=0 || pPrior->nOffset>0 ){ if( pPrior->pLimit ){
sqlite3ErrorMsg(pParse,"LIMIT clause should come after %s not before", sqlite3ErrorMsg(pParse,"LIMIT clause should come after %s not before",
selectOpName(p->op)); selectOpName(p->op));
rc = 1; rc = 1;
@@ -1504,8 +1499,9 @@ static int multiSelect(
switch( p->op ){ switch( p->op ){
case TK_ALL: { case TK_ALL: {
if( p->pOrderBy==0 ){ if( p->pOrderBy==0 ){
pPrior->nLimit = p->nLimit; assert( !pPrior->pLimit );
pPrior->nOffset = p->nOffset; pPrior->pLimit = p->pLimit;
pPrior->pOffset = p->pOffset;
rc = sqlite3Select(pParse, pPrior, eDest, iParm, 0, 0, 0, aff); rc = sqlite3Select(pParse, pPrior, eDest, iParm, 0, 0, 0, aff);
if( rc ){ if( rc ){
goto multi_select_end; goto multi_select_end;
@@ -1513,8 +1509,8 @@ static int multiSelect(
p->pPrior = 0; p->pPrior = 0;
p->iLimit = pPrior->iLimit; p->iLimit = pPrior->iLimit;
p->iOffset = pPrior->iOffset; p->iOffset = pPrior->iOffset;
p->nLimit = -1; p->pLimit = 0;
p->nOffset = 0; p->pOffset = 0;
rc = sqlite3Select(pParse, p, eDest, iParm, 0, 0, 0, aff); rc = sqlite3Select(pParse, p, eDest, iParm, 0, 0, 0, aff);
p->pPrior = pPrior; p->pPrior = pPrior;
if( rc ){ if( rc ){
@@ -1529,12 +1525,12 @@ static int multiSelect(
int unionTab; /* Cursor number of the temporary table holding result */ int unionTab; /* Cursor number of the temporary table holding result */
int op = 0; /* One of the SRT_ operations to apply to self */ int op = 0; /* One of the SRT_ operations to apply to self */
int priorOp; /* The SRT_ operation to apply to prior selects */ int priorOp; /* The SRT_ operation to apply to prior selects */
int nLimit, nOffset; /* Saved values of p->nLimit and p->nOffset */ Expr *pLimit, *pOffset; /* Saved values of p->nLimit and p->nOffset */
ExprList *pOrderBy; /* The ORDER BY clause for the right SELECT */ ExprList *pOrderBy; /* The ORDER BY clause for the right SELECT */
int addr; int addr;
priorOp = p->op==TK_ALL ? SRT_Table : SRT_Union; priorOp = p->op==TK_ALL ? SRT_Table : SRT_Union;
if( eDest==priorOp && p->pOrderBy==0 && p->nLimit<0 && p->nOffset==0 ){ if( eDest==priorOp && p->pOrderBy==0 && !p->pLimit && !p->pOffset ){
/* We can reuse a temporary table generated by a SELECT to our /* We can reuse a temporary table generated by a SELECT to our
** right. ** right.
*/ */
@@ -1580,15 +1576,16 @@ static int multiSelect(
p->pPrior = 0; p->pPrior = 0;
pOrderBy = p->pOrderBy; pOrderBy = p->pOrderBy;
p->pOrderBy = 0; p->pOrderBy = 0;
nLimit = p->nLimit; pLimit = p->pLimit;
p->nLimit = -1; p->pLimit = 0;
nOffset = p->nOffset; pOffset = p->pOffset;
p->nOffset = 0; p->pOffset = 0;
rc = sqlite3Select(pParse, p, op, unionTab, 0, 0, 0, aff); rc = sqlite3Select(pParse, p, op, unionTab, 0, 0, 0, aff);
p->pPrior = pPrior; p->pPrior = pPrior;
p->pOrderBy = pOrderBy; p->pOrderBy = pOrderBy;
p->nLimit = nLimit; sqlite3ExprDelete(p->pLimit);
p->nOffset = nOffset; p->pLimit = pLimit;
p->pOffset = pOffset;
p->iLimit = -1; p->iLimit = -1;
p->iOffset = -1; p->iOffset = -1;
if( rc ){ if( rc ){
@@ -1627,7 +1624,7 @@ static int multiSelect(
case TK_INTERSECT: { case TK_INTERSECT: {
int tab1, tab2; int tab1, tab2;
int iCont, iBreak, iStart; int iCont, iBreak, iStart;
int nLimit, nOffset; Expr *pLimit, *pOffset;
int addr; int addr;
/* INTERSECT is different from the others since it requires /* INTERSECT is different from the others since it requires
@@ -1669,14 +1666,15 @@ static int multiSelect(
assert( nAddr<sizeof(aAddr)/sizeof(aAddr[0]) ); assert( nAddr<sizeof(aAddr)/sizeof(aAddr[0]) );
aAddr[nAddr++] = sqlite3VdbeAddOp(v, OP_SetNumColumns, tab2, 0); aAddr[nAddr++] = sqlite3VdbeAddOp(v, OP_SetNumColumns, tab2, 0);
p->pPrior = 0; p->pPrior = 0;
nLimit = p->nLimit; pLimit = p->pLimit;
p->nLimit = -1; p->pLimit = 0;
nOffset = p->nOffset; pOffset = p->pOffset;
p->nOffset = 0; p->pOffset = 0;
rc = sqlite3Select(pParse, p, SRT_Union, tab2, 0, 0, 0, aff); rc = sqlite3Select(pParse, p, SRT_Union, tab2, 0, 0, 0, aff);
p->pPrior = pPrior; p->pPrior = pPrior;
p->nLimit = nLimit; sqlite3ExprDelete(p->pLimit);
p->nOffset = nOffset; p->pLimit = pLimit;
p->pOffset = pOffset;
if( rc ){ if( rc ){
goto multi_select_end; goto multi_select_end;
} }
@@ -1959,11 +1957,13 @@ static int flattenSubquery(
if( subqueryIsAgg && pSrc->nSrc>1 ) return 0; if( subqueryIsAgg && pSrc->nSrc>1 ) return 0;
pSubSrc = pSub->pSrc; pSubSrc = pSub->pSrc;
assert( pSubSrc ); assert( pSubSrc );
if( (pSub->pLimit && p->pLimit) || pSub->pOffset ||
(pSub->pLimit && isAgg) ) return 0;
if( pSubSrc->nSrc==0 ) return 0; if( pSubSrc->nSrc==0 ) return 0;
if( (pSub->isDistinct || pSub->nLimit>=0) && (pSrc->nSrc>1 || isAgg) ){ if( pSub->isDistinct && (pSrc->nSrc>1 || isAgg) ){
return 0; return 0;
} }
if( (p->isDistinct || p->nLimit>=0) && subqueryIsAgg ) return 0; if( p->isDistinct && subqueryIsAgg ) return 0;
if( p->pOrderBy && pSub->pOrderBy ) return 0; if( p->pOrderBy && pSub->pOrderBy ) return 0;
/* Restriction 3: If the subquery is a join, make sure the subquery is /* Restriction 3: If the subquery is a join, make sure the subquery is
@@ -2095,17 +2095,10 @@ static int flattenSubquery(
*/ */
p->isDistinct = p->isDistinct || pSub->isDistinct; p->isDistinct = p->isDistinct || pSub->isDistinct;
/* Transfer the limit expression from the subquery to the outer if( pSub->pLimit ){
** query. p->pLimit = pSub->pLimit;
*/ pSub->pLimit = 0;
if( pSub->nLimit>=0 ){
if( p->nLimit<0 ){
p->nLimit = pSub->nLimit;
}else if( p->nLimit+p->nOffset > pSub->nLimit+pSub->nOffset ){
p->nLimit = pSub->nLimit + pSub->nOffset - p->nOffset;
}
} }
p->nOffset += pSub->nOffset;
/* Finially, delete what is left of the subquery and return /* Finially, delete what is left of the subquery and return
** success. ** success.
@@ -2330,15 +2323,28 @@ int sqlite3SelectResolve(
return SQLITE_ERROR; return SQLITE_ERROR;
} }
/* Set up the local name-context to pass to ExprResolveNames(). */ /* Resolve the expressions in the LIMIT and OFFSET clauses. These
sNC.pNext = pOuterNC; ** are not allowed to refer to any names, so pass an empty NameContext.
*/
sNC.pParse = pParse; sNC.pParse = pParse;
sNC.pSrcList = p->pSrc;
sNC.allowAgg = 1;
sNC.hasAgg = 0; sNC.hasAgg = 0;
sNC.nErr = 0; sNC.nErr = 0;
sNC.nRef = 0; sNC.nRef = 0;
sNC.pEList = 0; sNC.pEList = 0;
sNC.allowAgg = 0;
sNC.pSrcList = 0;
sNC.pNext = 0;
if( sqlite3ExprResolveNames(&sNC, p->pLimit) ||
sqlite3ExprResolveNames(&sNC, p->pOffset) ){
return SQLITE_ERROR;
}
/* Set up the local name-context to pass to ExprResolveNames() to
** resolve the expression-list.
*/
sNC.allowAgg = 1;
sNC.pSrcList = p->pSrc;
sNC.pNext = pOuterNC;
/* NameContext.nDepth stores the depth of recursion for this query. For /* NameContext.nDepth stores the depth of recursion for this query. For
** an outer query (e.g. SELECT * FROM sqlite_master) this is 1. For ** an outer query (e.g. SELECT * FROM sqlite_master) this is 1. For

View File

@@ -11,7 +11,7 @@
************************************************************************* *************************************************************************
** Internal interface definitions for SQLite. ** Internal interface definitions for SQLite.
** **
** @(#) $Id: sqliteInt.h,v 1.366 2005/02/04 04:07:17 danielk1977 Exp $ ** @(#) $Id: sqliteInt.h,v 1.367 2005/02/05 12:48:48 danielk1977 Exp $
*/ */
#ifndef _SQLITEINT_H_ #ifndef _SQLITEINT_H_
#define _SQLITEINT_H_ #define _SQLITEINT_H_
@@ -1013,7 +1013,8 @@ struct Select {
Expr *pHaving; /* The HAVING clause */ Expr *pHaving; /* The HAVING clause */
ExprList *pOrderBy; /* The ORDER BY clause */ ExprList *pOrderBy; /* The ORDER BY clause */
Select *pPrior; /* Prior select in a compound select statement */ Select *pPrior; /* Prior select in a compound select statement */
int nLimit, nOffset; /* LIMIT and OFFSET values. -1 means not used */ Expr *pLimit; /* LIMIT expression. NULL means not used. */
Expr *pOffset; /* OFFSET expression. NULL means not used. */
int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */ int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */
IdList **ppOpenTemp; /* OP_OpenTemp addresses used by multi-selects */ IdList **ppOpenTemp; /* OP_OpenTemp addresses used by multi-selects */
Fetch *pFetch; /* If this stmt is part of a FETCH command */ Fetch *pFetch; /* If this stmt is part of a FETCH command */
@@ -1378,7 +1379,7 @@ void sqlite3AddKeyType(Vdbe*, ExprList*);
void sqlite3AddIdxKeyType(Vdbe*, Index*); void sqlite3AddIdxKeyType(Vdbe*, Index*);
int sqlite3Select(Parse*, Select*, int, int, Select*, int, int*, char *aff); int sqlite3Select(Parse*, Select*, int, int, Select*, int, int*, char *aff);
Select *sqlite3SelectNew(ExprList*,SrcList*,Expr*,ExprList*,Expr*,ExprList*, Select *sqlite3SelectNew(ExprList*,SrcList*,Expr*,ExprList*,Expr*,ExprList*,
int,int,int); int,Expr*,Expr*);
void sqlite3SelectDelete(Select*); void sqlite3SelectDelete(Select*);
void sqlite3SelectUnbind(Select*); void sqlite3SelectUnbind(Select*);
Table *sqlite3SrcListLookup(Parse*, SrcList*); Table *sqlite3SrcListLookup(Parse*, SrcList*);

View File

@@ -14,7 +14,7 @@
** Most of the code in this file may be omitted by defining the ** Most of the code in this file may be omitted by defining the
** SQLITE_OMIT_VACUUM macro. ** SQLITE_OMIT_VACUUM macro.
** **
** $Id: vacuum.c,v 1.37 2005/02/03 01:08:20 drh Exp $ ** $Id: vacuum.c,v 1.38 2005/02/05 12:48:48 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include "os.h" #include "os.h"
@@ -212,7 +212,9 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
rc = execExecSql(db, rc = execExecSql(db,
"SELECT 'DELETE FROM vacuum_db.' || quote(name) || ';' " "SELECT 'DELETE FROM vacuum_db.' || quote(name) || ';' "
"FROM sqlite_master WHERE name='sqlite_sequence' " "FROM sqlite_master WHERE name='sqlite_sequence' "
"UNION ALL " );
if( rc!=SQLITE_OK ) goto end_of_vacuum;
rc = execExecSql(db,
"SELECT 'INSERT INTO vacuum_db.' || quote(name) " "SELECT 'INSERT INTO vacuum_db.' || quote(name) "
"|| ' SELECT * FROM ' || quote(name) || ';' " "|| ' SELECT * FROM ' || quote(name) || ';' "
"FROM sqlite_master WHERE name=='sqlite_sequence';" "FROM sqlite_master WHERE name=='sqlite_sequence';"

View File

@@ -43,7 +43,7 @@
** in this file for details. If in doubt, do not deviate from existing ** in this file for details. If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code. ** commenting and indentation practices when changing or adding code.
** **
** $Id: vdbe.c,v 1.452 2005/02/05 06:49:54 danielk1977 Exp $ ** $Id: vdbe.c,v 1.453 2005/02/05 12:48:48 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include "os.h" #include "os.h"
@@ -4203,7 +4203,7 @@ case OP_MemMax: {
/* Opcode: MemIncr P1 P2 * /* Opcode: MemIncr P1 P2 *
** **
** Increment the integer valued memory cell P1 by 1. If P2 is not zero ** Increment the integer valued memory cell P1 by 1. If P2 is not zero
** and the result after the increment is greater than zero, then jump ** and the result after the increment is exactly 1, then jump
** to P2. ** to P2.
** **
** This instruction throws an error if the memory cell is not initially ** This instruction throws an error if the memory cell is not initially
@@ -4216,7 +4216,24 @@ case OP_MemIncr: {
pMem = &p->aMem[i]; pMem = &p->aMem[i];
assert( pMem->flags==MEM_Int ); assert( pMem->flags==MEM_Int );
pMem->i++; pMem->i++;
if( pOp->p2>0 && pMem->i>0 ){ if( pOp->p2>0 && pMem->i==1 ){
pc = pOp->p2 - 1;
}
break;
}
/* Opcode: IfMemPos P1 P2 *
**
** If the value of memory cell P1 is 1 or greater, jump to P2. This
** opcode assumes that memory cell P1 holds an integer value.
*/
case OP_IfMemPos: {
int i = pOp->p1;
Mem *pMem;
assert( i>=0 && i<p->nMem );
pMem = &p->aMem[i];
assert( pMem->flags==MEM_Int );
if( pMem->i>0 ){
pc = pOp->p2 - 1; pc = pOp->p2 - 1;
} }
break; break;

View File

@@ -13,7 +13,7 @@
# This file implements tests to make sure SQLite does not crash or # This file implements tests to make sure SQLite does not crash or
# segfault if it sees a corrupt database file. # segfault if it sees a corrupt database file.
# #
# $Id: corrupt.test,v 1.6 2005/02/05 06:49:55 danielk1977 Exp $ # $Id: corrupt.test,v 1.7 2005/02/05 12:48:49 danielk1977 Exp $
catch {file delete -force test.db} catch {file delete -force test.db}
catch {file delete -force test.db-journal} catch {file delete -force test.db-journal}
@@ -121,10 +121,8 @@ do_test corrupt-3.1 {
db close db close
copy_file test.bu test.db copy_file test.bu test.db
sqlite3 db test.db sqlite3 db test.db
execsql { list
SELECT name, rootpage FROM sqlite_master } {}
}
} {t1 2 t1i1 85 t2 177}
do_test corrupt-3.2 { do_test corrupt-3.2 {
set t1_r [execsql {SELECT rootpage FROM sqlite_master WHERE name = 't1i1'}] set t1_r [execsql {SELECT rootpage FROM sqlite_master WHERE name = 't1i1'}]
set t1i1_r [execsql {SELECT rootpage FROM sqlite_master WHERE name = 't1'}] set t1i1_r [execsql {SELECT rootpage FROM sqlite_master WHERE name = 't1'}]
@@ -135,9 +133,8 @@ do_test corrupt-3.2 {
UPDATE sqlite_master SET rootpage = $t1i1_r WHERE name = 't1i1'; UPDATE sqlite_master SET rootpage = $t1i1_r WHERE name = 't1i1';
PRAGMA writable_schema = 0; PRAGMA writable_schema = 0;
PRAGMA schema_version = $cookie; PRAGMA schema_version = $cookie;
SELECT name, rootpage FROM sqlite_master;
" "
} {t1 85 t1i1 2 t2 177} } {}
# This one tests the case caught by code in checkin [2313]. # This one tests the case caught by code in checkin [2313].
do_test corrupt-3.3 { do_test corrupt-3.3 {

View File

@@ -12,7 +12,7 @@
# focus of this file is testing the LIMIT ... OFFSET ... clause # focus of this file is testing the LIMIT ... OFFSET ... clause
# of SELECT statements. # of SELECT statements.
# #
# $Id: limit.test,v 1.23 2005/01/21 04:25:47 danielk1977 Exp $ # $Id: limit.test,v 1.24 2005/02/05 12:48:49 danielk1977 Exp $
set testdir [file dirname $argv0] set testdir [file dirname $argv0]
source $testdir/tester.tcl source $testdir/tester.tcl
@@ -388,4 +388,41 @@ ifcapable compound {
} {1 {LIMIT clause should come after UNION not before}} } {1 {LIMIT clause should come after UNION not before}}
} }
# Test LIMIT and OFFSET using SQL variables.
do_test limit-10.1 {
set limit 10
db eval {
SELECT x FROM t1 LIMIT $limit;
}
} {31 30 29 28 27 26 25 24 23 22}
do_test limit-10.2 {
set limit 5
set offset 5
db eval {
SELECT x FROM t1 LIMIT $limit OFFSET $offset;
}
} {26 25 24 23 22}
do_test limit-10.3 {
set limit -1
db eval {
SELECT x FROM t1 WHERE x<10 LIMIT $limit;
}
} {9 8 7 6 5 4 3 2 1 0}
do_test limit-10.4 {
set limit 1.5
set rc [catch {
db eval {
SELECT x FROM t1 WHERE x<10 LIMIT $limit;
} } msg]
list $rc $msg
} {1 {datatype mismatch}}
do_test limit-10.5 {
set limit "hello world"
set rc [catch {
db eval {
SELECT x FROM t1 WHERE x<10 LIMIT $limit;
} } msg]
list $rc $msg
} {1 {datatype mismatch}}
finish_test finish_test