diff --git a/manifest b/manifest index 27c2e0c6c5..a5f66a31ae 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improvements\sto\scomments.\s\sNo\scode\schanges.\s(CVS\s618) -D 2002-06-14T20:58:45 +C Make\sthe\sLIMIT\sclause\swork\seven\sif\sthe\sdestination\sof\sthe\sSELECT\sis\nsomething\sother\sthan\sa\scallback.\s\s(Ticket\s#66)\s(CVS\s619) +D 2002-06-14T22:38:42 F Makefile.in 6291a33b87d2a395aafd7646ee1ed562c6f2c28c F Makefile.template 4e11752e0b5c7a043ca50af4296ec562857ba495 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0 @@ -37,7 +37,7 @@ F src/pager.h 6fddfddd3b73aa8abc081b973886320e3c614f0e F src/parse.y 42920305d49666419358b469e4ec522ac867a39f F src/printf.c d8032ee18b860c812eeff596c9bebfdacb7930fd F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe -F src/select.c 1d5cb1ae0bb3376bedfde7ae22e6e927e4d0b5e2 +F src/select.c 6c3a92d7a0bdf3448265d530cc0e6f6e5a764997 F src/shell.c 1d22fe870ee852cfb975fd000dbe3973713d0a15 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e F src/sqlite.h.in 0038faa6d642de06b91143ee65a131bd831d020b @@ -52,9 +52,9 @@ F src/tokenize.c 890ca022d45f1798dadc300a798951597428853e F src/trigger.c 21342af6ac031fece39c8fc6eabd1739ca5327c1 F src/update.c 05431e23a9c83502fd7911e771c8366fc2b90b4c F src/util.c 876b259f9186e84b944b72e793dd3dad50e63e95 -F src/vdbe.c 836f2c4f823c94c3c3454125d9ba9283e8b22dda -F src/vdbe.h 1742d6f8b40f40879475b4c41cf4f9980ceb0e21 -F src/where.c bd25559800ba1fbe1fd596eaec14c417aa1e1b46 +F src/vdbe.c 7d9bb3701ea00576c5d5fb3f3de63af7b7304241 +F src/vdbe.h fba15f3280688f6f32f11d1042078e3c557bac43 +F src/where.c aa5d9d83abf0f7dda06fdd4235020f2b72d58dec F test/all.test e4d3821eeba751829b419cd47814bd20af4286d1 F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578 F test/btree.test bf326f546a666617367a7033fa2c07451bd4f8e1 @@ -72,7 +72,7 @@ F test/insert2.test eb8481878a7f52ccb4e3346f87550f5afdd77f76 F test/intpkey.test 31b5f28b2c44273e6695cf36ab2e4133aee7753c F test/ioerr.test 57d9bffaca18b34f9e976f786eadc2591d6efc6a F test/join.test ea6a4097e4fcebbb16eac7ec819569e759336a74 -F test/limit.test 6f98bcefc92209103bb3764c81975a6ec21d6702 +F test/limit.test 96b33e108e134b5cc674a660064229d15fa278b9 F test/lock.test 3fcfd46a73119f6a18094673328a32c7b3047a8f F test/main.test c66b564554b770ee7fdbf6a66c0cd90329bc2c85 F test/malloc.test 7ba32a9ebd3aeed52ae4aaa6d42ca37e444536fd @@ -108,7 +108,7 @@ F test/unique.test 572aa791327c1e8d797932263e9d67f176cfdb44 F test/update.test a0aa0bf83e6fad8407d0e4ad25ebb09b513f5bf4 F test/vacuum.test 059871b312eb910bbe49dafde1d01490cc2c6bbe F test/view.test b9851e9142de5e5831fdf18f125cbe1256cb550a -F test/where.test fe33c2f92de1da8192abf763a2852a4ba270fefe +F test/where.test 367d7911ee0522f1bb8a2daf17fb985e06cfe8bb F tool/lemon.c 459cb2bb3738a1ad5cb0ad8b805587a88a885d95 F tool/lempar.c 73a991cc3017fb34804250fa901488b5147b3717 F tool/memleak.awk 296dfbce7a9ca499b95ce04e30334e64a50052e0 @@ -137,7 +137,7 @@ F www/speed.tcl da8afcc1d3ccc5696cfb388a68982bc3d9f7f00f F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218 -P e67ac0fec51788aca0429d04310f86c9c1cf9d13 -R c5c72185b42e3894c8a67dc4dc44e497 +P 7e10e60bb78b680bb822908a0b7763f8f5e3ee5a +R 93cd20298bab2e582d79237e05fbb947 U drh -Z ec1b6ad2227bde141b128cfba4f6e91b +Z 34cf64c3405f1f55004d8bc22fcb2d56 diff --git a/manifest.uuid b/manifest.uuid index f1e8e0b894..cf3f3eaa03 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7e10e60bb78b680bb822908a0b7763f8f5e3ee5a \ No newline at end of file +699cf362083043615eb88635a228bfa46a315c9c \ No newline at end of file diff --git a/src/select.c b/src/select.c index f0b154166d..b86c6c62c1 100644 --- a/src/select.c +++ b/src/select.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle SELECT statements in SQLite. ** -** $Id: select.c,v 1.92 2002/06/06 23:42:28 drh Exp $ +** $Id: select.c,v 1.93 2002/06/14 22:38:42 drh Exp $ */ #include "sqliteInt.h" @@ -294,6 +294,7 @@ static void sqliteAggregateInfoReset(Parse *pParse){ */ static int selectInnerLoop( Parse *pParse, /* The parser context */ + Select *p, /* The complete select statement being coded */ ExprList *pEList, /* List of values being extracted */ int srcTab, /* Pull data from this table */ int nColumn, /* Number of columns in the source table */ @@ -308,6 +309,18 @@ static int selectInnerLoop( int i; if( v==0 ) return 0; + /* If there was a LIMIT clause on the SELECT statement, then do the check + ** to see if this row should be output. + */ + if( pOrderBy==0 ){ + if( p->nOffset>0 ){ + sqliteVdbeAddOp(v, OP_LimitCk, 1, iContinue); + } + if( p->nLimit>0 ){ + sqliteVdbeAddOp(v, OP_LimitCk, 0, iBreak); + } + } + /* Pull the requested columns. */ if( pEList ){ @@ -419,7 +432,8 @@ static int selectInnerLoop( /* If none of the above, send the data to the callback function. */ { - sqliteVdbeAddOp(v, OP_Callback, nColumn, iBreak); + assert( eDest==SRT_Callback ); + sqliteVdbeAddOp(v, OP_Callback, nColumn, 0); } return 0; } @@ -430,12 +444,18 @@ static int selectInnerLoop( ** we need to run the sorter and output the results. The following ** routine generates the code needed to do that. */ -static void generateSortTail(Vdbe *v, int nColumn){ +static void generateSortTail(Select *p, Vdbe *v, int nColumn){ int end = sqliteVdbeMakeLabel(v); int addr; sqliteVdbeAddOp(v, OP_Sort, 0, 0); addr = sqliteVdbeAddOp(v, OP_SortNext, 0, end); - sqliteVdbeAddOp(v, OP_SortCallback, nColumn, end); + if( p->nOffset>0 ){ + sqliteVdbeAddOp(v, OP_LimitCk, 1, addr); + } + if( p->nLimit>0 ){ + sqliteVdbeAddOp(v, OP_LimitCk, 0, end); + } + sqliteVdbeAddOp(v, OP_SortCallback, nColumn, 0); sqliteVdbeAddOp(v, OP_Goto, 0, addr); sqliteVdbeResolveLabel(v, end); sqliteVdbeAddOp(v, OP_SortReset, 0, 0); @@ -975,7 +995,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){ iCont = sqliteVdbeMakeLabel(v); sqliteVdbeAddOp(v, OP_Rewind, unionTab, iBreak); iStart = sqliteVdbeCurrentAddr(v); - rc = selectInnerLoop(pParse, 0, unionTab, p->pEList->nExpr, + rc = selectInnerLoop(pParse, p, 0, unionTab, p->pEList->nExpr, p->pOrderBy, -1, eDest, iParm, iCont, iBreak); if( rc ) return 1; @@ -984,7 +1004,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){ sqliteVdbeResolveLabel(v, iBreak); sqliteVdbeAddOp(v, OP_Close, unionTab, 0); if( p->pOrderBy ){ - generateSortTail(v, p->pEList->nExpr); + generateSortTail(p, v, p->pEList->nExpr); } } break; @@ -1031,7 +1051,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){ sqliteVdbeAddOp(v, OP_Rewind, tab1, iBreak); iStart = sqliteVdbeAddOp(v, OP_FullKey, tab1, 0); sqliteVdbeAddOp(v, OP_NotFound, tab2, iCont); - rc = selectInnerLoop(pParse, 0, tab1, p->pEList->nExpr, + rc = selectInnerLoop(pParse, p, 0, tab1, p->pEList->nExpr, p->pOrderBy, -1, eDest, iParm, iCont, iBreak); if( rc ) return 1; @@ -1041,7 +1061,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){ sqliteVdbeAddOp(v, OP_Close, tab2, 0); sqliteVdbeAddOp(v, OP_Close, tab1, 0); if( p->pOrderBy ){ - generateSortTail(v, p->pEList->nExpr); + generateSortTail(p, v, p->pEList->nExpr); } break; } @@ -1176,6 +1196,14 @@ substExprList(ExprList *pList, int iTable, ExprList *pEList, int iSub){ ** ** (7) The subquery has a FROM clause. ** +** (8) The subquery does not use LIMIT or the outer query is not a join. +** +** (9) The subquery does not use LIMIT or the outer query does not use +** aggregates. +** +** (10) The subquery does not use aggregates or the outer query does not +** use LIMIT. +** ** In this routine, the "p" parameter is a pointer to the outer query. ** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query ** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates. @@ -1207,9 +1235,10 @@ int flattenSubquery(Select *p, int iFrom, int isAgg, int subqueryIsAgg){ pSubSrc = pSub->pSrc; assert( pSubSrc ); if( pSubSrc->nSrc!=1 ) return 0; - if( pSub->isDistinct && pSrc->nSrc>1 ) return 0; - if( pSub->isDistinct && isAgg ) return 0; - if( p->isDistinct && subqueryIsAgg ) return 0; + if( (pSub->isDistinct || pSub->nLimit>=0) && (pSrc->nSrc>1 || isAgg) ){ + return 0; + } + if( (p->isDistinct || p->nLimit) && subqueryIsAgg ) return 0; /* If we reach this point, it means flattening is permitted for the ** i-th entry of the FROM clause in the outer query. @@ -1267,6 +1296,14 @@ int flattenSubquery(Select *p, int iFrom, int isAgg, int subqueryIsAgg){ } } p->isDistinct = p->isDistinct || pSub->isDistinct; + 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; if( pSrc->a[iFrom].pTab && pSrc->a[iFrom].pTab->isTransient ){ sqliteDeleteTable(0, pSrc->a[iFrom].pTab); } @@ -1386,7 +1423,7 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){ eList.a = &eListItem; eList.a[0].pExpr = pExpr; cont = sqliteVdbeMakeLabel(v); - selectInnerLoop(pParse, &eList, base, 1, 0, -1, eDest, iParm, cont, cont); + selectInnerLoop(pParse, p, &eList, base, 1, 0, -1, eDest, iParm, cont, cont); sqliteVdbeResolveLabel(v, cont); sqliteVdbeAddOp(v, OP_Close, base, 0); return 1; @@ -1720,8 +1757,8 @@ int sqliteSelect( ** aggregates */ if( !isAgg ){ - if( selectInnerLoop(pParse, pEList, 0, 0, pOrderBy, distinct, eDest, iParm, - pWInfo->iContinue, pWInfo->iBreak) ){ + if( selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, distinct, eDest, + iParm, pWInfo->iContinue, pWInfo->iBreak) ){ goto select_end; } } @@ -1779,8 +1816,8 @@ int sqliteSelect( if( pHaving ){ sqliteExprIfFalse(pParse, pHaving, startagg, 1); } - if( selectInnerLoop(pParse, pEList, 0, 0, pOrderBy, distinct, eDest, iParm, - startagg, endagg) ){ + if( selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, distinct, eDest, + iParm, startagg, endagg) ){ goto select_end; } sqliteVdbeAddOp(v, OP_Goto, 0, startagg); @@ -1793,7 +1830,7 @@ int sqliteSelect( ** and send them to the callback one by one. */ if( pOrderBy ){ - generateSortTail(v, pEList->nExpr); + generateSortTail(p, v, pEList->nExpr); } diff --git a/src/vdbe.c b/src/vdbe.c index a838c8d061..8589fd39d5 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -30,7 +30,7 @@ ** But other routines are also provided to help in building up ** a program instruction by instruction. ** -** $Id: vdbe.c,v 1.155 2002/06/11 02:25:42 danielk1977 Exp $ +** $Id: vdbe.c,v 1.156 2002/06/14 22:38:43 drh Exp $ */ #include "sqliteInt.h" #include @@ -1080,6 +1080,7 @@ static char *zOpName[] = { 0, "Gt", "Ge", "IsNull", "NotNull", "Negative", "And", "Or", "Not", "Concat", "Noop", "Function", "Limit", + "LimitCk", }; /* @@ -1585,18 +1586,11 @@ case OP_ColumnName: { break; } -/* Opcode: Callback P1 P2 * +/* Opcode: Callback P1 * * ** ** Pop P1 values off the stack and form them into an array. Then ** invoke the callback function using the newly formed array as the ** 3rd parameter. -** -** If the offset counter (set by the OP_Limit opcode) is positive, -** then decrement the counter and do not invoke the callback. -** -** If the callback is invoked, then after the callback returns -** decrement the limit counter. When the limit counter reaches -** zero, jump to address P2. */ case OP_Callback: { int i = p->tos - pOp->p1 + 1; @@ -1612,22 +1606,12 @@ case OP_Callback: { } zStack[p->tos+1] = 0; if( xCallback!=0 ){ - if( p->iOffset>0 ){ - p->iOffset--; - }else{ - if( sqliteSafetyOff(db) ) goto abort_due_to_misuse; - if( xCallback(pArg, pOp->p1, &zStack[i], p->azColName)!=0 ){ - rc = SQLITE_ABORT; - } - if( sqliteSafetyOn(db) ) goto abort_due_to_misuse; - p->nCallback++; - if( p->iLimit>0 ){ - p->iLimit--; - if( p->iLimit==0 ){ - pc = pOp->p2 - 1; - } - } + if( sqliteSafetyOff(db) ) goto abort_due_to_misuse; + if( xCallback(pArg, pOp->p1, &zStack[i], p->azColName)!=0 ){ + rc = SQLITE_ABORT; } + if( sqliteSafetyOn(db) ) goto abort_due_to_misuse; + p->nCallback++; } PopStack(p, pOp->p1); if( sqlite_malloc_failed ) goto no_mem; @@ -3917,6 +3901,35 @@ case OP_Limit: { break; } +/* Opcode: LimitCk P1 P2 * +** +** If P1 is 1, then check to see if the offset counter (set by the +** P2 argument of OP_Limit) is positive. If the offset counter is +** positive then decrement the counter and jump immediately to P2. +** Otherwise fall straight through. +** +** If P1 is 0, then check the value of the limit counter (set by the +** P1 argument of OP_Limit). If the limit counter is negative or +** zero then jump immedately to P2. Otherwise decrement the limit +** counter and fall through. +*/ +case OP_LimitCk: { + if( pOp->p1 ){ + if( p->iOffset ){ + p->iOffset--; + pc = pOp->p2 - 1; + } + }else{ + if( p->iLimit>0 ){ + p->iLimit--; + }else{ + pc = pOp->p2 - 1; + } + } + break; +} + + /* Opcode: ListWrite * * * ** ** Write the integer on the top of the stack @@ -4207,40 +4220,22 @@ case OP_SortNext: { break; } -/* Opcode: SortCallback P1 P2 * +/* Opcode: SortCallback P1 * * ** ** The top of the stack contains a callback record built using ** the SortMakeRec operation with the same P1 value as this ** instruction. Pop this record from the stack and invoke the ** callback on it. -** -** If the offset counter (set by the OP_Limit opcode) is positive, -** then decrement the counter and do not invoke the callback. -** -** If the callback is invoked, then after the callback returns -** decrement the limit counter. When the limit counter reaches -** zero, jump to address P2. */ case OP_SortCallback: { int i = p->tos; VERIFY( if( i<0 ) goto not_enough_stack; ) if( xCallback!=0 ){ - if( p->iOffset>0 ){ - p->iOffset--; - }else{ - if( sqliteSafetyOff(db) ) goto abort_due_to_misuse; - if( xCallback(pArg, pOp->p1, (char**)zStack[i], p->azColName)!=0 ){ - rc = SQLITE_ABORT; - } - if( sqliteSafetyOn(db) ) goto abort_due_to_misuse; - p->nCallback++; - if( p->iLimit>0 ){ - p->iLimit--; - if( p->iLimit==0 ){ - pc = pOp->p2 - 1; - } - } + if( sqliteSafetyOff(db) ) goto abort_due_to_misuse; + if( xCallback(pArg, pOp->p1, (char**)zStack[i], p->azColName)!=0 ){ + rc = SQLITE_ABORT; } + if( sqliteSafetyOn(db) ) goto abort_due_to_misuse; p->nCallback++; } POPSTACK; diff --git a/src/vdbe.h b/src/vdbe.h index e8ffe6d360..4e2327e9da 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -15,7 +15,7 @@ ** or VDBE. The VDBE implements an abstract machine that runs a ** simple program to access and modify the underlying database. ** -** $Id: vdbe.h,v 1.54 2002/06/08 23:25:09 drh Exp $ +** $Id: vdbe.h,v 1.55 2002/06/14 22:38:43 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ @@ -204,9 +204,10 @@ typedef struct VdbeOp VdbeOp; #define OP_Function 119 #define OP_Limit 120 +#define OP_LimitCk 121 -#define OP_MAX 120 +#define OP_MAX 121 /* ** Prototypes for the VDBE interface. See comments on the implementation diff --git a/src/where.c b/src/where.c index c658ab59e7..d7b24dfa69 100644 --- a/src/where.c +++ b/src/where.c @@ -13,7 +13,7 @@ ** the WHERE clause of SQL statements. Also found here are subroutines ** to generate VDBE code to evaluate expressions. ** -** $Id: where.c,v 1.51 2002/06/14 20:58:45 drh Exp $ +** $Id: where.c,v 1.52 2002/06/14 22:38:43 drh Exp $ */ #include "sqliteInt.h" @@ -258,6 +258,7 @@ WhereInfo *sqliteWhereBegin( */ if( pWhere && sqliteExprIsConstant(pWhere) ){ sqliteExprIfFalse(pParse, pWhere, pWInfo->iBreak, 1); + pWhere = 0; } /* Split the WHERE clause into as many as 32 separate subexpressions diff --git a/test/limit.test b/test/limit.test index f53f7a62b5..60221c497d 100644 --- a/test/limit.test +++ b/test/limit.test @@ -12,7 +12,7 @@ # focus of this file is testing the LIMIT ... OFFSET ... clause # of SELECT statements. # -# $Id: limit.test,v 1.2 2002/05/24 16:14:16 drh Exp $ +# $Id: limit.test,v 1.3 2002/06/14 22:38:43 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -56,5 +56,23 @@ do_test limit-1.7 { execsql {SELECT * FROM t1 AS a, t1 AS b ORDER BY a.x, b.x LIMIT 5 OFFSET 32} } {1 5 0 5 1 5 1 5 1 5 2 5 1 5 3 5 1 5 4 5} +do_test limit-2.1 { + execsql { + CREATE VIEW v1 AS SELECT * FROM t1 LIMIT 2; + SELECT count(*) FROM (SELECT * FROM v1); + } +} 2 +do_test limit-2.2 { + execsql { + CREATE TABLE t2 AS SELECT * FROM t1 LIMIT 2; + SELECT count(*) FROM t2; + } +} 2 +do_test limit-2.3 { + execsql { + SELECT count(*) FROM t1 WHERE rowid IN (SELECT rowid FROM t1 LIMIT 2); + } +} 2 + finish_test diff --git a/test/where.test b/test/where.test index c9848f0e8a..d9991da651 100644 --- a/test/where.test +++ b/test/where.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the use of indices in WHERE clases. # -# $Id: where.test,v 1.7 2002/06/09 01:55:20 drh Exp $ +# $Id: where.test,v 1.8 2002/06/14 22:38:43 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -249,7 +249,7 @@ do_test where-4.2 { count { SELECT * FROM t1 WHERE 1 LIMIT 1 } -} {1 0 4 0} +} {1 0 4 1} do_test where-4.3 { execsql { SELECT 99 WHERE 0