diff --git a/manifest b/manifest index 6aae0d09e1..2586360507 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sinitial\sinfrastructure\sfor\scursors.\s\sIn\swhere.c,\soptimize\sout\sclauses\nof\sthe\sform\s"ORDER\sBY\srowid"\sif\sa\stable\sscan\sis\sbeing\sperformed.\s\sDo\sa\nreverse\stable\sscan\sif\s"ORDER\sBY\srowid\sDESC"\sis\spresent.\s(CVS\s2141) -D 2004-11-22T19:12:20 +C More\swork\son\sthe\simplementation\sof\scursors,\sbut\sthey\sare\sstill\snot\nfunctioning.\s(CVS\s2142) +D 2004-11-23T01:47:30 F Makefile.in 8291610f5839939a5fbff4dbbf85adb0fe1ac37f F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457 F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1 @@ -32,10 +32,10 @@ F src/auth.c 3b81f2a42f48a62c2c9c9b0eda31a157c681edea F src/btree.c 05fe410ebbcbac72b66bc3aeeadf7e5588b0699e F src/btree.h 861e40b759a195ba63819740e484390012cf81ab F src/build.c b62389de594d0b413068d6e067794249a1f1d209 -F src/cursor.c dddc11922f583645318c9446d0d1920d0bc940cd +F src/cursor.c f883813759742068890b1f699335872bfa8fdf41 F src/date.c 65536e7ea04fdde6e0551264fca15966966e171f -F src/delete.c cf185995e20a61c0fecc2a9a9a3b19bd18bd05b3 -F src/expr.c b985b42a3bce753040ab3415669d8a39b0538e5f +F src/delete.c 9083377a4c5b152b4466021592f32e3e8a3819e3 +F src/expr.c 7987e5f50c189d4c2550f247f8039c06ea272345 F src/func.c b668e5ad043176049454c95a6a780367a0e8f6bb F src/hash.c a97721a55440b7bea31ffe471bb2f6b4123cddd5 F src/hash.h 1b0c445e1c89ff2aaad9b4605ba61375af001e84 @@ -59,10 +59,10 @@ F src/parse.y ceba179b9703657180963568f54b0e75f33e36e1 F src/pragma.c d6406e12c9eac353b3a026b50d41e4fd561afcc2 F src/printf.c 3d20b21cfecadacecac3fb7274e746cb81d3d357 F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3 -F src/select.c b00478f33d6a9beb56f85b5d6b2e7325793c0bdf +F src/select.c 36cc9da999596578566e167d310e99f2005a7f03 F src/shell.c e8f4f486cbf6e60d81173146ac8a6522c930fa51 F src/sqlite.h.in 6d0e82c24ef3f84a10b468119f3943a5dfc806c7 -F src/sqliteInt.h f783c2793ca02b1e8bee587f526932a43da14173 +F src/sqliteInt.h 1dc643cf84f1d83bd45e314f724bb824ee24c000 F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9 F src/tclsqlite.c 7f1a1a678140e6901c8954590ca2aabe50b48f71 F src/test1.c b7d94c54e58f95452387a5cabdf98b2be8059f29 @@ -72,17 +72,17 @@ F src/test4.c 7c6b9fc33dd1f3f93c7f1ee6e5e6d016afa6c1df F src/test5.c 64f08b2a50ef371a1bd68ff206829e7b1b9997f5 F src/tokenize.c 2ad3d1ae1a0a70746db0b31a0a74f58050a3c39a F src/trigger.c 0c91b56182560263733e4b035acdb939bd1cf0e2 -F src/update.c 24a605a4250fe4a0b173bedd7132d1892ad27090 +F src/update.c d79d1936c5603b3ce2ee22557618b6baad67efba F src/utf.c e45ce11be6922408cd381561721f6cca7d3b992a F src/util.c 4a8db4e97a3cfda12ad8dda3e77dd2d00ad1de5e F src/vacuum.c 705256e1111521fa04f0029de7f1667bc131d015 -F src/vdbe.c ba3a920731d43bcf2497d558238400369008531a +F src/vdbe.c b6caad985b7804b8158ab01b2021f1890570b7f3 F src/vdbe.h 067ca8d6750ba4f69a50284765e5883dee860181 F src/vdbeInt.h 6017100adff362b8dfa37a69e3f1431f084bfa5b F src/vdbeapi.c 74be7f96c0a1ac275661f8b32276ac521d9ce37c F src/vdbeaux.c dc06bbb8511d07f8d45ed2ea760f35f0736a690c F src/vdbemem.c 5876c8abf4374fef671f4fd8dc333ef3fc95a2f0 -F src/where.c be486196af29c4be8cc49275bc483a8af4cb8dcd +F src/where.c f9d3b6f2243a0eb9065fa4c465fe0831a64b7db7 F test/all.test 929bfa932b55e75c96fe2203f7650ba451c1862c F test/alter.test 95c57a4f461fa81293e0dccef7f83889aadb169a F test/attach.test a71117bab079c8a3a955e2d0270a76f9cb445935 @@ -115,7 +115,7 @@ F test/conflict.test c5b849b01cfbe0a4f63a90cba6f68e2fe3a75f87 F test/corrupt.test 0080ddcece23e8ba47c44608c4fb73fd4d1d8ce2 F test/crash.test 48b481769dd0ead25b0dfc0150853bfa39a3b65c F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2 -F test/cursor.test 376eabf81c727e15debc72d255d2c2624b9076c7 +F test/cursor.test d7c65ea0fc4e321e12fbcf5c7f3e2211ef45379b F test/date.test dda578ec1857837156bd8b32f8e09d81d7d7881c F test/delete.test fc29491f6a7ac899ce29f4549a104809e245d9a6 F test/delete2.test e382b6a97787197eb8b93dd4ccd37797c3725ea3 @@ -261,7 +261,7 @@ F www/tclsqlite.tcl 560ecd6a916b320e59f2917317398f3d59b7cc25 F www/vdbe.tcl 095f106d93875c94b47367384ebc870517431618 F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0 F www/whentouse.tcl fdacb0ba2d39831e8a6240d05a490026ad4c4e4c -P 3d2536c479c943b3a55047898068625f91c872ae -R 72745c2cddac7210800e29c25c7bccd9 +P fc8c1393c86017a816beb52725b68af3b973f979 +R 98af4fe305ebafc3241be9d01cb582a6 U drh -Z a1c5d9d0383eefbb16dbdfab1a5f6edc +Z 5a67f7855a17ccbeb24c26f03275c455 diff --git a/manifest.uuid b/manifest.uuid index 1b26bb4bdb..a7b2ef862e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fc8c1393c86017a816beb52725b68af3b973f979 \ No newline at end of file +8b61d1ae1ca469b80f2b344d550c3b363448b193 \ No newline at end of file diff --git a/src/cursor.c b/src/cursor.c index 061fcb6994..6bd6f6cd72 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -21,7 +21,7 @@ ** cursors, they are really very different things. It is worth your while ** to fully understand the difference. ** -** @(#) $Id: cursor.c,v 1.1 2004/11/22 19:12:20 drh Exp $ +** @(#) $Id: cursor.c,v 1.2 2004/11/23 01:47:30 drh Exp $ */ #ifndef SQLITE_OMIT_CURSOR #include "sqliteInt.h" @@ -86,13 +86,16 @@ void sqlite3CursorCreate(Parse *pParse, Token *pName, Select *pSelect){ } db->apSqlCursor[i] = pNew = sqliteMallocRaw( sizeof(*pNew) + pName->n + 1 ); if( pNew==0 ) goto end_create_cursor; + pNew->idx = i; pNew->zName = (char*)&pNew[1]; memcpy(pNew->zName, pName->z, pName->n); pNew->zName[pName->n] = 0; pNew->pSelect = sqlite3SelectDup(pSelect); - pNew->nPtr = 0; - pNew->aPtr = 0; - pNew->idx = i; + pNew->nPtr = 2; + pNew->aPtr = sqliteMalloc( sizeof(Mem)*2 ); + for(i=0; i<2; i++){ + pNew->aPtr[i].flags = MEM_Null; + } end_create_cursor: sqlite3SelectDelete(pSelect); @@ -117,6 +120,26 @@ void sqlite3CursorClose(Parse *pParse, Token *pName){ sqlite3CursorDelete(p); } +/* +** Reverse the direction the ORDER BY clause on the SELECT statement. +*/ +static void reverseSortOrder(Select *p){ + if( p->pOrderBy==0 ){ + /* If there no ORDER BY clause, add a new one that is "rowid DESC" */ + static const Token rowid = { "ROWID", 0, 5 }; + Expr *pExpr = sqlite3Expr(TK_ID, 0, 0, &rowid); + ExprList *pList = sqlite3ExprListAppend(0, pExpr, 0); + if( pList ) pList->a[0].sortOrder = SQLITE_SO_DESC; + p->pOrderBy = pList; + }else{ + int i; + ExprList *pList = p->pOrderBy; + for(i=0; inExpr; i++){ + pList->a[i].sortOrder = !pList->a[i].sortOrder; + } + } +} + /* ** The parser calls this routine when it sees a complete FETCH statement. ** This routine generates code to implement the FETCH. @@ -142,26 +165,73 @@ void sqlite3Fetch(Parse *pParse, Token *pName, IdList *pInto){ pCopy->pFetch = &sFetch; switch( pParse->fetchDir ){ case TK_FIRST: { + sFetch.isBackwards = 0; + sFetch.doRewind = 1; + pCopy->nLimit = pParse->dirArg1; + pCopy->nOffset = 0; break; } case TK_LAST: { + reverseSortOrder(pCopy); + sFetch.isBackwards = 1; + sFetch.doRewind = 1; + pCopy->nLimit = pParse->dirArg1; + pCopy->nOffset = 0; break; } case TK_NEXT: { + sFetch.isBackwards = 0; + sFetch.doRewind = 0; + pCopy->nLimit = pParse->dirArg1; + pCopy->nOffset = 0; break; } case TK_PRIOR: { + reverseSortOrder(pCopy); + sFetch.isBackwards = 1; + sFetch.doRewind = 0; + pCopy->nLimit = pParse->dirArg1; + pCopy->nOffset = 0; break; } case TK_ABSOLUTE: { + sFetch.isBackwards = 0; + sFetch.doRewind = 1; + pCopy->nLimit = pParse->dirArg1; + pCopy->nOffset = pParse->dirArg2; break; } default: { assert( pParse->fetchDir==TK_RELATIVE ); + if( pParse->dirArg2>=0 ){ + /* The index parameter is positive. Move forward from the current + ** location */ + sFetch.isBackwards = 0; + sFetch.doRewind = 0; + pCopy->nLimit = pParse->dirArg1; + pCopy->nOffset = pParse->dirArg2; + }else{ + /* The index is negative. We have to code two separate SELECTs. + ** The first one seeks to the no position and the second one does + ** the query. + */ + Select *pSeek = sqlite3SelectDup(pCopy); + reverseSortOrder(pSeek); + sFetch.isBackwards = 1; + sFetch.doRewind = 0; + pSeek->nLimit = pParse->dirArg2; + pSeek->pFetch = &sFetch; + sqlite3Select(pParse, pSeek, SRT_Discard, 0, 0, 0, 0, 0); + sFetch.isBackwards = 0; + sFetch.doRewind = 0; + pCopy->nLimit = pParse->dirArg1; + pCopy->nOffset = 0; + } break; } } sqlite3Select(pParse, pCopy, SRT_Callback, 0, 0, 0, 0, 0); + end_fetch: sqlite3IdListDelete(pInto); } diff --git a/src/delete.c b/src/delete.c index 4887d69848..a0d30ea9d3 100644 --- a/src/delete.c +++ b/src/delete.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle DELETE FROM statements. ** -** $Id: delete.c,v 1.90 2004/11/22 10:02:10 danielk1977 Exp $ +** $Id: delete.c,v 1.91 2004/11/23 01:47:30 drh Exp $ */ #include "sqliteInt.h" @@ -229,7 +229,7 @@ void sqlite3DeleteFrom( /* Begin the database scan */ - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 1, 0); + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 1, 0, 0); if( pWInfo==0 ) goto delete_from_cleanup; /* Remember the key of every item to be deleted. diff --git a/src/expr.c b/src/expr.c index 03fd866bdd..de96ada747 100644 --- a/src/expr.c +++ b/src/expr.c @@ -12,7 +12,7 @@ ** This file contains routines used for analyzing expressions and ** for generating VDBE code that evaluates expressions in SQLite. ** -** $Id: expr.c,v 1.173 2004/11/22 19:12:20 drh Exp $ +** $Id: expr.c,v 1.174 2004/11/23 01:47:30 drh Exp $ */ #include "sqliteInt.h" #include @@ -179,7 +179,7 @@ static int codeCompare( ** for this node is obtained from sqliteMalloc(). The calling function ** is responsible for making sure the node eventually gets freed. */ -Expr *sqlite3Expr(int op, Expr *pLeft, Expr *pRight, Token *pToken){ +Expr *sqlite3Expr(int op, Expr *pLeft, Expr *pRight, const Token *pToken){ Expr *pNew; pNew = sqliteMalloc( sizeof(Expr) ); if( pNew==0 ){ diff --git a/src/select.c b/src/select.c index d17c269609..d16f43d233 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.216 2004/11/22 19:12:21 drh Exp $ +** $Id: select.c,v 1.217 2004/11/23 01:47:30 drh Exp $ */ #include "sqliteInt.h" @@ -2547,16 +2547,8 @@ int sqlite3Select( /* Begin the database scan */ -#if 0 /* ndef SQLITE_OMIT_CURSOR */ - if( p->pFetch ){ - pWInfo = sqlite3WhereBeginFetch(pParse, pTabList, pWhere, - pOrderby, p->pFetch); - }else -#endif - { - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, - pGroupBy ? 0 : &pOrderBy); - } + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, + pGroupBy ? 0 : &pOrderBy, p->pFetch); if( pWInfo==0 ) goto select_end; /* Use the standard inner loop if we are not dealing with diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 0616d05a37..43f9afa1fe 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.343 2004/11/22 19:12:21 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.344 2004/11/23 01:47:30 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -1281,7 +1281,7 @@ void sqlite3Dequote(char*); int sqlite3KeywordCode(const char*, int); int sqlite3RunParser(Parse*, const char*, char **); void sqlite3FinishCoding(Parse*); -Expr *sqlite3Expr(int, Expr*, Expr*, Token*); +Expr *sqlite3Expr(int, Expr*, Expr*, const Token*); Expr *sqlite3RegisterExpr(Parse*,Token*); Expr *sqlite3ExprAnd(Expr*, Expr*); void sqlite3ExprSpan(Expr*,Token*,Token*); @@ -1341,7 +1341,7 @@ void sqlite3OpenTableForReading(Vdbe*, int iCur, Table*); void sqlite3OpenTable(Vdbe*, int iCur, Table*, int); void sqlite3DeleteFrom(Parse*, SrcList*, Expr*); void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int); -WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, int, ExprList**); +WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, int, ExprList**, Fetch*); void sqlite3WhereEnd(WhereInfo*); void sqlite3ExprCode(Parse*, Expr*); int sqlite3ExprCodeExprList(Parse*, ExprList*); diff --git a/src/update.c b/src/update.c index 501ca6c3eb..4dd040ee5b 100644 --- a/src/update.c +++ b/src/update.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** -** $Id: update.c,v 1.96 2004/11/22 10:02:11 danielk1977 Exp $ +** $Id: update.c,v 1.97 2004/11/23 01:47:30 drh Exp $ */ #include "sqliteInt.h" @@ -235,7 +235,7 @@ void sqlite3Update( /* Begin the database scan */ - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 1, 0); + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 1, 0, 0); if( pWInfo==0 ) goto update_cleanup; /* Remember the index of every item to be updated. diff --git a/src/vdbe.c b/src/vdbe.c index 31ecea193c..dc4963eafa 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -43,7 +43,7 @@ ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** -** $Id: vdbe.c,v 1.430 2004/11/18 02:10:55 drh Exp $ +** $Id: vdbe.c,v 1.431 2004/11/23 01:47:30 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -4433,6 +4433,56 @@ case OP_Vacuum: { break; } +#ifndef SQLITE_OMIT_CURSOR +/* Opcode: CursorStore P1 P2 * +** +** The implementation of SQL cursors (not to be confused with VDBE cursors +** or B-tree cursors) stores information in the SQLite database connection +** structure (the sqlite3* pointer) that identifies the row +** in a table that an SQL cursor is pointing to. This opcode is +** used to store that information. P1 is an index of an SQL cursor. +** P2 is the index of a memory slot within that SQL cursor. This opcode +** pops the top of the stack and stores it in the SQL cursor. +*/ +case OP_CursorStore: { + SqlCursor *pCursor; + assert( pTos>=p->aStack ); + assert( pOp->p1>=0 && pOp->p1nSqlCursor ); + pCursor = db->apSqlCursor[pOp->p1]; + assert( pCursor!=0 ); + assert( pOp->p2>=0 && pOp->p2<2 ); + rc = sqlite3VdbeMemMove(&pCursor->aPtr[pOp->p1], pTos); + pTos--; + break; +} + +/* Opcode: CursorLoad P1 P2 * +** +** The implementation of SQL cursors (not to be confused with VDBE cursors +** or B-tree cursors) stores information in the SQLite database connection +** structure (the sqlite3* pointer) that effectively records the current +** location in a table that an SQL cursor is pointing to. This opcode is +** used to recover that information. P1 is an index of an SQL cursor. +** P2 is the index of a memory slot within that SQL cursor. This opcode +** pushes a new value onto the stack which is a copy of the information +** obtained from entry P2 of cursor P1. +*/ +case OP_CursorLoad: { + SqlCursor *pCursor; + int i = pOp->p1; + assert( pTos>=p->aStack ); + assert( pOp->p1>=0 && pOp->p1nSqlCursor ); + pCursor = db->apSqlCursor[pOp->p1]; + assert( pCursor!=0 ); + assert( pOp->p2>=0 && pOp->p2<2 ); + pTos++; + sqlite3VdbeMemShallowCopy(pTos, &pCursor->aPtr[i], MEM_Ephem); + break; +} +#endif /* SQLITE_OMIT_CURSOR */ + + + /* An other opcode is illegal... */ default: { diff --git a/src/where.c b/src/where.c index 97cf37d698..1e7073c2f0 100644 --- a/src/where.c +++ b/src/where.c @@ -12,7 +12,7 @@ ** This module contains C code that generates VDBE code used to process ** the WHERE clause of SQL statements. ** -** $Id: where.c,v 1.119 2004/11/22 19:12:21 drh Exp $ +** $Id: where.c,v 1.120 2004/11/23 01:47:31 drh Exp $ */ #include "sqliteInt.h" @@ -498,17 +498,14 @@ static void codeEqualityTerm( ** ** If the where clause loops cannot be arranged to provide the correct ** output order, then the *ppOrderBy is unchanged. -** -** If parameter iTabCur is non-negative, then it is a cursor already open -** on table pTabList->aSrc[0]. Use this cursor instead of opening a new -** one. */ WhereInfo *sqlite3WhereBegin( Parse *pParse, /* The parser context */ SrcList *pTabList, /* A list of all tables to be scanned */ Expr *pWhere, /* The WHERE clause */ int pushKey, /* If TRUE, leave the table key on the stack */ - ExprList **ppOrderBy /* An ORDER BY clause, or NULL */ + ExprList **ppOrderBy, /* An ORDER BY clause, or NULL */ + Fetch *pFetch /* Initial location of cursors. NULL otherwise */ ){ int i; /* Loop counter */ WhereInfo *pWInfo; /* Will become the return value of this function */ @@ -529,6 +526,9 @@ WhereInfo *sqlite3WhereBegin( */ assert( pushKey==0 || pTabList->nSrc==1 ); + /* + */ + /* Split the WHERE clause into separate subexpressions where each ** subexpression is separated by an AND operator. If the aExpr[] ** array fills up, the last entry might point to an expression which diff --git a/test/cursor.test b/test/cursor.test index dcd83a2a43..815913e3ba 100644 --- a/test/cursor.test +++ b/test/cursor.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is DECLARE...CURSOR functionality # -# $Id: cursor.test,v 1.1 2004/11/22 19:12:21 drh Exp $ +# $Id: cursor.test,v 1.2 2004/11/23 01:47:31 drh Exp $ # set testdir [file dirname $argv0]