1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-21 09:00:59 +03:00

Performance improvements when reading large blobs, especially if

SQLITE_DIRECT_OVERFLOW_READ is defined.

FossilOrigin-Name: 2312eb6a9eb31051db4e0baf19e033ba39adc7b1
This commit is contained in:
drh
2014-04-04 18:49:19 +00:00
8 changed files with 139 additions and 124 deletions

View File

@@ -1,5 +1,5 @@
C Merge\schanges\sto\sthe\squery\splanner\sthat\sstrive\sto\sensure\sthat\sany\sindex\s\nusage\sthat\sis\sa\sproper\ssubset\sof\ssome\sother\sindex\susage\salways\shas\sa\sslightly\nhigher\scost. C Performance\simprovements\swhen\sreading\slarge\sblobs,\sespecially\sif\nSQLITE_DIRECT_OVERFLOW_READ\sis\sdefined.
D 2014-04-04T18:20:35.620 D 2014-04-04T18:49:19.417
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 2ef13430cd359f7b361bb863504e227b25cc7f81 F Makefile.in 2ef13430cd359f7b361bb863504e227b25cc7f81
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -164,9 +164,9 @@ F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
F src/backup.c a729e63cf5cd1829507cb7b8e89f99b95141bb53 F src/backup.c a729e63cf5cd1829507cb7b8e89f99b95141bb53
F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb
F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
F src/btree.c a59a199f21338ae1847d69f5db87c3e8ef1b1578 F src/btree.c 6c9b51abd404ce5b78b173b6f2248e8cb824758c
F src/btree.h 232836cb51753f2e96aa8ce0f052c6df850f76ba F src/btree.h d79306df4ed9181b48916737fe8871a4392c4594
F src/btreeInt.h 0be66063468a520e4d66b80c7a1dc26d04ee6ea4 F src/btreeInt.h cf180d86b2e9e418f638d65baa425c4c69c0e0e3
F src/build.c 0d50ef95aad63f4c4fc47f3fa2670d4557c45db0 F src/build.c 0d50ef95aad63f4c4fc47f3fa2670d4557c45db0
F src/callback.c 174e3c8656bc29f91d710ab61550d16eea34be98 F src/callback.c 174e3c8656bc29f91d710ab61550d16eea34be98
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
@@ -239,7 +239,7 @@ F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60
F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8 F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8
F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12 F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12
F src/test_backup.c 3875e899222b651e18b662f86e0e50daa946344e F src/test_backup.c 3875e899222b651e18b662f86e0e50daa946344e
F src/test_btree.c 5b89601dcb42a33ba8b820a6b763cc9cb48bac16 F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f
F src/test_config.c 0336e0bdbe541b4af89d7e3dd0656e8e6b51e585 F src/test_config.c 0336e0bdbe541b4af89d7e3dd0656e8e6b51e585
F src/test_demovfs.c 69b2085076654ebc18014cbc6386f04409c959a9 F src/test_demovfs.c 69b2085076654ebc18014cbc6386f04409c959a9
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
@@ -283,7 +283,7 @@ F src/vdbe.h 394464909ed682334aa3d5831aae0c2fe2abef94
F src/vdbeInt.h e6d83e5bfd62fc6685ba1ed6153f7099f82de9f7 F src/vdbeInt.h e6d83e5bfd62fc6685ba1ed6153f7099f82de9f7
F src/vdbeapi.c 0ed6053f947edd0b30f64ce5aeb811872a3450a4 F src/vdbeapi.c 0ed6053f947edd0b30f64ce5aeb811872a3450a4
F src/vdbeaux.c 1153175fb57a8454e1c8cf79b59b7bf92b26779d F src/vdbeaux.c 1153175fb57a8454e1c8cf79b59b7bf92b26779d
F src/vdbeblob.c 15377abfb59251bccedd5a9c7d014a895f0c04aa F src/vdbeblob.c 9205ce9d3b064d9600f8418a897fc88b5687d9ac
F src/vdbemem.c 6fc77594c60f6155404f3f8d71bf36d1fdeb4447 F src/vdbemem.c 6fc77594c60f6155404f3f8d71bf36d1fdeb4447
F src/vdbesort.c 4abb7c0f8f19b7d7d82f4558d5da1a30fdf9ea38 F src/vdbesort.c 4abb7c0f8f19b7d7d82f4558d5da1a30fdf9ea38
F src/vdbetrace.c 6f52bc0c51e144b7efdcfb2a8f771167a8816767 F src/vdbetrace.c 6f52bc0c51e144b7efdcfb2a8f771167a8816767
@@ -574,7 +574,7 @@ F test/fts4merge4.test c19c85ca1faa7b6d536832b49c12e1867235f584
F test/fts4noti.test aed33ba44808852dcb24bf70fa132e7bf530f057 F test/fts4noti.test aed33ba44808852dcb24bf70fa132e7bf530f057
F test/fts4unicode.test 01ec3fe2a7c3cfff3b4c0581b83caa11b33efa36 F test/fts4unicode.test 01ec3fe2a7c3cfff3b4c0581b83caa11b33efa36
F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d
F test/func.test a21814945d32137412b553d98ad2107f9b2173a9 F test/func.test c2cbfc23d554c5bf8678d0fb271aa4f8ef94839c
F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f
F test/func3.test dbccee9133cfef1473c59ec07b5f0262b9d72f9a F test/func3.test dbccee9133cfef1473c59ec07b5f0262b9d72f9a
F test/func4.test 6beacdfcb0e18c358e6c2dcacf1b65d1fa80955f F test/func4.test 6beacdfcb0e18c358e6c2dcacf1b65d1fa80955f
@@ -1160,8 +1160,8 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
P e54330b43127e46fc6494748cbb353a6fc91cfd7 7473c4dfc10a47594affa6f4e071a08dc8838c0c P 683dd379a293b2f330e1e4cd746f190527fe48ee 834bf1c367e1ccd498c7f9f843be2d2aa11ffb3c
R dcb8d4aedf5f44fc76078dc97c08f05d R 3de83004edd963c4959934c7f5a61b03
T +closed 7473c4dfc10a47594affa6f4e071a08dc8838c0c T +closed 834bf1c367e1ccd498c7f9f843be2d2aa11ffb3c
U drh U drh
Z b9b64aa08b9101816e80a031012d4116 Z f4df6ef034f1a63bb656a3ddc69e0011

