diff --git a/manifest b/manifest index e4b253aca1..7f2c312a8f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Change\sto\sOP_PutIntKey\sto\suse\snew\sbtree\sAPI.\s(CVS\s1346) -D 2004-05-11T00:28:43 +C Updates\sto\ssqlite3BtreeKeyFetch()\sand\ssqlite3BtreeDataFetch().\s(CVS\s1347) +D 2004-05-11T00:58:56 F Makefile.in ab7b0d5118e2da97bac66be8684a1034e3500f5a F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd @@ -23,8 +23,8 @@ F sqlite.def fc4f5734786fe4743cfe2aa98eb2da4b089edb5f F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2 F src/attach.c fa9a58234406d84eeb900517d0c0adc4b2da051a F src/auth.c 5c2f0bea4729c98c2be3b69d6b466fc51448fe79 -F src/btree.c 38cff183ac4f1d1a2e3079c9eeda7137d103e06e -F src/btree.h 91343fe6893b8bc35f496bbcecec095a803e5763 +F src/btree.c ac2de2cfaf183e15ae6e58e52530b37a7de02a0f +F src/btree.h 578dc465c801cf4e7666efbb0fa1c46a54758008 F src/btree_rb.c 9d7973e266ee6f9c61ce592f68742ce9cd5b10e5 F src/build.c 8d9965b3ce5dcc1bd4dac60bd0f14524fea269cb F src/copy.c 4d2038602fd0549d80c59bda27d96f13ea9b5e29 @@ -54,7 +54,7 @@ F src/table.c af14284fa36c8d41f6829e3f2819dce07d3e2de2 F src/tclsqlite.c a38bf2263a097fcc9603e818c291151de1782c11 F src/test1.c 79956f70dddd1a28f8577bbd61c8cf28e5875eb8 F src/test2.c 6195a1ca2c8d0d2d93644e86da3289b403486872 -F src/test3.c bcc9a49e8d2cb7864efb964974e1807f3e0c30c4 +F src/test3.c e238a8ded4d7842cb369a7e03ab1fd0bcee5f15a F src/test4.c b3fab9aea7a8940a8a7386ce1c7e2157b09bd296 F src/test5.c eb39aac8fed61bd930b92613cd705c145244074a F src/tokenize.c e7536dd31205d5afb76c1bdc832dea009c7a3847 @@ -66,7 +66,7 @@ F src/vacuum.c c134702e023db8778e6be59ac0ea7b02315b5476 F src/vdbe.c c6c763868eec22a5d7b23357a89fa7792cc9575b F src/vdbe.h 2dc4d1161b64f5684faa6a2d292e318a185ecb2e F src/vdbeInt.h d5786e1c4f7dadac24e3baeed9847dbfed3016de -F src/vdbeaux.c 943484a2437b6cf40d1ffd3d62e48b5c9a043c5a +F src/vdbeaux.c 7162f1390620257e25070da1ac2b4ec83e472752 F src/where.c 487e55b1f64c8fbf0f46a9a90c2247fc45ae6a9a F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242 F test/attach.test 76f096f384221f775cc0a856e51d5a35f9ee1541 @@ -78,7 +78,7 @@ F test/bind.test 56a57043b42c4664ca705f6050e56717a8a6699a F test/btree.test ed5781db83b6c1de02e62781d44915a9abe3450a F test/btree2.test aa4a6d05b1ea90b1acaf83ba89039dd302a88635 F test/btree4.test 3797b4305694c7af6828675b0f4b1424b8ca30e4 -F test/btree5.test a677d181b3995dc07a6da12c2abdcd4c37ab7a3d +F test/btree5.test 56977bd84ec64a8bc6ffdaa36b6621c2103c10e2 F test/capi2.test ec96e0e235d87b53cbaef3d8e3e0f8ccf32c71ca F test/conflict.test 0911bb2f079046914a6e9c3341b36658c4e2103e F test/copy.test f07ea8d60878da7a67416ab62f78e9706b9d3c45 @@ -188,7 +188,7 @@ F www/sqlite.tcl 3c83b08cf9f18aa2d69453ff441a36c40e431604 F www/tclsqlite.tcl b9271d44dcf147a93c98f8ecf28c927307abd6da F www/vdbe.tcl 9b9095d4495f37697fd1935d10e14c6015e80aa1 F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4 -P bc5a2dafa1df74ba6403b4751ac1c33b0fee2884 -R 8e699e3ea26654772e1f0482b7621587 -U danielk1977 -Z e970f7cae661bb80a4780d6bbbd8aa7f +P c080fed7b58e754bb34afe597ff3b2f399c7d313 +R 8d721f5b30f9e9c24be64343355846eb +U drh +Z c086113e1e7bdfbe291de2f300b02c98 diff --git a/manifest.uuid b/manifest.uuid index a481e23249..19cf852f72 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c080fed7b58e754bb34afe597ff3b2f399c7d313 \ No newline at end of file +a675ac49882887dfcbf671e9092a29aca9eb694e \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 7daab2a7c5..78bf7b2e05 100644 --- a/src/btree.c +++ b/src/btree.c @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btree.c,v 1.124 2004/05/10 23:29:49 drh Exp $ +** $Id: btree.c,v 1.125 2004/05/11 00:58:56 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -1416,6 +1416,41 @@ int sqlite3BtreeKeySize(BtCursor *pCur, u64 *pSize){ return SQLITE_OK; } +/* +** Set *pSize to the number of bytes of data in the entry the +** cursor currently points to. Always return SQLITE_OK. +** Failure is not possible. If the cursor is not currently +** pointing to an entry (which can happen, for example, if +** the database is empty) then *pSize is set to 0. +*/ +int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){ + MemPage *pPage; + unsigned char *cell; + u64 size; + + if( !pCur->isValid ){ + return pCur->status ? pCur->status : SQLITE_INTERNAL; + } + pPage = pCur->pPage; + assert( pPage!=0 ); + assert( pPage->isInit ); + pageIntegrity(pPage); + if( pPage->zeroData ){ + *pSize = 0; + }else{ + assert( pCur->idx>=0 && pCur->idxnCell ); + cell = pPage->aCell[pCur->idx]; + cell += 2; /* Skip the offset to the next cell */ + if( !pPage->leaf ){ + cell += 4; /* Skip the child pointer */ + } + getVarint(cell, &size); + assert( (size & 0x00000000ffffffff)==size ); + *pSize = (u32)size; + } + return SQLITE_OK; +} + /* ** Read payload information from the entry that the pCur cursor is ** pointing to. Begin reading the payload at "offset" and read @@ -1533,93 +1568,6 @@ int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ return getPayload(pCur, offset, amt, (unsigned char*)pBuf, 0); } -/* -** Return a pointer to the key of record that cursor pCur -** is point to if the entire key is in contiguous memory. -** If the key is split up among multiple tables, return 0. -** If pCur is not pointing to a valid entry return 0. -** -** The pointer returned is ephemeral. The key may move -** or be destroyed on the next call to any Btree routine. -** -** This routine is used to do quick key comparisons in the -** common case where the entire key fits in the payload area -** of a cell and does not overflow onto secondary pages. If -** this routine returns 0 for a valid cursor, the caller will -** need to allocate a buffer big enough to hold the whole key -** then use sqlite3BtreeKey() to copy the key value into the -** buffer. That is substantially slower. But fortunately, -** most keys are small enough to fit in the payload area so -** the slower method is rarely needed. -*/ -void *sqlite3BtreeKeyFetch(BtCursor *pCur){ - unsigned char *aPayload; - MemPage *pPage; - Btree *pBt; - u64 nData, nKey; - - assert( pCur!=0 ); - if( !pCur->isValid ){ - return 0; - } - assert( pCur->pPage!=0 ); - assert( pCur->idx>=0 && pCur->idxpPage->nCell ); - pBt = pCur->pBt; - pPage = pCur->pPage; - pageIntegrity(pPage); - assert( pCur->idx>=0 && pCur->idxnCell ); - assert( pPage->intKey==0 ); - aPayload = pPage->aCell[pCur->idx]; - aPayload += 2; /* Skip the next cell index */ - if( !pPage->leaf ){ - aPayload += 4; /* Skip the child pointer */ - } - if( !pPage->zeroData ){ - aPayload += getVarint(aPayload, &nData); - } - aPayload += getVarint(aPayload, &nKey); - if( nKey>pBt->maxLocal ){ - return 0; - } - return aPayload; -} - - -/* -** Set *pSize to the number of bytes of data in the entry the -** cursor currently points to. Always return SQLITE_OK. -** Failure is not possible. If the cursor is not currently -** pointing to an entry (which can happen, for example, if -** the database is empty) then *pSize is set to 0. -*/ -int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){ - MemPage *pPage; - unsigned char *cell; - u64 size; - - if( !pCur->isValid ){ - return pCur->status ? pCur->status : SQLITE_INTERNAL; - } - pPage = pCur->pPage; - assert( pPage!=0 ); - assert( pPage->isInit ); - pageIntegrity(pPage); - if( pPage->zeroData ){ - *pSize = 0; - }else{ - assert( pCur->idx>=0 && pCur->idxnCell ); - cell = pPage->aCell[pCur->idx]; - cell += 2; /* Skip the offset to the next cell */ - if( !pPage->leaf ){ - cell += 4; /* Skip the child pointer */ - } - getVarint(cell, &size); - assert( (size & 0x00000000ffffffff)==size ); - *pSize = (u32)size; - } - return SQLITE_OK; -} - /* ** Read part of the data associated with cursor pCur. Exactly ** "amt" bytes will be transfered into pBuf[]. The transfer @@ -1640,6 +1588,101 @@ int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ return getPayload(pCur, offset, amt, pBuf, 1); } +/* +** Return a pointer to payload information from the entry that the +** pCur cursor is pointing to. The pointer is to the beginning of +** the key if skipKey==0 and it points to the beginning of data if +** skipKey==1. +** +** At least amt bytes of information must be available on the local +** page or else this routine returns NULL. If amt<0 then the entire +** key/data must be available. +** +** This routine is an optimization. It is common for the entire key +** and data to fit on the local page and for there to be no overflow +** pages. When that is so, this routine can be used to access the +** key and data without making a copy. If the key and/or data spills +** onto overflow pages, then getPayload() must be used to reassembly +** the key/data and copy it into a preallocated buffer. +** +** The pointer returned by this routine looks directly into the cached +** page of the database. The data might change or move the next time +** any btree routine is called. +*/ +static const unsigned char *fetchPayload( + BtCursor *pCur, /* Cursor pointing to entry to read from */ + int amt, /* Amount requested */ + int skipKey /* read beginning at data if this is true */ +){ + unsigned char *aPayload; + MemPage *pPage; + Btree *pBt; + u64 nData, nKey; + int maxLocal; + + assert( pCur!=0 && pCur->pPage!=0 ); + assert( pCur->isValid ); + pBt = pCur->pBt; + pPage = pCur->pPage; + pageIntegrity(pPage); + assert( pCur->idx>=0 && pCur->idxnCell ); + aPayload = pPage->aCell[pCur->idx]; + aPayload += 2; /* Skip the next cell index */ + if( !pPage->leaf ){ + aPayload += 4; /* Skip the child pointer */ + } + if( pPage->zeroData ){ + nData = 0; + }else{ + aPayload += getVarint(aPayload, &nData); + } + aPayload += getVarint(aPayload, &nKey); + if( pPage->intKey ){ + nKey = 0; + } + maxLocal = pBt->maxLocal; + if( skipKey ){ + aPayload += nKey; + maxLocal -= nKey; + if( amt<0 ) amt = nData; + assert( amt<=nData ); + }else{ + if( amt<0 ) amt = nKey; + assert( amt<=nKey ); + } + if( amt>maxLocal ){ + return 0; /* If any of the data is not local, return nothing */ + } + return aPayload; +} + + +/* +** Return a pointer to the first amt bytes of the key or data +** for record that cursor pCur is point to if the entire request +** exists in contiguous memory on the main tree page. If any +** any part of the request is on an overflow page, return 0. +** If pCur is not pointing to a valid entry return 0. +** +** If amt<0 then return the entire key or data. +** +** The pointer returned is ephemeral. The key/data may move +** or be destroyed on the next call to any Btree routine. +** +** These routines is used to get quick access to key and data +** in the common case where no overflow pages are used. +** +** It is a fatal error to call these routines with amt values that +** are larger than the key/data size. +*/ +const void *sqlite3BtreeKeyFetch(BtCursor *pCur, int amt){ + return (const void*)fetchPayload(pCur, amt, 0); +} +const void *sqlite3BtreeDataFetch(BtCursor *pCur, int amt){ + return (const void*)fetchPayload(pCur, amt, 1); +} + + /* ** Move the cursor down to a new child page. The newPgno argument is the ** page number of the child page in the byte order of the disk image. @@ -1907,7 +1950,7 @@ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, u64 nKey, int *pRes){ upr = pPage->nCell-1; pageIntegrity(pPage); while( lwr<=upr ){ - void *pCellKey; + const void *pCellKey; u64 nCellKey; pCur->idx = (lwr+upr)/2; sqlite3BtreeKeySize(pCur, &nCellKey); @@ -1919,10 +1962,10 @@ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, u64 nKey, int *pRes){ }else{ c = 0; } - }else if( (pCellKey = sqlite3BtreeKeyFetch(pCur))!=0 ){ + }else if( (pCellKey = sqlite3BtreeKeyFetch(pCur, nCellKey))!=0 ){ c = pCur->xCompare(pCur->pArg, nCellKey, pCellKey, nKey, pKey); }else{ - pCellKey = sqliteMalloc( nCellKey ); + u8 *pCellKey = sqliteMalloc( nCellKey ); if( pCellKey==0 ) return SQLITE_NOMEM; rc = sqlite3BtreeKey(pCur, 0, nCellKey, pCellKey); c = pCur->xCompare(pCur->pArg, nCellKey, pCellKey, nKey, pKey); diff --git a/src/btree.h b/src/btree.h index 625084fc36..a43d152383 100644 --- a/src/btree.h +++ b/src/btree.h @@ -13,7 +13,7 @@ ** subsystem. See comments in the source code for a detailed description ** of what each interface routine does. ** -** @(#) $Id: btree.h,v 1.44 2004/05/10 23:29:50 drh Exp $ +** @(#) $Id: btree.h,v 1.45 2004/05/11 00:58:56 drh Exp $ */ #ifndef _BTREE_H_ #define _BTREE_H_ @@ -85,7 +85,8 @@ int sqlite3BtreeFlags(BtCursor*); int sqlite3BtreePrevious(BtCursor*, int *pRes); int sqlite3BtreeKeySize(BtCursor*, u64 *pSize); int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*); -void *sqlite3BtreeKeyFetch(BtCursor*); +const void *sqlite3BtreeKeyFetch(BtCursor*, int amt); +const void *sqlite3BtreeDataFetch(BtCursor*, int amt); int sqlite3BtreeDataSize(BtCursor*, u32 *pSize); int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*); diff --git a/src/test3.c b/src/test3.c index fe250eb2cc..711857ce61 100644 --- a/src/test3.c +++ b/src/test3.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test3.c,v 1.34 2004/05/10 16:18:48 drh Exp $ +** $Id: test3.c,v 1.35 2004/05/11 00:58:56 drh Exp $ */ #include "sqliteInt.h" #include "pager.h" @@ -863,7 +863,8 @@ static int btree_eof( /* ** Usage: btree_keysize ID ** -** Return the number of bytes of key. +** Return the number of bytes of key. For an INTKEY table, this +** returns the key itself. */ static int btree_keysize( void *NotUsed, @@ -963,6 +964,80 @@ static int btree_data( return SQLITE_OK; } +/* +** Usage: btree_fetch_key ID AMT +** +** Use the sqlite3BtreeKeyFetch() routine to get AMT bytes of the key. +** If sqlite3BtreeKeyFetch() fails, return an empty string. +*/ +static int btree_fetch_key( + void *NotUsed, + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int argc, /* Number of arguments */ + const char **argv /* Text of each argument */ +){ + BtCursor *pCur; + int n; + u64 nKey; + const char *zBuf; + char zStatic[1000]; + + if( argc!=3 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " ID AMT\"", 0); + return TCL_ERROR; + } + if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR; + if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR; + sqlite3BtreeKeySize(pCur, &nKey); + zBuf = sqlite3BtreeKeyFetch(pCur, n); + if( zBuf ){ + assert( nKey0 ) nKey = n; + memcpy(zStatic, zBuf, (int)nKey); + zStatic[nKey] = 0; + Tcl_AppendResult(interp, zStatic, 0); + } + return TCL_OK; +} + +/* +** Usage: btree_fetch_data ID AMT +** +** Use the sqlite3BtreeDataFetch() routine to get AMT bytes of the key. +** If sqlite3BtreeDataFetch() fails, return an empty string. +*/ +static int btree_fetch_data( + void *NotUsed, + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int argc, /* Number of arguments */ + const char **argv /* Text of each argument */ +){ + BtCursor *pCur; + int n; + u32 nData; + const char *zBuf; + char zStatic[1000]; + + if( argc!=3 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " ID AMT \"", 0); + return TCL_ERROR; + } + if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR; + if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR; + sqlite3BtreeDataSize(pCur, &nData); + zBuf = sqlite3BtreeDataFetch(pCur, n); + if( zBuf ){ + assert( nData0 ) nData = n; + memcpy(zStatic, zBuf, (int)nData); + zStatic[nData] = 0; + Tcl_AppendResult(interp, zStatic, 0); + } + return TCL_OK; +} + /* ** Usage: btree_payload_size ID ** @@ -1096,6 +1171,8 @@ int Sqlitetest3_Init(Tcl_Interp *interp){ { "btree_keysize", (Tcl_CmdProc*)btree_keysize }, { "btree_key", (Tcl_CmdProc*)btree_key }, { "btree_data", (Tcl_CmdProc*)btree_data }, + { "btree_fetch_key", (Tcl_CmdProc*)btree_fetch_key }, + { "btree_fetch_data", (Tcl_CmdProc*)btree_fetch_data }, { "btree_payload_size", (Tcl_CmdProc*)btree_payload_size }, { "btree_first", (Tcl_CmdProc*)btree_first }, { "btree_last", (Tcl_CmdProc*)btree_last }, diff --git a/src/vdbeaux.c b/src/vdbeaux.c index d6f3c2fdbe..16ff2d7195 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -1073,7 +1073,8 @@ int sqlite2BtreeKeyCompare( int nIgnore, /* Ignore this many bytes at the end of pCur */ int *pResult /* Write the result here */ ){ - void *pCellKey; + const void *pCellKey; + void *pMallocedKey; u64 nCellKey; int rc; @@ -1084,18 +1085,18 @@ int sqlite2BtreeKeyCompare( return SQLITE_OK; } - pCellKey = sqlite3BtreeKeyFetch(pCur); + pCellKey = sqlite3BtreeKeyFetch(pCur, nCellKey); if( pCellKey ){ *pResult = memcmp(pCellKey, pKey, nKey>nCellKey?nCellKey:nKey); return SQLITE_OK; } - pCellKey = sqliteMalloc( nCellKey ); - if( pCellKey==0 ) return SQLITE_NOMEM; + pMallocedKey = sqliteMalloc( nCellKey ); + if( pMallocedKey==0 ) return SQLITE_NOMEM; - rc = sqlite3BtreeKey(pCur, 0, nCellKey, pCellKey); - *pResult = memcmp(pCellKey, pKey, nKey>nCellKey?nCellKey:nKey); - sqliteFree(pCellKey); + rc = sqlite3BtreeKey(pCur, 0, nCellKey, pMallocedKey); + *pResult = memcmp(pMallocedKey, pKey, nKey>nCellKey?nCellKey:nKey); + sqliteFree(pMallocedKey); return rc; } @@ -1246,7 +1247,7 @@ int sqlite3VdbeDeserialize( pMem->i = 0; for(ii=0; iii = pMem->i<<8 + zBuf[ii+ret]; + pMem->i = (pMem->i<<8) + zBuf[ii+ret]; } /* If this is a 1, 2 or 4 byte integer, extend the sign-bit if need be. */ diff --git a/test/btree5.test b/test/btree5.test index 9c3310990b..f89ccf8ae5 100644 --- a/test/btree5.test +++ b/test/btree5.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is btree database backend # -# $Id: btree5.test,v 1.1 2004/05/10 18:45:10 drh Exp $ +# $Id: btree5.test,v 1.2 2004/05/11 00:58:56 drh Exp $ set testdir [file dirname $argv0] @@ -116,9 +116,25 @@ proc check_table {N} { btree_first $c1 set cnt 0 while {![btree_eof $c1]} { - if {[btree_data $c1] ne "data-for-[btree_key $c1]"} { + if {[set data [btree_data $c1]] ne "data-for-[btree_key $c1]"} { return "wrong data for entry $cnt" } + set n [string length $data] + set fdata1 [btree_fetch_data $c1 $n] + set fdata2 [btree_fetch_data $c1 -1] + if {$fdata1 ne "" && $fdata1 ne $data} { +puts "fdata1=[list $fdata1] data=[list $data]" + return "DataFetch returned the wrong value with amt=$n" + } + if {$fdata1 ne $fdata2} { + return "DataFetch returned the wrong value when amt=-1" + } + if {$n>10} { + set fdata3 [btree_fetch_data $c1 10] + if {$fdata3 ne [string range $data 0 9]} { + return "DataFetch returned the wrong value when amt=10" + } + } incr cnt btree_next $c1 }