View File

@@ -1 +1 @@
683dd379a293b2f330e1e4cd746f190527fe48ee 2312eb6a9eb31051db4e0baf19e033ba39adc7b1

View File

@@ -446,16 +446,11 @@ static int cursorHoldsMutex(BtCursor *p){
} }
#endif #endif
#ifndef SQLITE_OMIT_INCRBLOB
/* /*
** Invalidate the overflow page-list cache for cursor pCur, if any. ** Invalidate the overflow cache of the cursor passed as the first argument.
** on the shared btree structure pBt.
*/ */
static void invalidateOverflowCache(BtCursor *pCur){ #define invalidateOverflowCache(pCur) (pCur->curFlags &= ~BTCF_ValidOvfl)
assert( cursorHoldsMutex(pCur) );
sqlite3_free(pCur->aOverflow);
pCur->aOverflow = 0;
}
/* /*
** Invalidate the overflow page-list cache for all cursors opened ** Invalidate the overflow page-list cache for all cursors opened
@@ -469,6 +464,7 @@ static void invalidateAllOverflowCache(BtShared *pBt){
} }
} }
#ifndef SQLITE_OMIT_INCRBLOB
/* /*
** This function is called before modifying the contents of a table ** This function is called before modifying the contents of a table
** to invalidate any incrblob cursors that are open on the ** to invalidate any incrblob cursors that are open on the
@@ -491,16 +487,14 @@ static void invalidateIncrblobCursors(
BtShared *pBt = pBtree->pBt; BtShared *pBt = pBtree->pBt;
assert( sqlite3BtreeHoldsMutex(pBtree) ); assert( sqlite3BtreeHoldsMutex(pBtree) );
for(p=pBt->pCursor; p; p=p->pNext){ for(p=pBt->pCursor; p; p=p->pNext){
if( p->isIncrblobHandle && (isClearTable || p->info.nKey==iRow) ){ if( (p->curFlags & BTCF_Incrblob)!=0 && (isClearTable || p->info.nKey==iRow) ){
p->eState = CURSOR_INVALID; p->eState = CURSOR_INVALID;
} }
} }
} }
#else #else
/* Stub functions when INCRBLOB is omitted */ /* Stub function when INCRBLOB is omitted */
#define invalidateOverflowCache(x)
#define invalidateAllOverflowCache(x)
#define invalidateIncrblobCursors(x,y,z) #define invalidateIncrblobCursors(x,y,z)
#endif /* SQLITE_OMIT_INCRBLOB */ #endif /* SQLITE_OMIT_INCRBLOB */
@@ -2563,7 +2557,8 @@ static int countValidCursors(BtShared *pBt, int wrOnly){
BtCursor *pCur; BtCursor *pCur;
int r = 0; int r = 0;
for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
if( (wrOnly==0 || pCur->wrFlag) && pCur->eState!=CURSOR_FAULT ) r++; if( (wrOnly==0 || (pCur->curFlags & BTCF_WriteFlag)!=0)
&& pCur->eState!=CURSOR_FAULT ) r++;
} }
return r; return r;
} }
@@ -3638,7 +3633,8 @@ static int btreeCursor(
pCur->pKeyInfo = pKeyInfo; pCur->pKeyInfo = pKeyInfo;
pCur->pBtree = p; pCur->pBtree = p;
pCur->pBt = pBt; pCur->pBt = pBt;
pCur->wrFlag = (u8)wrFlag; assert( wrFlag==0 || wrFlag==BTCF_WriteFlag );
pCur->curFlags = wrFlag;
pCur->pNext = pBt->pCursor; pCur->pNext = pBt->pCursor;
if( pCur->pNext ){ if( pCur->pNext ){
pCur->pNext->pPrev = pCur; pCur->pNext->pPrev = pCur;
@@ -3708,7 +3704,7 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){
releasePage(pCur->apPage[i]); releasePage(pCur->apPage[i]);
} }
unlockBtreeIfUnused(pBt); unlockBtreeIfUnused(pBt);
invalidateOverflowCache(pCur); sqlite3DbFree(pBtree->db, pCur->aOverflow);
/* sqlite3_free(pCur); */ /* sqlite3_free(pCur); */
sqlite3BtreeLeave(pBtree); sqlite3BtreeLeave(pBtree);
} }
@@ -3747,7 +3743,7 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){
if( pCur->info.nSize==0 ){ if( pCur->info.nSize==0 ){
int iPage = pCur->iPage; int iPage = pCur->iPage;
btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info);
pCur->validNKey = 1; pCur->curFlags |= BTCF_ValidNKey;
}else{ }else{
assertCellInfo(pCur); assertCellInfo(pCur);
} }
@@ -3758,7 +3754,7 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){
if( pCur->info.nSize==0 ){ \ if( pCur->info.nSize==0 ){ \
int iPage = pCur->iPage; \ int iPage = pCur->iPage; \
btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); \ btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); \
pCur->validNKey = 1; \ pCur->curFlags |= BTCF_ValidNKey; \
}else{ \ }else{ \
assertCellInfo(pCur); \ assertCellInfo(pCur); \
} }
@@ -3929,10 +3925,12 @@ static int copyPayload(
/* /*
** This function is used to read or overwrite payload information ** This function is used to read or overwrite payload information
** for the entry that the pCur cursor is pointing to. If the eOp ** for the entry that the pCur cursor is pointing to. The eOp
** parameter is 0, this is a read operation (data copied into ** argument is interpreted as follows:
** buffer pBuf). If it is non-zero, a write (data copied from **
** buffer pBuf). ** 0: The operation is a read. Populate the overflow cache.
** 1: The operation is a write. Populate the overflow cache.
** 2: The operation is a read. Do not populate the overflow cache.
** **
** A total of "amt" bytes are read or written beginning at "offset". ** A total of "amt" bytes are read or written beginning at "offset".
** Data is read to or from the buffer pBuf. ** Data is read to or from the buffer pBuf.
@@ -3940,11 +3938,11 @@ static int copyPayload(
** The content being read or written might appear on the main page ** The content being read or written might appear on the main page
** or be scattered out on multiple overflow pages. ** or be scattered out on multiple overflow pages.
** **
** If the BtCursor.isIncrblobHandle flag is set, and the current ** If the current cursor entry uses one or more overflow pages and the
** cursor entry uses one or more overflow pages, this function ** eOp argument is not 2, this function may allocate space for and lazily
** allocates space for and lazily popluates the overflow page-list ** popluates the overflow page-list cache array (BtCursor.aOverflow).
** cache array (BtCursor.aOverflow). Subsequent calls use this ** Subsequent calls use this cache to make seeking to the supplied offset
** cache to make seeking to the supplied offset more efficient. ** more efficient.
** **
** Once an overflow page-list cache has been allocated, it may be ** Once an overflow page-list cache has been allocated, it may be
** invalidated if some other cursor writes to the same table, or if ** invalidated if some other cursor writes to the same table, or if
@@ -3968,15 +3966,22 @@ static int accessPayload(
int iIdx = 0; int iIdx = 0;
MemPage *pPage = pCur->apPage[pCur->iPage]; /* Btree page of current entry */ MemPage *pPage = pCur->apPage[pCur->iPage]; /* Btree page of current entry */
BtShared *pBt = pCur->pBt; /* Btree this cursor belongs to */ BtShared *pBt = pCur->pBt; /* Btree this cursor belongs to */
#ifdef SQLITE_DIRECT_OVERFLOW_READ
int bEnd; /* True if reading to end of data */
#endif
assert( pPage ); assert( pPage );
assert( pCur->eState==CURSOR_VALID ); assert( pCur->eState==CURSOR_VALID );
assert( pCur->aiIdx[pCur->iPage]<pPage->nCell ); assert( pCur->aiIdx[pCur->iPage]<pPage->nCell );
assert( cursorHoldsMutex(pCur) ); assert( cursorHoldsMutex(pCur) );
assert( eOp!=2 || offset==0 ); /* Always start from beginning for eOp==2 */
getCellInfo(pCur); getCellInfo(pCur);
aPayload = pCur->info.pCell + pCur->info.nHeader; aPayload = pCur->info.pCell + pCur->info.nHeader;
nKey = (pPage->intKey ? 0 : (int)pCur->info.nKey); nKey = (pPage->intKey ? 0 : (int)pCur->info.nKey);
#ifdef SQLITE_DIRECT_OVERFLOW_READ
bEnd = (offset+amt==nKey+pCur->info.nData);
#endif
if( NEVER(offset+amt > nKey+pCur->info.nData) if( NEVER(offset+amt > nKey+pCur->info.nData)
|| &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize] || &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize]
@@ -3991,7 +3996,7 @@ static int accessPayload(
if( a+offset>pCur->info.nLocal ){ if( a+offset>pCur->info.nLocal ){
a = pCur->info.nLocal - offset; a = pCur->info.nLocal - offset;
} }
rc = copyPayload(&aPayload[offset], pBuf, a, eOp, pPage->pDbPage); rc = copyPayload(&aPayload[offset], pBuf, a, (eOp & 0x01), pPage->pDbPage);
offset = 0; offset = 0;
pBuf += a; pBuf += a;
amt -= a; amt -= a;
@@ -4005,21 +4010,30 @@ static int accessPayload(
nextPage = get4byte(&aPayload[pCur->info.nLocal]); nextPage = get4byte(&aPayload[pCur->info.nLocal]);
#ifndef SQLITE_OMIT_INCRBLOB /* If the BtCursor.aOverflow[] has not been allocated, allocate it now.
/* If the isIncrblobHandle flag is set and the BtCursor.aOverflow[] ** Except, do not allocate aOverflow[] for eOp==2.
** has not been allocated, allocate it now. The array is sized at **
** one entry for each overflow page in the overflow chain. The ** The aOverflow[] array is sized at one entry for each overflow page
** page number of the first overflow page is stored in aOverflow[0], ** in the overflow chain. The page number of the first overflow page is
** etc. A value of 0 in the aOverflow[] array means "not yet known" ** stored in aOverflow[0], etc. A value of 0 in the aOverflow[] array
** (the cache is lazily populated). ** means "not yet known" (the cache is lazily populated).
*/ */
if( pCur->isIncrblobHandle && !pCur->aOverflow ){ if( eOp!=2 && (pCur->curFlags & BTCF_ValidOvfl)==0 ){
int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize; int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize;
pCur->aOverflow = (Pgno *)sqlite3MallocZero(sizeof(Pgno)*nOvfl); if( nOvfl>pCur->nOvflAlloc ){
/* nOvfl is always positive. If it were zero, fetchPayload would have Pgno *aNew = (Pgno*)sqlite3DbRealloc(
** been used instead of this routine. */ pCur->pBtree->db, pCur->aOverflow, nOvfl*2*sizeof(Pgno)
if( ALWAYS(nOvfl) && !pCur->aOverflow ){ );
if( aNew==0 ){
rc = SQLITE_NOMEM; rc = SQLITE_NOMEM;
}else{
pCur->nOvflAlloc = nOvfl*2;
pCur->aOverflow = aNew;
}
}
if( rc==SQLITE_OK ){
memset(pCur->aOverflow, 0, nOvfl*sizeof(Pgno));
pCur->curFlags |= BTCF_ValidOvfl;
} }
} }
@@ -4027,22 +4041,19 @@ static int accessPayload(
** entry for the first required overflow page is valid, skip ** entry for the first required overflow page is valid, skip
** directly to it. ** directly to it.
*/ */
if( pCur->aOverflow && pCur->aOverflow[offset/ovflSize] ){ if( (pCur->curFlags & BTCF_ValidOvfl)!=0 && pCur->aOverflow[offset/ovflSize] ){
iIdx = (offset/ovflSize); iIdx = (offset/ovflSize);
nextPage = pCur->aOverflow[iIdx]; nextPage = pCur->aOverflow[iIdx];
offset = (offset%ovflSize); offset = (offset%ovflSize);
} }
#endif
for( ; rc==SQLITE_OK && amt>0 && nextPage; iIdx++){ for( ; rc==SQLITE_OK && amt>0 && nextPage; iIdx++){
#ifndef SQLITE_OMIT_INCRBLOB
/* If required, populate the overflow page-list cache. */ /* If required, populate the overflow page-list cache. */
if( pCur->aOverflow ){ if( (pCur->curFlags & BTCF_ValidOvfl)!=0 ){
assert(!pCur->aOverflow[iIdx] || pCur->aOverflow[iIdx]==nextPage); assert(!pCur->aOverflow[iIdx] || pCur->aOverflow[iIdx]==nextPage);
pCur->aOverflow[iIdx] = nextPage; pCur->aOverflow[iIdx] = nextPage;
} }
#endif
if( offset>=ovflSize ){ if( offset>=ovflSize ){
/* The only reason to read this page is to obtain the page /* The only reason to read this page is to obtain the page
@@ -4050,13 +4061,17 @@ static int accessPayload(
** data is not required. So first try to lookup the overflow ** data is not required. So first try to lookup the overflow
** page-list cache, if any, then fall back to the getOverflowPage() ** page-list cache, if any, then fall back to the getOverflowPage()
** function. ** function.
**
** Note that the aOverflow[] array must be allocated because eOp!=2
** here. If eOp==2, then offset==0 and this branch is never taken.
*/ */
#ifndef SQLITE_OMIT_INCRBLOB assert( eOp!=2 );
if( pCur->aOverflow && pCur->aOverflow[iIdx+1] ){ assert( pCur->curFlags & BTCF_ValidOvfl );
if( pCur->aOverflow[iIdx+1] ){
nextPage = pCur->aOverflow[iIdx+1]; nextPage = pCur->aOverflow[iIdx+1];
} else }else{
#endif
rc = getOverflowPage(pBt, nextPage, 0, &nextPage); rc = getOverflowPage(pBt, nextPage, 0, &nextPage);
}
offset -= ovflSize; offset -= ovflSize;
}else{ }else{
/* Need to read this page properly. It contains some of the /* Need to read this page properly. It contains some of the
@@ -4078,13 +4093,15 @@ static int accessPayload(
** 3) the database is file-backed, and ** 3) the database is file-backed, and
** 4) there is no open write-transaction, and ** 4) there is no open write-transaction, and
** 5) the database is not a WAL database, ** 5) the database is not a WAL database,
** 6) all data from the page is being read.
** **
** then data can be read directly from the database file into the ** then data can be read directly from the database file into the
** output buffer, bypassing the page-cache altogether. This speeds ** output buffer, bypassing the page-cache altogether. This speeds
** up loading large records that span many overflow pages. ** up loading large records that span many overflow pages.
*/ */
if( eOp==0 /* (1) */ if( (eOp&0x01)==0 /* (1) */
&& offset==0 /* (2) */ && offset==0 /* (2) */
&& (bEnd || a==ovflSize) /* (6) */
&& pBt->inTransaction==TRANS_READ /* (4) */ && pBt->inTransaction==TRANS_READ /* (4) */
&& (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (3) */ && (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (3) */
&& pBt->pPage1->aData[19]==0x01 /* (5) */ && pBt->pPage1->aData[19]==0x01 /* (5) */
@@ -4101,12 +4118,12 @@ static int accessPayload(
{ {
DbPage *pDbPage; DbPage *pDbPage;
rc = sqlite3PagerAcquire(pBt->pPager, nextPage, &pDbPage, rc = sqlite3PagerAcquire(pBt->pPager, nextPage, &pDbPage,
(eOp==0 ? PAGER_GET_READONLY : 0) ((eOp&0x01)==0 ? PAGER_GET_READONLY : 0)
); );
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
aPayload = sqlite3PagerGetData(pDbPage); aPayload = sqlite3PagerGetData(pDbPage);
nextPage = get4byte(aPayload); nextPage = get4byte(aPayload);
rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage); rc = copyPayload(&aPayload[offset+4], pBuf, a, (eOp&0x01), pDbPage);
sqlite3PagerUnref(pDbPage); sqlite3PagerUnref(pDbPage);
offset = 0; offset = 0;
} }
@@ -4257,14 +4274,14 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
return SQLITE_CORRUPT_BKPT; return SQLITE_CORRUPT_BKPT;
} }
rc = getAndInitPage(pBt, newPgno, &pNewPage, rc = getAndInitPage(pBt, newPgno, &pNewPage,
pCur->wrFlag==0 ? PAGER_GET_READONLY : 0); (pCur->curFlags & BTCF_WriteFlag)==0 ? PAGER_GET_READONLY : 0);
if( rc ) return rc; if( rc ) return rc;
pCur->apPage[i+1] = pNewPage; pCur->apPage[i+1] = pNewPage;
pCur->aiIdx[i+1] = 0; pCur->aiIdx[i+1] = 0;
pCur->iPage++; pCur->iPage++;
pCur->info.nSize = 0; pCur->info.nSize = 0;
pCur->validNKey = 0; pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
if( pNewPage->nCell<1 || pNewPage->intKey!=pCur->apPage[i]->intKey ){ if( pNewPage->nCell<1 || pNewPage->intKey!=pCur->apPage[i]->intKey ){
return SQLITE_CORRUPT_BKPT; return SQLITE_CORRUPT_BKPT;
} }
@@ -4322,7 +4339,7 @@ static void moveToParent(BtCursor *pCur){
releasePage(pCur->apPage[pCur->iPage]); releasePage(pCur->apPage[pCur->iPage]);
pCur->iPage--; pCur->iPage--;
pCur->info.nSize = 0; pCur->info.nSize = 0;
pCur->validNKey = 0; pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
} }
/* /*
@@ -4369,7 +4386,7 @@ static int moveToRoot(BtCursor *pCur){
return SQLITE_OK; return SQLITE_OK;
}else{ }else{
rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->apPage[0], rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->apPage[0],
pCur->wrFlag==0 ? PAGER_GET_READONLY : 0); (pCur->curFlags & BTCF_WriteFlag)==0 ? PAGER_GET_READONLY : 0);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
pCur->eState = CURSOR_INVALID; pCur->eState = CURSOR_INVALID;
return rc; return rc;
@@ -4396,8 +4413,7 @@ static int moveToRoot(BtCursor *pCur){
pCur->aiIdx[0] = 0; pCur->aiIdx[0] = 0;
pCur->info.nSize = 0; pCur->info.nSize = 0;
pCur->atLast = 0; pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidNKey|BTCF_ValidOvfl);
pCur->validNKey = 0;
if( pRoot->nCell>0 ){ if( pRoot->nCell>0 ){
pCur->eState = CURSOR_VALID; pCur->eState = CURSOR_VALID;
@@ -4460,7 +4476,7 @@ static int moveToRightmost(BtCursor *pCur){
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
pCur->aiIdx[pCur->iPage] = pPage->nCell-1; pCur->aiIdx[pCur->iPage] = pPage->nCell-1;
pCur->info.nSize = 0; pCur->info.nSize = 0;
pCur->validNKey = 0; pCur->curFlags &= ~BTCF_ValidNKey;
} }
return rc; return rc;
} }
@@ -4499,7 +4515,7 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
/* If the cursor already points to the last entry, this is a no-op. */ /* If the cursor already points to the last entry, this is a no-op. */
if( CURSOR_VALID==pCur->eState && pCur->atLast ){ if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){
#ifdef SQLITE_DEBUG #ifdef SQLITE_DEBUG
/* This block serves to assert() that the cursor really does point /* This block serves to assert() that the cursor really does point
** to the last entry in the b-tree. */ ** to the last entry in the b-tree. */
@@ -4522,7 +4538,12 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
assert( pCur->eState==CURSOR_VALID ); assert( pCur->eState==CURSOR_VALID );
*pRes = 0; *pRes = 0;
rc = moveToRightmost(pCur); rc = moveToRightmost(pCur);
pCur->atLast = rc==SQLITE_OK ?1:0; if( rc==SQLITE_OK ){
pCur->curFlags |= BTCF_AtLast;
}else{
pCur->curFlags &= ~BTCF_AtLast;
}
} }
} }
return rc; return rc;
@@ -4573,14 +4594,14 @@ int sqlite3BtreeMovetoUnpacked(
/* If the cursor is already positioned at the point we are trying /* If the cursor is already positioned at the point we are trying
** to move to, then just return without doing any work */ ** to move to, then just return without doing any work */
if( pCur->eState==CURSOR_VALID && pCur->validNKey if( pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0
&& pCur->apPage[0]->intKey && pCur->apPage[0]->intKey
){ ){
if( pCur->info.nKey==intKey ){ if( pCur->info.nKey==intKey ){
*pRes = 0; *pRes = 0;
return SQLITE_OK; return SQLITE_OK;
} }
if( pCur->atLast && pCur->info.nKey<intKey ){ if( (pCur->curFlags & BTCF_AtLast)!=0 && pCur->info.nKey<intKey ){
*pRes = -1; *pRes = -1;
return SQLITE_OK; return SQLITE_OK;
} }
@@ -4647,7 +4668,7 @@ int sqlite3BtreeMovetoUnpacked(
if( lwr>upr ){ c = +1; break; } if( lwr>upr ){ c = +1; break; }
}else{ }else{
assert( nCellKey==intKey ); assert( nCellKey==intKey );
pCur->validNKey = 1; pCur->curFlags |= BTCF_ValidNKey;
pCur->info.nKey = nCellKey; pCur->info.nKey = nCellKey;
pCur->aiIdx[pCur->iPage] = (u16)idx; pCur->aiIdx[pCur->iPage] = (u16)idx;
if( !pPage->leaf ){ if( !pPage->leaf ){
@@ -4704,7 +4725,7 @@ int sqlite3BtreeMovetoUnpacked(
goto moveto_finish; goto moveto_finish;
} }
pCur->aiIdx[pCur->iPage] = (u16)idx; pCur->aiIdx[pCur->iPage] = (u16)idx;
rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0); rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 2);
if( rc ){ if( rc ){
sqlite3_free(pCellKey); sqlite3_free(pCellKey);
goto moveto_finish; goto moveto_finish;
@@ -4751,7 +4772,7 @@ moveto_next_layer:
} }
moveto_finish: moveto_finish:
pCur->info.nSize = 0; pCur->info.nSize = 0;
pCur->validNKey = 0; pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
return rc; return rc;
} }
@@ -4796,6 +4817,7 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
assert( *pRes==0 || *pRes==1 ); assert( *pRes==0 || *pRes==1 );
assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
if( pCur->eState!=CURSOR_VALID ){ if( pCur->eState!=CURSOR_VALID ){
invalidateOverflowCache(pCur);
rc = restoreCursorPosition(pCur); rc = restoreCursorPosition(pCur);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
*pRes = 0; *pRes = 0;
@@ -4829,7 +4851,7 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
testcase( idx>pPage->nCell ); testcase( idx>pPage->nCell );
pCur->info.nSize = 0; pCur->info.nSize = 0;
pCur->validNKey = 0; pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
if( idx>=pPage->nCell ){ if( idx>=pPage->nCell ){
if( !pPage->leaf ){ if( !pPage->leaf ){
rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8])); rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8]));
@@ -4890,7 +4912,7 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
assert( pRes!=0 ); assert( pRes!=0 );
assert( *pRes==0 || *pRes==1 ); assert( *pRes==0 || *pRes==1 );
assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
pCur->atLast = 0; pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidOvfl);
if( pCur->eState!=CURSOR_VALID ){ if( pCur->eState!=CURSOR_VALID ){
if( ALWAYS(pCur->eState>=CURSOR_REQUIRESEEK) ){ if( ALWAYS(pCur->eState>=CURSOR_REQUIRESEEK) ){
rc = btreeRestoreCursorPosition(pCur); rc = btreeRestoreCursorPosition(pCur);
@@ -4935,7 +4957,7 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
moveToParent(pCur); moveToParent(pCur);
} }
pCur->info.nSize = 0; pCur->info.nSize = 0;
pCur->validNKey = 0; pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
pCur->aiIdx[pCur->iPage]--; pCur->aiIdx[pCur->iPage]--;
pPage = pCur->apPage[pCur->iPage]; pPage = pCur->apPage[pCur->iPage];
@@ -6960,7 +6982,7 @@ int sqlite3BtreeInsert(
} }
assert( cursorHoldsMutex(pCur) ); assert( cursorHoldsMutex(pCur) );
assert( pCur->wrFlag && pBt->inTransaction==TRANS_WRITE assert( (pCur->curFlags & BTCF_WriteFlag)!=0 && pBt->inTransaction==TRANS_WRITE
&& (pBt->btsFlags & BTS_READ_ONLY)==0 ); && (pBt->btsFlags & BTS_READ_ONLY)==0 );
assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
@@ -6993,7 +7015,7 @@ int sqlite3BtreeInsert(
/* If the cursor is currently on the last row and we are appending a /* If the cursor is currently on the last row and we are appending a
** new row onto the end, set the "loc" to avoid an unnecessary btreeMoveto() ** new row onto the end, set the "loc" to avoid an unnecessary btreeMoveto()
** call */ ** call */
if( pCur->validNKey && nKey>0 && pCur->info.nKey==nKey-1 ){ if( (pCur->curFlags&BTCF_ValidNKey)!=0 && nKey>0 && pCur->info.nKey==nKey-1 ){
loc = -1; loc = -1;
} }
} }
@@ -7046,7 +7068,7 @@ int sqlite3BtreeInsert(
/* If no error has occurred and pPage has an overflow cell, call balance() /* If no error has occurred and pPage has an overflow cell, call balance()
** to redistribute the cells within the tree. Since balance() may move ** to redistribute the cells within the tree. Since balance() may move
** the cursor, zero the BtCursor.info.nSize and BtCursor.validNKey ** the cursor, zero the BtCursor.info.nSize and BTCF_ValidNKey
** variables. ** variables.
** **
** Previous versions of SQLite called moveToRoot() to move the cursor ** Previous versions of SQLite called moveToRoot() to move the cursor
@@ -7066,7 +7088,7 @@ int sqlite3BtreeInsert(
*/ */
pCur->info.nSize = 0; pCur->info.nSize = 0;
if( rc==SQLITE_OK && pPage->nOverflow ){ if( rc==SQLITE_OK && pPage->nOverflow ){
pCur->validNKey = 0; pCur->curFlags &= ~(BTCF_ValidNKey);
rc = balance(pCur); rc = balance(pCur);
/* Must make sure nOverflow is reset to zero even if the balance() /* Must make sure nOverflow is reset to zero even if the balance()
@@ -7098,7 +7120,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
assert( cursorHoldsMutex(pCur) ); assert( cursorHoldsMutex(pCur) );
assert( pBt->inTransaction==TRANS_WRITE ); assert( pBt->inTransaction==TRANS_WRITE );
assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); assert( (pBt->btsFlags & BTS_READ_ONLY)==0 );
assert( pCur->wrFlag ); assert( pCur->curFlags & BTCF_WriteFlag );
assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
assert( !hasReadConflicts(p, pCur->pgnoRoot) ); assert( !hasReadConflicts(p, pCur->pgnoRoot) );
@@ -8411,7 +8433,7 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
int rc; int rc;
assert( cursorHoldsMutex(pCsr) ); assert( cursorHoldsMutex(pCsr) );
assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) ); assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) );
assert( pCsr->isIncrblobHandle ); assert( pCsr->curFlags & BTCF_Incrblob );
rc = restoreCursorPosition(pCsr); rc = restoreCursorPosition(pCsr);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
@@ -8440,7 +8462,7 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
** (d) there are no conflicting read-locks, and ** (d) there are no conflicting read-locks, and
** (e) the cursor points at a valid row of an intKey table. ** (e) the cursor points at a valid row of an intKey table.
*/ */
if( !pCsr->wrFlag ){ if( (pCsr->curFlags & BTCF_WriteFlag)==0 ){
return SQLITE_READONLY; return SQLITE_READONLY;
} }
assert( (pCsr->pBt->btsFlags & BTS_READ_ONLY)==0 assert( (pCsr->pBt->btsFlags & BTS_READ_ONLY)==0
@@ -8453,20 +8475,10 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
} }
/* /*
** Set a flag on this cursor to cache the locations of pages from the ** Mark this cursor as an incremental blob cursor.
** overflow list for the current row. This is used by cursors opened
** for incremental blob IO only.
**
** This function sets a flag only. The actual page location cache
** (stored in BtCursor.aOverflow[]) is allocated and used by function
** accessPayload() (the worker function for sqlite3BtreeData() and
** sqlite3BtreePutData()).
*/ */
void sqlite3BtreeCacheOverflow(BtCursor *pCur){ void sqlite3BtreeIncrblobCursor(BtCursor *pCur){
assert( cursorHoldsMutex(pCur) ); pCur->curFlags |= BTCF_Incrblob;
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
invalidateOverflowCache(pCur);
pCur->isIncrblobHandle = 1;
} }
#endif #endif

View File

@@ -190,7 +190,7 @@ char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*);
struct Pager *sqlite3BtreePager(Btree*); struct Pager *sqlite3BtreePager(Btree*);
int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*); int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*);
void sqlite3BtreeCacheOverflow(BtCursor *); void sqlite3BtreeIncrblobCursor(BtCursor *);
void sqlite3BtreeClearCursor(BtCursor *); void sqlite3BtreeClearCursor(BtCursor *);
int sqlite3BtreeSetVersion(Btree *pBt, int iVersion); int sqlite3BtreeSetVersion(Btree *pBt, int iVersion);
void sqlite3BtreeCursorHints(BtCursor *, unsigned int mask); void sqlite3BtreeCursorHints(BtCursor *, unsigned int mask);

View File

@@ -496,27 +496,30 @@ struct BtCursor {
BtShared *pBt; /* The BtShared this cursor points to */ BtShared *pBt; /* The BtShared this cursor points to */
BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */ BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */
struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */ struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */
#ifndef SQLITE_OMIT_INCRBLOB
Pgno *aOverflow; /* Cache of overflow page locations */ Pgno *aOverflow; /* Cache of overflow page locations */
#endif
Pgno pgnoRoot; /* The root page of this tree */
CellInfo info; /* A parse of the cell we are pointing at */ CellInfo info; /* A parse of the cell we are pointing at */
i64 nKey; /* Size of pKey, or last integer key */ i64 nKey; /* Size of pKey, or last integer key */
void *pKey; /* Saved key that was cursor's last known position */ void *pKey; /* Saved key that was cursor last known position */
Pgno pgnoRoot; /* The root page of this tree */
int nOvflAlloc; /* Allocated size of aOverflow[] array */
int skipNext; /* Prev() is noop if negative. Next() is noop if positive */ int skipNext; /* Prev() is noop if negative. Next() is noop if positive */
u8 wrFlag; /* True if writable */ u8 curFlags; /* zero or more BTCF_* flags defined below */
u8 atLast; /* Cursor pointing to the last entry */
u8 validNKey; /* True if info.nKey is valid */
u8 eState; /* One of the CURSOR_XXX constants (see below) */ u8 eState; /* One of the CURSOR_XXX constants (see below) */
#ifndef SQLITE_OMIT_INCRBLOB
u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */
#endif
u8 hints; /* As configured by CursorSetHints() */ u8 hints; /* As configured by CursorSetHints() */
i16 iPage; /* Index of current page in apPage */ i16 iPage; /* Index of current page in apPage */
u16 aiIdx[BTCURSOR_MAX_DEPTH]; /* Current index in apPage[i] */ u16 aiIdx[BTCURSOR_MAX_DEPTH]; /* Current index in apPage[i] */
MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */ MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */
}; };
/*
** Legal values for BtCursor.curFlags
*/
#define BTCF_WriteFlag 0x01 /* True if a write cursor */
#define BTCF_ValidNKey 0x02 /* True if info.nKey is valid */
#define BTCF_ValidOvfl 0x04 /* True if aOverflow is valid */
#define BTCF_AtLast 0x08 /* Cursor is pointing ot the last entry */
#define BTCF_Incrblob 0x10 /* True if an incremental I/O handle */
/* /*
** Potential values for BtCursor.eState. ** Potential values for BtCursor.eState.
** **

View File

@@ -51,7 +51,7 @@ void sqlite3BtreeCursorList(Btree *p){
BtShared *pBt = p->pBt; BtShared *pBt = p->pBt;
for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
MemPage *pPage = pCur->apPage[pCur->iPage]; MemPage *pPage = pCur->apPage[pCur->iPage];
char *zMode = pCur->wrFlag ? "rw" : "ro"; char *zMode = (pCur->curFlags & BTCF_WriteFlag) ? "rw" : "ro";
sqlite3DebugPrintf("CURSOR %p rooted at %4d(%s) currently at %d.%d%s\n", sqlite3DebugPrintf("CURSOR %p rooted at %4d(%s) currently at %d.%d%s\n",
pCur, pCur->pgnoRoot, zMode, pCur, pCur->pgnoRoot, zMode,
pPage ? pPage->pgno : 0, pCur->aiIdx[pCur->iPage], pPage ? pPage->pgno : 0, pCur->aiIdx[pCur->iPage],

View File

@@ -77,9 +77,7 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){
p->iOffset = pC->aType[p->iCol + pC->nField]; p->iOffset = pC->aType[p->iCol + pC->nField];
p->nByte = sqlite3VdbeSerialTypeLen(type); p->nByte = sqlite3VdbeSerialTypeLen(type);
p->pCsr = pC->pCursor; p->pCsr = pC->pCursor;
sqlite3BtreeEnterCursor(p->pCsr); sqlite3BtreeIncrblobCursor(p->pCsr);
sqlite3BtreeCacheOverflow(p->pCsr);
sqlite3BtreeLeaveCursor(p->pCsr);
} }
} }

View File

@@ -1301,12 +1301,14 @@ do_test func-29.3 {
db eval {SELECT typeof(+x) FROM t29 ORDER BY id} db eval {SELECT typeof(+x) FROM t29 ORDER BY id}
} {integer null real blob text} } {integer null real blob text}
if {[permutation] != "mmap"} { if {[permutation] != "mmap"} {
ifcapable !direct_read {
do_test func-29.4 { do_test func-29.4 {
set x [lindex [sqlite3_db_status db CACHE_MISS 1] 1] set x [lindex [sqlite3_db_status db CACHE_MISS 1] 1]
if {$x>100} {set x many} if {$x>100} {set x many}
set x set x
} {many} } {many}
} }
}
do_test func-29.5 { do_test func-29.5 {
db close db close
sqlite3 db test.db sqlite3 db test.db