Merge recent trunk changes (performance enhancements) into the sessions

branch.

FossilOrigin-Name: 497367cb57345dd37793e5f369b34d12be56172e
This commit is contained in:
drh 2014-09-27 19:51:50 +00:00
commit 6382b6dc71
21 changed files with 783 additions and 376 deletions

View File

@ -1,5 +1,5 @@
C Fix\sa\ssegfault\sin\sthe\ssessions\smodule\sthat\scould\sfollow\san\sOOM.
D 2014-09-27T18:18:32.969
C Merge\srecent\strunk\schanges\s(performance\senhancements)\sinto\sthe\ssessions\nbranch.
D 2014-09-27T19:51:50.438
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in dd5f245aa8c741bc65845747203c8ce2f3fb6c83
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@ -188,19 +188,19 @@ F src/auth.c d8abcde53426275dab6243b441256fcd8ccbebb2
F src/backup.c a31809c65623cc41849b94d368917f8bb66e6a7e
F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb
F src/btmutex.c 49ca66250c7dfa844a4d4cb8272b87420d27d3a5
F src/btree.c 6aa61c0e3d20d1d1acc8fb33d8f0ebd675305d3c
F src/btree.c 59f03e421dad3cb6e27cc7d2393d3a7459be4b5e
F src/btree.h a79aa6a71e7f1055f01052b7f821bd1c2dce95c8
F src/btreeInt.h e0ecb5dba292722039a7540beb3fc448103273cc
F src/build.c 8dbca25988045fbf2a33c9631c42706fa6449e60
F src/btreeInt.h 1bd7957161a1346a914f1f09231610e777a8e58d
F src/build.c bde83dd5cf812e310a7e5ad2846790a14745bef4
F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0
F src/complete.c 535183afb3c75628b78ce82612931ac7cdf26f14
F src/ctime.c 16cd19215d9fd849ee2b7509b092f2e0bbd6a958
F src/ctime.c bb434068b5308a857b181c2d204a320ff0d6c638
F src/date.c 57a7f9ba9f6b4d5268f5e411739066a611f99036
F src/delete.c de3d07d6602b90ae6e8bdebeb7b3265bb846377f
F src/expr.c 4f101c8ddc6d5a22303c88278069f5261562a9a8
F src/expr.c f32119248996680aa73c5c37bfdd42820804dc17
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c da985ae673efef2c712caef825a5d2edb087ead7
F src/func.c 1629ccdd8ef3f19d7accc9d9287190489469ff81
F src/func.c ba47c1671ab3cfdafa6e9d6ee490939ea578adee
F src/global.c 5110fa12e09729b84eee0191c984ec4008e21937
F src/hash.c 4263fbc955f26c2e8cdc0cf214bc42435aa4e4f5
F src/hash.h c8f3c31722cf3277d03713909761e152a5b81094
@ -228,7 +228,7 @@ F src/os.c 1b147e4cf7cc39e618115c14a086aed44bc91ace
F src/os.h 3e57a24e2794a94d3cf2342c6d9a884888cd96bf
F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa
F src/os_unix.c 9096a1b1449182e67e759f59994eee04113bc587
F src/os_unix.c fb587121840f690101336879adfa6d0b2cd0e8c7
F src/os_win.c 0a4042ef35f322e86fa01f6c8884c5e645b911e7
F src/os_win.h 09e751b20bbc107ffbd46e13555dc73576d88e21
F src/pager.c caab007743821d96752597c9cfd7351654697b06
@ -248,7 +248,7 @@ F src/shell.c 85aae71dcc9bd6df28047b95ab631eb0ac91401f
F src/sqlite.h.in 1af072be5ed8902c8c12b5b105487d0efedd00b4
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
F src/sqlite3ext.h 17d487c3c91b0b8c584a32fbeb393f6f795eea7d
F src/sqliteInt.h 35f074ded974804602e3ed89576a74c9b7255c93
F src/sqliteInt.h 219f6e9323f7fdddaec9f442d9610cce356c3ad7
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
F src/table.c 2e99ef7ef16187e17033d9398dc962ce22dab5cb
@ -278,7 +278,7 @@ F src/test_intarray.h 9dc57417fb65bc7835cc18548852cc08cc062202
F src/test_journal.c f5c0a05b7b3d5930db769b5ee6c3766dc2221a64
F src/test_loadext.c a5251f956ab6af21e138dc1f9c0399394a510cb4
F src/test_malloc.c ba34143f941a9d74b30bbffc8818389bb73a1ca2
F src/test_multiplex.c ca90057438b63bf0840ebb84d0ef050624519a76
F src/test_multiplex.c caadb62cc777268b4f8fb94d5b27b80156c8f7c0
F src/test_multiplex.h c08e4e8f8651f0c5e0509b138ff4d5b43ed1f5d3
F src/test_mutex.c 293042d623ebba969160f471a82aa1551626454f
F src/test_onefile.c 0396f220561f3b4eedc450cef26d40c593c69a25
@ -299,15 +299,15 @@ F src/test_vfs.c f84075a388527892ff184988f43b69ce69b8083c
F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 22dded4283dc4b25422f6444cdcb8d6b1ea0b5ff
F src/tokenize.c 3df63041994f55afeb168b463ec836e8f1c50e7c
F src/tokenize.c cc9016e5007fc5e76789079616d2f26741bcc689
F src/trigger.c 25571661fdeae8c7f975ff40ffec205520a3f92f
F src/update.c b9e5295d3a78e96b7c2978c4f9d224d06880f031
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
F src/util.c 4006c01772bd8d8ac4306d523bbcee41d3e392d8
F src/vacuum.c 59f03f92bcff57faa6a8ca256eb29ccddfb0614a
F src/vdbe.c 5e6d4ef36cfff2bacb4d11eccc99bd55c76692f5
F src/vdbe.c a96171a2d025cf3dfcea4f0d94232f4bfa192f34
F src/vdbe.h d61daeffed696e21630759de9e135ee298ad9573
F src/vdbeInt.h 0e6e8d18199cef7dd5e9fa5de8490f60806259f0
F src/vdbeInt.h 7d15127815d68e683d6a31bb4f7a52baa9cd67e5
F src/vdbeapi.c cdded67e36d2a20f6d1c7d56f008a646557d2bf0
F src/vdbeaux.c 07b0045d0f34d0ad70c1c42ff75246a7e64e4e87
F src/vdbeblob.c d65b01f439df63911ac3d7a9a85c15503965f2c3
@ -318,7 +318,7 @@ F src/vtab.c 019dbfd0406a7447c990e1f7bd1dfcdb8895697f
F src/wal.c 10e7de7ce90865a68153f001a61f1d985cd17983
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804
F src/where.c 0888567c0e01a41b6001647e333f8ccfd3ae7d36
F src/where.c a14d3d8042adeb51f81731c1b47b3e481d1cc23a
F src/whereInt.h 124d970450955a6982e174b07c320ae6d62a595c
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
@ -357,7 +357,7 @@ F test/auth.test 855233ef26eb3601b6886567ea4e326c72959360
F test/auth2.test 264c6af53cad9aba5218c68bbe18036e39007bfa
F test/auth3.test 5cfa94ed90c6617c42b7ba4b133fd79678b251c7
F test/autoinc.test c58912526998a39e11f66b533e23cfabea7f25b7
F test/autoindex1.test 762ff3f8e25d852aae55c6462ca166a80c0cde61
F test/autoindex1.test 6ff78b94f43a59616c06c11c55b12935173506d7
F test/autoindex2.test 60d2fc6f38364308ce73a9beb01b47ded38697de
F test/autoindex3.test 8254f689c3241081fad52b7bea18ba53e07e14a2
F test/autovacuum.test 941892505d2c0f410a0cb5970dfa1c7c4e5f6e74
@ -454,7 +454,7 @@ F test/ctime.test 7bd009071e242aac4f18521581536b652b789a47
F test/date.test 42973251b9429f2c41b77eb98a7b0b0ba2d3b2c0
F test/dbstatus.test 8de104bb5606f19537d23cd553b41349b5ab1204
F test/dbstatus2.test 10418e62b3db5dca070f0c3eef3ea13946f339c2
F test/default.test 792c3c70836f1901e2a8cb34fa0880ed71e2c1a9
F test/default.test 0cb49b1c315a0d81c81d775e407f66906a2a604d
F test/delete.test a065b05d2ebf60fd16639c579a4adfb7c381c701
F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa
F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab
@ -745,6 +745,7 @@ F test/mmapfault.test d4c9eff9cd8c2dc14bc43e71e042f175b0a26fe3
F test/multiplex.test efd015ca0b5b4a57dc9535b8feb1273eebeadb60
F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a
F test/multiplex3.test d228f59eac91839a977eac19f21d053f03e4d101
F test/multiplex4.test d3e8a5a522c51cbf3ed1c5b0bd496be02c29d7b1
F test/mutex1.test 78b2b9bb320e51d156c4efdb71b99b051e7a4b41
F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
F test/nan.test e9648b9d007c7045242af35e11a984d4b169443a
@ -799,7 +800,7 @@ F test/releasetest.tcl a0df0dfc5e3ee83ade87b9cc96db41b52d590b9e
F test/resolver01.test 33abf37ff8335e6bf98f2b45a0af3e06996ccd9a
F test/rollback.test e9504a009a202c3ed711da2e6879ff60c5a4669c
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
F test/rowid.test b78b30afb9537a73788ca1233a23a32190a3bb1f
F test/rowid.test 742b5741584a8a44fd83e856cc2896688401d645
F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798
F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09
F test/savepoint.test 51d3900dc071a7c2ad4248578a5925631b476313
@ -853,10 +854,10 @@ F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3
F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5
F test/shrink.test 8c70f62b6e8eb4d54533de6d65bd06b1b9a17868
F test/sidedelete.test f0ad71abe6233e3b153100f3b8d679b19a488329
F test/skipscan1.test 28c7faa41a0d7265040ecb0a0abd90c0904270b2
F test/skipscan1.test 7e15e1cc524524e7b2c4595ec85c75501d22f4ff
F test/skipscan2.test d1d1450952b7275f0b0a3a981f0230532743951a
F test/skipscan3.test ec5bab3f81c7038b43450e7b3062e04a198bdbb5
F test/skipscan5.test d8b9692b702745a0e41c23f9da6beac81df01196
F test/skipscan5.test 67817a4b6857c47e0e33ba3e506da6f23ef68de2
F test/soak.test 0b5b6375c9f4110c828070b826b3b4b0bb65cd5f
F test/softheap1.test 40562fe6cac6d9827b7b42b86d45aedf12c15e24
F test/sort.test 15e1d3014abc3f6d4357ed81b93b82117aefd235
@ -1216,7 +1217,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P d2642543eed54da1ac0f757d43dd4d72482eb752
R 7328e4de54076fa7989cf4f3b00a08d8
U dan
Z 371024a806f6988dfc33105901bfbbae
P 09985fa6b60a0bf38e23bbccd4d8e1d1cbf66124 d026f0c944ce812732d3595eaa3c5d432a86c7dd
R 6b80dc3719dfaf6510fd0fc97f44558f
U drh
Z 29718882bdf5b938c85c12503154fa25

View File

@ -1 +1 @@
09985fa6b60a0bf38e23bbccd4d8e1d1cbf66124
497367cb57345dd37793e5f369b34d12be56172e

View File

@ -487,7 +487,9 @@ static void invalidateIncrblobCursors(
BtShared *pBt = pBtree->pBt;
assert( sqlite3BtreeHoldsMutex(pBtree) );
for(p=pBt->pCursor; p; p=p->pNext){
if( (p->curFlags & BTCF_Incrblob)!=0 && (isClearTable || p->info.nKey==iRow) ){
if( (p->curFlags & BTCF_Incrblob)!=0
&& (isClearTable || p->info.nKey==iRow)
){
p->eState = CURSOR_INVALID;
}
}
@ -660,9 +662,9 @@ static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){
** broken out from its caller to avoid unnecessary stack pointer movement.
*/
static int SQLITE_NOINLINE saveCursorsOnList(
BtCursor *p, /* The first cursor that needs saving */
Pgno iRoot, /* Only save cursor with this iRoot. Save all if zero */
BtCursor *pExcept /* Do not save this cursor */
BtCursor *p, /* The first cursor that needs saving */
Pgno iRoot, /* Only save cursor with this iRoot. Save all if zero */
BtCursor *pExcept /* Do not save this cursor */
){
do{
if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) ){
@ -968,47 +970,44 @@ static u8 *findOverflowCell(MemPage *pPage, int iCell){
** are two versions of this function. btreeParseCell() takes a
** cell index as the second argument and btreeParseCellPtr()
** takes a pointer to the body of the cell as its second argument.
**
** Within this file, the parseCell() macro can be called instead of
** btreeParseCellPtr(). Using some compilers, this will be faster.
*/
static void btreeParseCellPtr(
MemPage *pPage, /* Page containing the cell */
u8 *pCell, /* Pointer to the cell text. */
CellInfo *pInfo /* Fill in this structure */
){
u16 n; /* Number bytes in cell content header */
u8 *pIter; /* For scanning through pCell */
u32 nPayload; /* Number of bytes of cell payload */
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
pInfo->pCell = pCell;
assert( pPage->leaf==0 || pPage->leaf==1 );
n = pPage->childPtrSize;
assert( n==4-4*pPage->leaf );
if( pPage->intKey ){
if( pPage->hasData ){
assert( n==0 );
n = getVarint32(pCell, nPayload);
}else{
nPayload = 0;
}
n += getVarint(&pCell[n], (u64*)&pInfo->nKey);
pInfo->nData = nPayload;
if( pPage->intKeyLeaf ){
assert( pPage->childPtrSize==0 );
pIter = pCell + getVarint32(pCell, nPayload);
pIter += getVarint(pIter, (u64*)&pInfo->nKey);
}else if( pPage->noPayload ){
assert( pPage->childPtrSize==4 );
pInfo->nSize = 4 + getVarint(&pCell[4], (u64*)&pInfo->nKey);
pInfo->nPayload = 0;
pInfo->nLocal = 0;
pInfo->iOverflow = 0;
pInfo->pPayload = 0;
return;
}else{
pInfo->nData = 0;
n += getVarint32(&pCell[n], nPayload);
pIter = pCell + pPage->childPtrSize;
pIter += getVarint32(pIter, nPayload);
pInfo->nKey = nPayload;
}
pInfo->nPayload = nPayload;
pInfo->nHeader = n;
pInfo->pPayload = pIter;
testcase( nPayload==pPage->maxLocal );
testcase( nPayload==pPage->maxLocal+1 );
if( likely(nPayload<=pPage->maxLocal) ){
if( nPayload<=pPage->maxLocal ){
/* This is the (easy) common case where the entire payload fits
** on the local page. No overflow is required.
*/
if( (pInfo->nSize = (u16)(n+nPayload))<4 ) pInfo->nSize = 4;
pInfo->nSize = nPayload + (u16)(pIter - pCell);
if( pInfo->nSize<4 ) pInfo->nSize = 4;
pInfo->nLocal = (u16)nPayload;
pInfo->iOverflow = 0;
}else{
@ -1035,18 +1034,16 @@ static void btreeParseCellPtr(
}else{
pInfo->nLocal = (u16)minLocal;
}
pInfo->iOverflow = (u16)(pInfo->nLocal + n);
pInfo->iOverflow = (u16)(&pInfo->pPayload[pInfo->nLocal] - pCell);
pInfo->nSize = pInfo->iOverflow + 4;
}
}
#define parseCell(pPage, iCell, pInfo) \
btreeParseCellPtr((pPage), findCell((pPage), (iCell)), (pInfo))
static void btreeParseCell(
MemPage *pPage, /* Page containing the cell */
int iCell, /* The cell index. First cell is 0 */
CellInfo *pInfo /* Fill in this structure */
){
parseCell(pPage, iCell, pInfo);
btreeParseCellPtr(pPage, findCell(pPage, iCell), pInfo);
}
/*
@ -1056,8 +1053,9 @@ static void btreeParseCell(
** the space used by the cell pointer.
*/
static u16 cellSizePtr(MemPage *pPage, u8 *pCell){
u8 *pIter = &pCell[pPage->childPtrSize];
u32 nSize;
u8 *pIter = pCell + pPage->childPtrSize; /* For looping over bytes of pCell */
u8 *pEnd; /* End mark for a varint */
u32 nSize; /* Size value to return */
#ifdef SQLITE_DEBUG
/* The value returned by this function should always be the same as
@ -1068,26 +1066,34 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){
btreeParseCellPtr(pPage, pCell, &debuginfo);
#endif
if( pPage->noPayload ){
pEnd = &pIter[9];
while( (*pIter++)&0x80 && pIter<pEnd );
assert( pPage->childPtrSize==4 );
return (u16)(pIter - pCell);
}
nSize = *pIter;
if( nSize>=0x80 ){
pEnd = &pIter[9];
nSize &= 0x7f;
do{
nSize = (nSize<<7) | (*++pIter & 0x7f);
}while( *(pIter)>=0x80 && pIter<pEnd );
}
pIter++;
if( pPage->intKey ){
u8 *pEnd;
if( pPage->hasData ){
pIter += getVarint32(pIter, nSize);
}else{
nSize = 0;
}
/* pIter now points at the 64-bit integer key value, a variable length
** integer. The following block moves pIter to point at the first byte
** past the end of the key value. */
pEnd = &pIter[9];
while( (*pIter++)&0x80 && pIter<pEnd );
}else{
pIter += getVarint32(pIter, nSize);
}
testcase( nSize==pPage->maxLocal );
testcase( nSize==pPage->maxLocal+1 );
if( nSize>pPage->maxLocal ){
if( nSize<=pPage->maxLocal ){
nSize += (u32)(pIter - pCell);
if( nSize<4 ) nSize = 4;
}else{
int minLocal = pPage->minLocal;
nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4);
testcase( nSize==pPage->maxLocal );
@ -1095,16 +1101,9 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){
if( nSize>pPage->maxLocal ){
nSize = minLocal;
}
nSize += 4;
nSize += 4 + (u16)(pIter - pCell);
}
nSize += (u32)(pIter - pCell);
/* The minimum size of any cell is 4 bytes. */
if( nSize<4 ){
nSize = 4;
}
assert( nSize==debuginfo.nSize );
assert( nSize==debuginfo.nSize || CORRUPT_DB );
return (u16)nSize;
}
@ -1127,7 +1126,6 @@ static void ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell, int *pRC){
if( *pRC ) return;
assert( pCell!=0 );
btreeParseCellPtr(pPage, pCell, &info);
assert( (info.nData+(pPage->intKey?0:info.nKey))==info.nPayload );
if( info.iOverflow ){
Pgno ovfl = get4byte(&pCell[info.iOverflow]);
ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, pRC);
@ -1340,7 +1338,7 @@ defragment_page:
** routine and return SQLITE_CORRUPT if any problems are found.
*/
static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
u16 iPtr; /* Address of pointer to next freeblock */
u16 iPtr; /* Address of ptr to next freeblock */
u16 iFreeBlk; /* Address of the next freeblock */
u8 hdr; /* Page header size. 0 or 100 */
u8 nFrag = 0; /* Reduction in fragmentation */
@ -1392,9 +1390,9 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
iFreeBlk = get2byte(&data[iFreeBlk]);
}
/* If iPtr is another freeblock (that is, if iPtr is not the freelist pointer
** in the page header) then check to see if iStart should be coalesced
** onto the end of iPtr.
/* If iPtr is another freeblock (that is, if iPtr is not the freelist
** pointer in the page header) then check to see if iStart should be
** coalesced onto the end of iPtr.
*/
if( iPtr>hdr+1 ){
int iPtrEnd = iPtr + get2byte(&data[iPtr+2]);
@ -1448,12 +1446,14 @@ static int decodeFlags(MemPage *pPage, int flagByte){
pBt = pPage->pBt;
if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){
pPage->intKey = 1;
pPage->hasData = pPage->leaf;
pPage->intKeyLeaf = pPage->leaf;
pPage->noPayload = !pPage->leaf;
pPage->maxLocal = pBt->maxLeaf;
pPage->minLocal = pBt->minLeaf;
}else if( flagByte==PTF_ZERODATA ){
pPage->intKey = 0;
pPage->hasData = 0;
pPage->intKeyLeaf = 0;
pPage->noPayload = 0;
pPage->maxLocal = pBt->maxLocal;
pPage->minLocal = pBt->minLocal;
}else{
@ -2627,11 +2627,11 @@ static void unlockBtreeIfUnused(BtShared *pBt){
assert( sqlite3_mutex_held(pBt->mutex) );
assert( countValidCursors(pBt,0)==0 || pBt->inTransaction>TRANS_NONE );
if( pBt->inTransaction==TRANS_NONE && pBt->pPage1!=0 ){
assert( pBt->pPage1->aData );
MemPage *pPage1 = pBt->pPage1;
assert( pPage1->aData );
assert( sqlite3PagerRefcount(pBt->pPager)==1 );
assert( pBt->pPage1->aData );
releasePage(pBt->pPage1);
pBt->pPage1 = 0;
releasePage(pPage1);
}
}
@ -3672,6 +3672,10 @@ static int btreeCursor(
if( NEVER(wrFlag && (pBt->btsFlags & BTS_READ_ONLY)!=0) ){
return SQLITE_READONLY;
}
if( wrFlag ){
allocateTempSpace(pBt);
if( pBt->pTmpSpace==0 ) return SQLITE_NOMEM;
}
if( iTable==1 && btreePagecount(pBt)==0 ){
assert( wrFlag==0 );
iTable = 0;
@ -3861,8 +3865,9 @@ int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
assert( cursorHoldsMutex(pCur) );
assert( pCur->eState==CURSOR_VALID );
assert( pCur->apPage[pCur->iPage]->intKeyLeaf==1 );
getCellInfo(pCur);
*pSize = pCur->info.nData;
*pSize = pCur->info.nPayload;
return SQLITE_OK;
}
@ -4013,30 +4018,27 @@ static int accessPayload(
){
unsigned char *aPayload;
int rc = SQLITE_OK;
u32 nKey;
int iIdx = 0;
MemPage *pPage = pCur->apPage[pCur->iPage]; /* Btree page of current entry */
BtShared *pBt = pCur->pBt; /* Btree this cursor belongs to */
#ifdef SQLITE_DIRECT_OVERFLOW_READ
int bEnd; /* True if reading to end of data */
int bEnd; /* True if reading to end of data */
#endif
assert( pPage );
assert( pCur->eState==CURSOR_VALID );
assert( pCur->aiIdx[pCur->iPage]<pPage->nCell );
assert( cursorHoldsMutex(pCur) );
assert( eOp!=2 || offset==0 ); /* Always start from beginning for eOp==2 */
assert( eOp!=2 || offset==0 ); /* Always start from beginning for eOp==2 */
getCellInfo(pCur);
aPayload = pCur->info.pCell + pCur->info.nHeader;
nKey = (pPage->intKey ? 0 : (int)pCur->info.nKey);
aPayload = pCur->info.pPayload;
#ifdef SQLITE_DIRECT_OVERFLOW_READ
bEnd = (offset+amt==nKey+pCur->info.nData);
bEnd = offset+amt==pCur->info.nPayload;
#endif
assert( offset+amt <= pCur->info.nPayload );
if( NEVER(offset+amt > nKey+pCur->info.nData)
|| &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize]
){
if( &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize] ){
/* Trying to read or write past the end of the data is an error */
return SQLITE_CORRUPT_BKPT;
}
@ -4092,7 +4094,9 @@ static int accessPayload(
** entry for the first required overflow page is valid, skip
** directly to it.
*/
if( (pCur->curFlags & BTCF_ValidOvfl)!=0 && pCur->aOverflow[offset/ovflSize] ){
if( (pCur->curFlags & BTCF_ValidOvfl)!=0
&& pCur->aOverflow[offset/ovflSize]
){
iIdx = (offset/ovflSize);
nextPage = pCur->aOverflow[iIdx];
offset = (offset%ovflSize);
@ -4270,7 +4274,7 @@ static const void *fetchPayload(
assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell );
assert( pCur->info.nSize>0 );
*pAmt = pCur->info.nLocal;
return (void*)(pCur->info.pCell + pCur->info.nHeader);
return (void*)pCur->info.pPayload;
}
@ -4698,7 +4702,7 @@ int sqlite3BtreeMovetoUnpacked(
for(;;){
i64 nCellKey;
pCell = findCell(pPage, idx) + pPage->childPtrSize;
if( pPage->hasData ){
if( pPage->intKeyLeaf ){
while( 0x80 <= *(pCell++) ){
if( pCell>=pPage->aDataEnd ) return SQLITE_CORRUPT_BKPT;
}
@ -4957,9 +4961,9 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
**
** The main entry point is sqlite3BtreePrevious(). That routine is optimized
** for the common case of merely decrementing the cell counter BtCursor.aiIdx
** to the previous cell on the current page. The (slower) btreePrevious() helper
** routine is called when it is necessary to move to a different page or
** to restore the cursor.
** to the previous cell on the current page. The (slower) btreePrevious()
** helper routine is called when it is necessary to move to a different page
** or to restore the cursor.
**
** The calling function will set *pRes to 0 or 1. The initial *pRes value
** will be 1 if the cursor being stepped corresponds to an SQL index and
@ -5287,7 +5291,7 @@ static int allocateBtreePage(
memcpy(&aData[8+closest*4], &aData[4+k*4], 4);
}
put4byte(&aData[4], k-1);
noContent = !btreeGetHasContent(pBt, *pPgno) ? PAGER_GET_NOCONTENT : 0;
noContent = !btreeGetHasContent(pBt, *pPgno)? PAGER_GET_NOCONTENT : 0;
rc = btreeGetPage(pBt, *pPgno, ppPage, noContent);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite((*ppPage)->pDbPage);
@ -5320,7 +5324,7 @@ static int allocateBtreePage(
** here are confined to those pages that lie between the end of the
** database image and the end of the database file.
*/
int bNoContent = (0==IfNotOmitAV(pBt->bDoTruncate)) ? PAGER_GET_NOCONTENT : 0;
int bNoContent = (0==IfNotOmitAV(pBt->bDoTruncate))? PAGER_GET_NOCONTENT:0;
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
if( rc ) return rc;
@ -5519,9 +5523,15 @@ static void freePage(MemPage *pPage, int *pRC){
}
/*
** Free any overflow pages associated with the given Cell.
** Free any overflow pages associated with the given Cell. Write the
** local Cell size (the number of bytes on the original page, omitting
** overflow) into *pnSize.
*/
static int clearCell(MemPage *pPage, unsigned char *pCell){
static int clearCell(
MemPage *pPage, /* The page that contains the Cell */
unsigned char *pCell, /* First byte of the Cell */
u16 *pnSize /* Write the size of the Cell here */
){
BtShared *pBt = pPage->pBt;
CellInfo info;
Pgno ovflPgno;
@ -5531,6 +5541,7 @@ static int clearCell(MemPage *pPage, unsigned char *pCell){
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
btreeParseCellPtr(pPage, pCell, &info);
*pnSize = info.nSize;
if( info.iOverflow==0 ){
return SQLITE_OK; /* No overflow pages. Return without doing anything */
}
@ -5614,7 +5625,6 @@ static int fillInCell(
BtShared *pBt = pPage->pBt;
Pgno pgnoOvfl = 0;
int nHeader;
CellInfo info;
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
@ -5624,23 +5634,17 @@ static int fillInCell(
|| sqlite3PagerIswriteable(pPage->pDbPage) );
/* Fill in the header. */
nHeader = 0;
if( !pPage->leaf ){
nHeader += 4;
}
if( pPage->hasData ){
nHeader += putVarint32(&pCell[nHeader], nData+nZero);
nHeader = pPage->childPtrSize;
nPayload = nData + nZero;
if( pPage->intKeyLeaf ){
nHeader += putVarint32(&pCell[nHeader], nPayload);
}else{
nData = nZero = 0;
assert( nData==0 );
assert( nZero==0 );
}
nHeader += putVarint(&pCell[nHeader], *(u64*)&nKey);
btreeParseCellPtr(pPage, pCell, &info);
assert( info.nHeader==nHeader );
assert( info.nKey==nKey );
assert( info.nData==(u32)(nData+nZero) );
/* Fill in the payload */
nPayload = nData + nZero;
/* Fill in the payload size */
if( pPage->intKey ){
pSrc = pData;
nSrc = nData;
@ -5649,15 +5653,55 @@ static int fillInCell(
if( NEVER(nKey>0x7fffffff || pKey==0) ){
return SQLITE_CORRUPT_BKPT;
}
nPayload += (int)nKey;
nPayload = (int)nKey;
pSrc = pKey;
nSrc = (int)nKey;
}
*pnSize = info.nSize;
spaceLeft = info.nLocal;
if( nPayload<=pPage->maxLocal ){
n = nHeader + nPayload;
testcase( n==3 );
testcase( n==4 );
if( n<4 ) n = 4;
*pnSize = n;
spaceLeft = nPayload;
pPrior = pCell;
}else{
int mn = pPage->minLocal;
n = mn + (nPayload - mn) % (pPage->pBt->usableSize - 4);
testcase( n==pPage->maxLocal );
testcase( n==pPage->maxLocal+1 );
if( n > pPage->maxLocal ) n = mn;
spaceLeft = n;
*pnSize = n + nHeader + 4;
pPrior = &pCell[nHeader+n];
}
pPayload = &pCell[nHeader];
pPrior = &pCell[info.iOverflow];
/* At this point variables should be set as follows:
**
** nPayload Total payload size in bytes
** pPayload Begin writing payload here
** spaceLeft Space available at pPayload. If nPayload>spaceLeft,
** that means content must spill into overflow pages.
** *pnSize Size of the local cell (not counting overflow pages)
** pPrior Where to write the pgno of the first overflow page
**
** Use a call to btreeParseCellPtr() to verify that the values above
** were computed correctly.
*/
#if SQLITE_DEBUG
{
CellInfo info;
btreeParseCellPtr(pPage, pCell, &info);
assert( nHeader=(int)(info.pPayload - pCell) );
assert( info.nKey==nKey );
assert( *pnSize == info.nSize );
assert( spaceLeft == info.nLocal );
assert( pPrior == &pCell[info.iOverflow] );
}
#endif
/* Write the payload into the local Cell and any extra into overflow pages */
while( nPayload>0 ){
if( spaceLeft==0 ){
#ifndef SQLITE_OMIT_AUTOVACUUM
@ -6365,7 +6409,7 @@ static int balance_nonroot(
** leafData: 1 if pPage holds key+data and pParent holds only keys.
*/
leafCorrection = apOld[0]->leaf*4;
leafData = apOld[0]->hasData;
leafData = apOld[0]->intKeyLeaf;
for(i=0; i<nOld; i++){
int limit;
@ -6941,7 +6985,7 @@ static int balance(BtCursor *pCur){
rc = sqlite3PagerWrite(pParent->pDbPage);
if( rc==SQLITE_OK ){
#ifndef SQLITE_OMIT_QUICKBALANCE
if( pPage->hasData
if( pPage->intKeyLeaf
&& pPage->nOverflow==1
&& pPage->aiOvfl[0]==pPage->nCell
&& pParent->pgno!=1
@ -7060,7 +7104,8 @@ int sqlite3BtreeInsert(
}
assert( cursorHoldsMutex(pCur) );
assert( (pCur->curFlags & BTCF_WriteFlag)!=0 && pBt->inTransaction==TRANS_WRITE
assert( (pCur->curFlags & BTCF_WriteFlag)!=0
&& pBt->inTransaction==TRANS_WRITE
&& (pBt->btsFlags & BTS_READ_ONLY)==0 );
assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
@ -7093,7 +7138,8 @@ int sqlite3BtreeInsert(
/* 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()
** call */
if( (pCur->curFlags&BTCF_ValidNKey)!=0 && nKey>0 && pCur->info.nKey==nKey-1 ){
if( (pCur->curFlags&BTCF_ValidNKey)!=0 && nKey>0
&& pCur->info.nKey==nKey-1 ){
loc = -1;
}
}
@ -7112,9 +7158,8 @@ int sqlite3BtreeInsert(
pCur->pgnoRoot, nKey, nData, pPage->pgno,
loc==0 ? "overwrite" : "new entry"));
assert( pPage->isInit );
allocateTempSpace(pBt);
newCell = pBt->pTmpSpace;
if( newCell==0 ) return SQLITE_NOMEM;
assert( newCell!=0 );
rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, nZero, &szNew);
if( rc ) goto end_insert;
assert( szNew==cellSizePtr(pPage, newCell) );
@ -7131,8 +7176,7 @@ int sqlite3BtreeInsert(
if( !pPage->leaf ){
memcpy(newCell, oldCell, 4);
}
szOld = cellSizePtr(pPage, oldCell);
rc = clearCell(pPage, oldCell);
rc = clearCell(pPage, oldCell, &szOld);
dropCell(pPage, idx, szOld, &rc);
if( rc ) goto end_insert;
}else if( loc<0 && pPage->nCell>0 ){
@ -7194,6 +7238,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
unsigned char *pCell; /* Pointer to cell to delete */
int iCellIdx; /* Index of cell to delete */
int iCellDepth; /* Depth of node containing pCell */
u16 szCell; /* Size of the cell being deleted */
assert( cursorHoldsMutex(pCur) );
assert( pBt->inTransaction==TRANS_WRITE );
@ -7242,8 +7287,8 @@ int sqlite3BtreeDelete(BtCursor *pCur){
rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc ) return rc;
rc = clearCell(pPage, pCell);
dropCell(pPage, iCellIdx, cellSizePtr(pPage, pCell), &rc);
rc = clearCell(pPage, pCell, &szCell);
dropCell(pPage, iCellIdx, szCell, &rc);
if( rc ) return rc;
/* If the cell deleted was not located on a leaf page, then the cursor
@ -7260,10 +7305,8 @@ int sqlite3BtreeDelete(BtCursor *pCur){
pCell = findCell(pLeaf, pLeaf->nCell-1);
nCell = cellSizePtr(pLeaf, pCell);
assert( MX_CELL_SIZE(pBt) >= nCell );
allocateTempSpace(pBt);
pTmp = pBt->pTmpSpace;
assert( pTmp!=0 );
rc = sqlite3PagerWrite(pLeaf->pDbPage);
insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc);
dropCell(pLeaf, pLeaf->nCell-1, nCell, &rc);
@ -7475,6 +7518,7 @@ static int clearDatabasePage(
unsigned char *pCell;
int i;
int hdr;
u16 szCell;
assert( sqlite3_mutex_held(pBt->mutex) );
if( pgno>btreePagecount(pBt) ){
@ -7490,7 +7534,7 @@ static int clearDatabasePage(
rc = clearDatabasePage(pBt, get4byte(pCell), 1, pnChange);
if( rc ) goto cleardatabasepage_out;
}
rc = clearCell(pPage, pCell);
rc = clearCell(pPage, pCell, &szCell);
if( rc ) goto cleardatabasepage_out;
}
if( !pPage->leaf ){
@ -7836,11 +7880,11 @@ Pager *sqlite3BtreePager(Btree *p){
*/
static void checkAppendMsg(
IntegrityCk *pCheck,
char *zMsg1,
const char *zFormat,
...
){
va_list ap;
char zBuf[200];
if( !pCheck->mxErr ) return;
pCheck->mxErr--;
pCheck->nErr++;
@ -7848,8 +7892,9 @@ static void checkAppendMsg(
if( pCheck->errMsg.nChar ){
sqlite3StrAccumAppend(&pCheck->errMsg, "\n", 1);
}
if( zMsg1 ){
sqlite3StrAccumAppendAll(&pCheck->errMsg, zMsg1);
if( pCheck->zPfx ){
sqlite3_snprintf(sizeof(zBuf), zBuf, pCheck->zPfx, pCheck->v1, pCheck->v2);
sqlite3StrAccumAppendAll(&pCheck->errMsg, zBuf);
}
sqlite3VXPrintf(&pCheck->errMsg, 1, zFormat, ap);
va_end(ap);
@ -7887,14 +7932,14 @@ static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){
**
** Also check that the page number is in bounds.
*/
static int checkRef(IntegrityCk *pCheck, Pgno iPage, char *zContext){
static int checkRef(IntegrityCk *pCheck, Pgno iPage){
if( iPage==0 ) return 1;
if( iPage>pCheck->nPage ){
checkAppendMsg(pCheck, zContext, "invalid page number %d", iPage);
checkAppendMsg(pCheck, "invalid page number %d", iPage);
return 1;
}
if( getPageReferenced(pCheck, iPage) ){
checkAppendMsg(pCheck, zContext, "2nd reference to page %d", iPage);
checkAppendMsg(pCheck, "2nd reference to page %d", iPage);
return 1;
}
setPageReferenced(pCheck, iPage);
@ -7911,8 +7956,7 @@ static void checkPtrmap(
IntegrityCk *pCheck, /* Integrity check context */
Pgno iChild, /* Child page number */
u8 eType, /* Expected pointer map type */
Pgno iParent, /* Expected pointer map parent page number */
char *zContext /* Context description (used for error msg) */
Pgno iParent /* Expected pointer map parent page number */
){
int rc;
u8 ePtrmapType;
@ -7921,12 +7965,12 @@ static void checkPtrmap(
rc = ptrmapGet(pCheck->pBt, iChild, &ePtrmapType, &iPtrmapParent);
if( rc!=SQLITE_OK ){
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) pCheck->mallocFailed = 1;
checkAppendMsg(pCheck, zContext, "Failed to read ptrmap key=%d", iChild);
checkAppendMsg(pCheck, "Failed to read ptrmap key=%d", iChild);
return;
}
if( ePtrmapType!=eType || iPtrmapParent!=iParent ){
checkAppendMsg(pCheck, zContext,
checkAppendMsg(pCheck,
"Bad ptr map entry key=%d expected=(%d,%d) got=(%d,%d)",
iChild, eType, iParent, ePtrmapType, iPtrmapParent);
}
@ -7941,8 +7985,7 @@ static void checkList(
IntegrityCk *pCheck, /* Integrity checking context */
int isFreeList, /* True for a freelist. False for overflow page list */
int iPage, /* Page number for first page in the list */
int N, /* Expected number of pages in the list */
char *zContext /* Context for error messages */
int N /* Expected number of pages in the list */
){
int i;
int expected = N;
@ -7951,14 +7994,14 @@ static void checkList(
DbPage *pOvflPage;
unsigned char *pOvflData;
if( iPage<1 ){
checkAppendMsg(pCheck, zContext,
checkAppendMsg(pCheck,
"%d of %d pages missing from overflow list starting at %d",
N+1, expected, iFirst);
break;
}
if( checkRef(pCheck, iPage, zContext) ) break;
if( checkRef(pCheck, iPage) ) break;
if( sqlite3PagerGet(pCheck->pPager, (Pgno)iPage, &pOvflPage) ){
checkAppendMsg(pCheck, zContext, "failed to get page %d", iPage);
checkAppendMsg(pCheck, "failed to get page %d", iPage);
break;
}
pOvflData = (unsigned char *)sqlite3PagerGetData(pOvflPage);
@ -7966,11 +8009,11 @@ static void checkList(
int n = get4byte(&pOvflData[4]);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pCheck->pBt->autoVacuum ){
checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0, zContext);
checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0);
}
#endif
if( n>(int)pCheck->pBt->usableSize/4-2 ){
checkAppendMsg(pCheck, zContext,
checkAppendMsg(pCheck,
"freelist leaf count too big on page %d", iPage);
N--;
}else{
@ -7978,10 +8021,10 @@ static void checkList(
Pgno iFreePage = get4byte(&pOvflData[8+i*4]);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pCheck->pBt->autoVacuum ){
checkPtrmap(pCheck, iFreePage, PTRMAP_FREEPAGE, 0, zContext);
checkPtrmap(pCheck, iFreePage, PTRMAP_FREEPAGE, 0);
}
#endif
checkRef(pCheck, iFreePage, zContext);
checkRef(pCheck, iFreePage);
}
N -= n;
}
@ -7994,7 +8037,7 @@ static void checkList(
*/
if( pCheck->pBt->autoVacuum && N>0 ){
i = get4byte(pOvflData);
checkPtrmap(pCheck, i, PTRMAP_OVERFLOW2, iPage, zContext);
checkPtrmap(pCheck, i, PTRMAP_OVERFLOW2, iPage);
}
}
#endif
@ -8026,7 +8069,6 @@ static void checkList(
static int checkTreePage(
IntegrityCk *pCheck, /* Context for the sanity check */
int iPage, /* Page number of the page to check */
char *zParentContext, /* Parent context */
i64 *pnParentMinKey,
i64 *pnParentMaxKey
){
@ -8037,23 +8079,26 @@ static int checkTreePage(
u8 *data;
BtShared *pBt;
int usableSize;
char zContext[100];
char *hit = 0;
i64 nMinKey = 0;
i64 nMaxKey = 0;
sqlite3_snprintf(sizeof(zContext), zContext, "Page %d: ", iPage);
const char *saved_zPfx = pCheck->zPfx;
int saved_v1 = pCheck->v1;
int saved_v2 = pCheck->v2;
/* Check that the page exists
*/
pBt = pCheck->pBt;
usableSize = pBt->usableSize;
if( iPage==0 ) return 0;
if( checkRef(pCheck, iPage, zParentContext) ) return 0;
if( checkRef(pCheck, iPage) ) return 0;
pCheck->zPfx = "Page %d: ";
pCheck->v1 = iPage;
if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){
checkAppendMsg(pCheck, zContext,
checkAppendMsg(pCheck,
"unable to get the page. error code=%d", rc);
return 0;
depth = -1;
goto end_of_check;
}
/* Clear MemPage.isInit to make sure the corruption detection code in
@ -8061,10 +8106,11 @@ static int checkTreePage(
pPage->isInit = 0;
if( (rc = btreeInitPage(pPage))!=0 ){
assert( rc==SQLITE_CORRUPT ); /* The only possible error from InitPage */
checkAppendMsg(pCheck, zContext,
checkAppendMsg(pCheck,
"btreeInitPage() returns error code %d", rc);
releasePage(pPage);
return 0;
depth = -1;
goto end_of_check;
}
/* Check out all the cells.
@ -8077,23 +8123,23 @@ static int checkTreePage(
/* Check payload overflow pages
*/
sqlite3_snprintf(sizeof(zContext), zContext,
"On tree page %d cell %d: ", iPage, i);
pCheck->zPfx = "On tree page %d cell %d: ";
pCheck->v1 = iPage;
pCheck->v2 = i;
pCell = findCell(pPage,i);
btreeParseCellPtr(pPage, pCell, &info);
sz = info.nData;
if( !pPage->intKey ) sz += (int)info.nKey;
sz = info.nPayload;
/* For intKey pages, check that the keys are in order.
*/
else if( i==0 ) nMinKey = nMaxKey = info.nKey;
else{
if( info.nKey <= nMaxKey ){
checkAppendMsg(pCheck, zContext,
"Rowid %lld out of order (previous was %lld)", info.nKey, nMaxKey);
if( pPage->intKey ){
if( i==0 ){
nMinKey = nMaxKey = info.nKey;
}else if( info.nKey <= nMaxKey ){
checkAppendMsg(pCheck,
"Rowid %lld out of order (previous was %lld)", info.nKey, nMaxKey);
}
nMaxKey = info.nKey;
}
assert( sz==info.nPayload );
if( (sz>info.nLocal)
&& (&pCell[info.iOverflow]<=&pPage->aData[pBt->usableSize])
){
@ -8101,10 +8147,10 @@ static int checkTreePage(
Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum ){
checkPtrmap(pCheck, pgnoOvfl, PTRMAP_OVERFLOW1, iPage, zContext);
checkPtrmap(pCheck, pgnoOvfl, PTRMAP_OVERFLOW1, iPage);
}
#endif
checkList(pCheck, 0, pgnoOvfl, nPage, zContext);
checkList(pCheck, 0, pgnoOvfl, nPage);
}
/* Check sanity of left child page.
@ -8113,12 +8159,12 @@ static int checkTreePage(
pgno = get4byte(pCell);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum ){
checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, zContext);
checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
}
#endif
d2 = checkTreePage(pCheck, pgno, zContext, &nMinKey, i==0 ? NULL : &nMaxKey);
d2 = checkTreePage(pCheck, pgno, &nMinKey, i==0?NULL:&nMaxKey);
if( i>0 && d2!=depth ){
checkAppendMsg(pCheck, zContext, "Child page depth differs");
checkAppendMsg(pCheck, "Child page depth differs");
}
depth = d2;
}
@ -8126,37 +8172,39 @@ static int checkTreePage(
if( !pPage->leaf ){
pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
sqlite3_snprintf(sizeof(zContext), zContext,
"On page %d at right child: ", iPage);
pCheck->zPfx = "On page %d at right child: ";
pCheck->v1 = iPage;
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum ){
checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, zContext);
checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
}
#endif
checkTreePage(pCheck, pgno, zContext, NULL, !pPage->nCell ? NULL : &nMaxKey);
checkTreePage(pCheck, pgno, NULL, !pPage->nCell?NULL:&nMaxKey);
}
/* For intKey leaf pages, check that the min/max keys are in order
** with any left/parent/right pages.
*/
pCheck->zPfx = "Page %d: ";
pCheck->v1 = iPage;
if( pPage->leaf && pPage->intKey ){
/* if we are a left child page */
if( pnParentMinKey ){
/* if we are the left most child page */
if( !pnParentMaxKey ){
if( nMaxKey > *pnParentMinKey ){
checkAppendMsg(pCheck, zContext,
checkAppendMsg(pCheck,
"Rowid %lld out of order (max larger than parent min of %lld)",
nMaxKey, *pnParentMinKey);
}
}else{
if( nMinKey <= *pnParentMinKey ){
checkAppendMsg(pCheck, zContext,
checkAppendMsg(pCheck,
"Rowid %lld out of order (min less than parent min of %lld)",
nMinKey, *pnParentMinKey);
}
if( nMaxKey > *pnParentMaxKey ){
checkAppendMsg(pCheck, zContext,
checkAppendMsg(pCheck,
"Rowid %lld out of order (max larger than parent max of %lld)",
nMaxKey, *pnParentMaxKey);
}
@ -8165,7 +8213,7 @@ static int checkTreePage(
/* else if we're a right child page */
} else if( pnParentMaxKey ){
if( nMinKey <= *pnParentMaxKey ){
checkAppendMsg(pCheck, zContext,
checkAppendMsg(pCheck,
"Rowid %lld out of order (min less than parent max of %lld)",
nMinKey, *pnParentMaxKey);
}
@ -8177,6 +8225,7 @@ static int checkTreePage(
data = pPage->aData;
hdr = pPage->hdrOffset;
hit = sqlite3PageMalloc( pBt->pageSize );
pCheck->zPfx = 0;
if( hit==0 ){
pCheck->mallocFailed = 1;
}else{
@ -8194,7 +8243,8 @@ static int checkTreePage(
size = cellSizePtr(pPage, &data[pc]);
}
if( (int)(pc+size-1)>=usableSize ){
checkAppendMsg(pCheck, 0,
pCheck->zPfx = 0;
checkAppendMsg(pCheck,
"Corruption detected in cell %d on page %d",i,iPage);
}else{
for(j=pc+size-1; j>=pc; j--) hit[j]++;
@ -8216,19 +8266,24 @@ static int checkTreePage(
if( hit[i]==0 ){
cnt++;
}else if( hit[i]>1 ){
checkAppendMsg(pCheck, 0,
checkAppendMsg(pCheck,
"Multiple uses for byte %d of page %d", i, iPage);
break;
}
}
if( cnt!=data[hdr+7] ){
checkAppendMsg(pCheck, 0,
checkAppendMsg(pCheck,
"Fragmentation of %d bytes reported as %d on page %d",
cnt, data[hdr+7], iPage);
}
}
sqlite3PageFree(hit);
releasePage(pPage);
end_of_check:
pCheck->zPfx = saved_zPfx;
pCheck->v1 = saved_v1;
pCheck->v2 = saved_v2;
return depth+1;
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
@ -8269,6 +8324,9 @@ char *sqlite3BtreeIntegrityCheck(
sCheck.mxErr = mxErr;
sCheck.nErr = 0;
sCheck.mallocFailed = 0;
sCheck.zPfx = 0;
sCheck.v1 = 0;
sCheck.v2 = 0;
*pnErr = 0;
if( sCheck.nPage==0 ){
sqlite3BtreeLeave(p);
@ -8288,8 +8346,10 @@ char *sqlite3BtreeIntegrityCheck(
/* Check the integrity of the freelist
*/
sCheck.zPfx = "Main freelist: ";
checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
get4byte(&pBt->pPage1->aData[36]), "Main freelist: ");
get4byte(&pBt->pPage1->aData[36]));
sCheck.zPfx = 0;
/* Check all the tables.
*/
@ -8297,10 +8357,12 @@ char *sqlite3BtreeIntegrityCheck(
if( aRoot[i]==0 ) continue;
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum && aRoot[i]>1 ){
checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0, 0);
checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0);
}
#endif
checkTreePage(&sCheck, aRoot[i], "List of tree roots: ", NULL, NULL);
sCheck.zPfx = "List of tree roots: ";
checkTreePage(&sCheck, aRoot[i], NULL, NULL);
sCheck.zPfx = 0;
}
/* Make sure every page in the file is referenced
@ -8308,7 +8370,7 @@ char *sqlite3BtreeIntegrityCheck(
for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
#ifdef SQLITE_OMIT_AUTOVACUUM
if( getPageReferenced(&sCheck, i)==0 ){
checkAppendMsg(&sCheck, 0, "Page %d is never used", i);
checkAppendMsg(&sCheck, "Page %d is never used", i);
}
#else
/* If the database supports auto-vacuum, make sure no tables contain
@ -8316,11 +8378,11 @@ char *sqlite3BtreeIntegrityCheck(
*/
if( getPageReferenced(&sCheck, i)==0 &&
(PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){
checkAppendMsg(&sCheck, 0, "Page %d is never used", i);
checkAppendMsg(&sCheck, "Page %d is never used", i);
}
if( getPageReferenced(&sCheck, i)!=0 &&
(PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){
checkAppendMsg(&sCheck, 0, "Pointer map page %d is referenced", i);
checkAppendMsg(&sCheck, "Pointer map page %d is referenced", i);
}
#endif
}
@ -8330,7 +8392,7 @@ char *sqlite3BtreeIntegrityCheck(
** of the integrity check.
*/
if( NEVER(nRef != sqlite3PagerRefcount(pBt->pPager)) ){
checkAppendMsg(&sCheck, 0,
checkAppendMsg(&sCheck,
"Outstanding page count goes from %d to %d during this analysis",
nRef, sqlite3PagerRefcount(pBt->pPager)
);
@ -8526,7 +8588,7 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
** required in case any of them are holding references to an xFetch
** version of the b-tree page modified by the accessPayload call below.
**
** Note that pCsr must be open on a BTREE_INTKEY table and saveCursorPosition()
** Note that pCsr must be open on a INTKEY table and saveCursorPosition()
** and hence saveAllCursors() cannot fail on a BTREE_INTKEY table, hence
** saveAllCursors can only return SQLITE_OK.
*/

View File

@ -273,9 +273,10 @@ typedef struct BtLock BtLock;
struct MemPage {
u8 isInit; /* True if previously initialized. MUST BE FIRST! */
u8 nOverflow; /* Number of overflow cell bodies in aCell[] */
u8 intKey; /* True if intkey flag is set */
u8 leaf; /* True if leaf flag is set */
u8 hasData; /* True if this page stores data */
u8 intKey; /* True if table b-trees. False for index b-trees */
u8 intKeyLeaf; /* True if the leaf of an intKey table */
u8 noPayload; /* True if internal intKey page (thus w/o data) */
u8 leaf; /* True if a leaf page */
u8 hdrOffset; /* 100 for page 1. 0 otherwise */
u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */
u8 max1bytePayload; /* min(maxLocal,127) */
@ -456,12 +457,10 @@ struct BtShared {
*/
typedef struct CellInfo CellInfo;
struct CellInfo {
i64 nKey; /* The key for INTKEY tables, or number of bytes in key */
u8 *pCell; /* Pointer to the start of cell content */
u32 nData; /* Number of bytes of data */
u32 nPayload; /* Total amount of payload */
u16 nHeader; /* Size of the cell content header in bytes */
u16 nLocal; /* Amount of payload held locally */
i64 nKey; /* The key for INTKEY tables, or nPayload otherwise */
u8 *pPayload; /* Pointer to the start of payload */
u32 nPayload; /* Bytes of payload */
u16 nLocal; /* Amount of payload held locally, not on overflow */
u16 iOverflow; /* Offset to overflow page number. Zero if no overflow */
u16 nSize; /* Size of the cell content on the main b-tree page */
};
@ -658,6 +657,8 @@ struct IntegrityCk {
int mxErr; /* Stop accumulating errors when this reaches zero */
int nErr; /* Number of messages written to zErrMsg so far */
int mallocFailed; /* A memory allocation error has occurred */
const char *zPfx; /* Error message prefix */
int v1, v2; /* Values for up to two %d fields in zPfx */
StrAccum errMsg; /* Accumulate the error message text here */
};

View File

@ -1236,7 +1236,7 @@ void sqlite3AddDefaultValue(Parse *pParse, ExprSpan *pSpan){
p = pParse->pNewTable;
if( p!=0 ){
pCol = &(p->aCol[p->nCol-1]);
if( !sqlite3ExprIsConstantOrFunction(pSpan->pExpr) ){
if( !sqlite3ExprIsConstantOrFunction(pSpan->pExpr, db->init.busy) ){
sqlite3ErrorMsg(pParse, "default value of column [%s] is not constant",
pCol->zName);
}else{

View File

@ -395,7 +395,7 @@ int sqlite3_compileoption_used(const char *zOptName){
** linear search is adequate. No need for a binary search. */
for(i=0; i<ArraySize(azCompileOpt); i++){
if( sqlite3StrNICmp(zOptName, azCompileOpt[i], n)==0
&& sqlite3CtypeMap[(unsigned char)azCompileOpt[i][n]]==0
&& sqlite3IsIdChar((unsigned char)azCompileOpt[i][n])==0
){
return 1;
}

View File

@ -1212,32 +1212,40 @@ void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
/*
** These routines are Walker callbacks. Walker.u.pi is a pointer
** to an integer. These routines are checking an expression to see
** if it is a constant. Set *Walker.u.pi to 0 if the expression is
** if it is a constant. Set *Walker.u.i to 0 if the expression is
** not constant.
**
** These callback routines are used to implement the following:
**
** sqlite3ExprIsConstant()
** sqlite3ExprIsConstantNotJoin()
** sqlite3ExprIsConstantOrFunction()
** sqlite3ExprIsConstant() pWalker->u.i==1
** sqlite3ExprIsConstantNotJoin() pWalker->u.i==2
** sqlite3ExprIsConstantOrFunction() pWalker->u.i==3 or 4
**
** The sqlite3ExprIsConstantOrFunction() is used for evaluating expressions
** in a CREATE TABLE statement. The Walker.u.i value is 4 when parsing
** an existing schema and 3 when processing a new statement. A bound
** parameter raises an error for new statements, but is silently converted
** to NULL for existing schemas. This allows sqlite_master tables that
** contain a bound parameter because they were generated by older versions
** of SQLite to be parsed by newer versions of SQLite without raising a
** malformed schema error.
*/
static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
/* If pWalker->u.i is 3 then any term of the expression that comes from
/* If pWalker->u.i is 2 then any term of the expression that comes from
** the ON or USING clauses of a join disqualifies the expression
** from being considered constant. */
if( pWalker->u.i==3 && ExprHasProperty(pExpr, EP_FromJoin) ){
if( pWalker->u.i==2 && ExprHasProperty(pExpr, EP_FromJoin) ){
pWalker->u.i = 0;
return WRC_Abort;
}
switch( pExpr->op ){
/* Consider functions to be constant if all their arguments are constant
** and either pWalker->u.i==2 or the function as the SQLITE_FUNC_CONST
** and either pWalker->u.i==3 or 4 or the function as the SQLITE_FUNC_CONST
** flag. */
case TK_FUNCTION:
if( pWalker->u.i==2 || ExprHasProperty(pExpr,EP_Constant) ){
if( pWalker->u.i>=3 || ExprHasProperty(pExpr,EP_Constant) ){
return WRC_Continue;
}
/* Fall through */
@ -1251,6 +1259,19 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
testcase( pExpr->op==TK_AGG_COLUMN );
pWalker->u.i = 0;
return WRC_Abort;
case TK_VARIABLE:
if( pWalker->u.i==4 ){
/* Silently convert bound parameters that appear inside of CREATE
** statements into a NULL when parsing the CREATE statement text out
** of the sqlite_master table */
pExpr->op = TK_NULL;
}else if( pWalker->u.i==3 ){
/* A bound parameter in a CREATE statement that originates from
** sqlite3_prepare() causes an error */
pWalker->u.i = 0;
return WRC_Abort;
}
/* Fall through */
default:
testcase( pExpr->op==TK_SELECT ); /* selectNodeIsConstant will disallow */
testcase( pExpr->op==TK_EXISTS ); /* selectNodeIsConstant will disallow */
@ -1291,7 +1312,7 @@ int sqlite3ExprIsConstant(Expr *p){
** an ON or USING clause.
*/
int sqlite3ExprIsConstantNotJoin(Expr *p){
return exprIsConst(p, 3);
return exprIsConst(p, 2);
}
/*
@ -1303,8 +1324,9 @@ int sqlite3ExprIsConstantNotJoin(Expr *p){
** is considered a variable but a single-quoted string (ex: 'abc') is
** a constant.
*/
int sqlite3ExprIsConstantOrFunction(Expr *p){
return exprIsConst(p, 2);
int sqlite3ExprIsConstantOrFunction(Expr *p, u8 isInit){
assert( isInit==0 || isInit==1 );
return exprIsConst(p, 3+isInit);
}
/*

View File

@ -22,7 +22,10 @@
** Return the collating function associated with a function.
*/
static CollSeq *sqlite3GetFuncCollSeq(sqlite3_context *context){
return context->pColl;
VdbeOp *pOp = &context->pVdbe->aOp[context->iOp-1];
assert( pOp->opcode==OP_CollSeq );
assert( pOp->p4type==P4_COLLSEQ );
return pOp->p4.pColl;
}
/*
@ -567,10 +570,12 @@ struct compareInfo {
** whereas only characters less than 0x80 do in ASCII.
*/
#if defined(SQLITE_EBCDIC)
# define sqlite3Utf8Read(A) (*((*A)++))
# define GlobUpperToLower(A) A = sqlite3UpperToLower[A]
# define sqlite3Utf8Read(A) (*((*A)++))
# define GlobUpperToLower(A) A = sqlite3UpperToLower[A]
# define GlobUpperToLowerAscii(A) A = sqlite3UpperToLower[A]
#else
# define GlobUpperToLower(A) if( !((A)&~0x7f) ){ A = sqlite3UpperToLower[A]; }
# define GlobUpperToLower(A) if( A<=0x7f ){ A = sqlite3UpperToLower[A]; }
# define GlobUpperToLowerAscii(A) A = sqlite3UpperToLower[A]
#endif
static const struct compareInfo globInfo = { '*', '?', '[', 0 };
@ -583,7 +588,7 @@ static const struct compareInfo likeInfoAlt = { '%', '_', 0, 0 };
/*
** Compare two UTF-8 strings for equality where the first string can
** potentially be a "glob" expression. Return true (1) if they
** potentially be a "glob" or "like" expression. Return true (1) if they
** are the same and false (0) if they are different.
**
** Globbing rules:
@ -603,11 +608,18 @@ static const struct compareInfo likeInfoAlt = { '%', '_', 0, 0 };
** "[a-z]" matches any single lower-case letter. To match a '-', make
** it the last character in the list.
**
** Like matching rules:
**
** '%' Matches any sequence of zero or more characters
**
*** '_' Matches any one character
**
** Ec Where E is the "esc" character and c is any other
** character, including '%', '_', and esc, match exactly c.
**
** The comments through this routine usually assume glob matching.
**
** This routine is usually quick, but can be N**2 in the worst case.
**
** Hints: to match '*' or '?', put them in "[]". Like this:
**
** abc[*]xyz Matches "abc*xyz" only
*/
static int patternCompare(
const u8 *zPattern, /* The glob pattern */
@ -615,17 +627,25 @@ static int patternCompare(
const struct compareInfo *pInfo, /* Information about how to do the compare */
u32 esc /* The escape character */
){
u32 c, c2;
int invert;
int seen;
u8 matchOne = pInfo->matchOne;
u8 matchAll = pInfo->matchAll;
u8 matchSet = pInfo->matchSet;
u8 noCase = pInfo->noCase;
int prevEscape = 0; /* True if the previous character was 'escape' */
u32 c, c2; /* Next pattern and input string chars */
u32 matchOne = pInfo->matchOne; /* "?" or "_" */
u32 matchAll = pInfo->matchAll; /* "*" or "%" */
u32 matchOther; /* "[" or the escape character */
u8 noCase = pInfo->noCase; /* True if uppercase==lowercase */
const u8 *zEscaped = 0; /* One past the last escaped input char */
/* The GLOB operator does not have an ESCAPE clause. And LIKE does not
** have the matchSet operator. So we either have to look for one or
** the other, never both. Hence the single variable matchOther is used
** to store the one we have to look for.
*/
matchOther = esc ? esc : pInfo->matchSet;
while( (c = sqlite3Utf8Read(&zPattern))!=0 ){
if( c==matchAll && !prevEscape ){
if( c==matchAll ){ /* Match "*" */
/* Skip over multiple "*" characters in the pattern. If there
** are also "?" characters, skip those as well, but consume a
** single character of the input string for each "?" skipped */
while( (c=sqlite3Utf8Read(&zPattern)) == matchAll
|| c == matchOne ){
if( c==matchOne && sqlite3Utf8Read(&zString)==0 ){
@ -633,86 +653,98 @@ static int patternCompare(
}
}
if( c==0 ){
return 1;
}else if( c==esc ){
c = sqlite3Utf8Read(&zPattern);
if( c==0 ){
return 0;
}
}else if( c==matchSet ){
assert( esc==0 ); /* This is GLOB, not LIKE */
assert( matchSet<0x80 ); /* '[' is a single-byte character */
while( *zString && patternCompare(&zPattern[-1],zString,pInfo,esc)==0 ){
SQLITE_SKIP_UTF8(zString);
}
return *zString!=0;
}
while( (c2 = sqlite3Utf8Read(&zString))!=0 ){
if( noCase ){
GlobUpperToLower(c2);
GlobUpperToLower(c);
while( c2 != 0 && c2 != c ){
c2 = sqlite3Utf8Read(&zString);
GlobUpperToLower(c2);
}
return 1; /* "*" at the end of the pattern matches */
}else if( c==matchOther ){
if( esc ){
c = sqlite3Utf8Read(&zPattern);
if( c==0 ) return 0;
}else{
while( c2 != 0 && c2 != c ){
c2 = sqlite3Utf8Read(&zString);
/* "[...]" immediately follows the "*". We have to do a slow
** recursive search in this case, but it is an unusual case. */
assert( matchOther<0x80 ); /* '[' is a single-byte character */
while( *zString
&& patternCompare(&zPattern[-1],zString,pInfo,esc)==0 ){
SQLITE_SKIP_UTF8(zString);
}
return *zString!=0;
}
}
/* At this point variable c contains the first character of the
** pattern string past the "*". Search in the input string for the
** first matching character and recursively contine the match from
** that point.
**
** For a case-insensitive search, set variable cx to be the same as
** c but in the other case and search the input string for either
** c or cx.
*/
if( c<=0x80 ){
u32 cx;
if( noCase ){
cx = sqlite3Toupper(c);
c = sqlite3Tolower(c);
}else{
cx = c;
}
while( (c2 = *(zString++))!=0 ){
if( c2!=c && c2!=cx ) continue;
if( patternCompare(zPattern,zString,pInfo,esc) ) return 1;
}
}else{
while( (c2 = sqlite3Utf8Read(&zString))!=0 ){
if( c2!=c ) continue;
if( patternCompare(zPattern,zString,pInfo,esc) ) return 1;
}
if( c2==0 ) return 0;
if( patternCompare(zPattern,zString,pInfo,esc) ) return 1;
}
return 0;
}else if( c==matchOne && !prevEscape ){
if( sqlite3Utf8Read(&zString)==0 ){
return 0;
}
}else if( c==matchSet ){
u32 prior_c = 0;
assert( esc==0 ); /* This only occurs for GLOB, not LIKE */
seen = 0;
invert = 0;
c = sqlite3Utf8Read(&zString);
if( c==0 ) return 0;
c2 = sqlite3Utf8Read(&zPattern);
if( c2=='^' ){
invert = 1;
c2 = sqlite3Utf8Read(&zPattern);
}
if( c2==']' ){
if( c==']' ) seen = 1;
c2 = sqlite3Utf8Read(&zPattern);
}
while( c2 && c2!=']' ){
if( c2=='-' && zPattern[0]!=']' && zPattern[0]!=0 && prior_c>0 ){
c2 = sqlite3Utf8Read(&zPattern);
if( c>=prior_c && c<=c2 ) seen = 1;
prior_c = 0;
}else{
if( c==c2 ){
seen = 1;
}
prior_c = c2;
}
c2 = sqlite3Utf8Read(&zPattern);
}
if( c2==0 || (seen ^ invert)==0 ){
return 0;
}
}else if( esc==c && !prevEscape ){
prevEscape = 1;
}else{
c2 = sqlite3Utf8Read(&zString);
if( noCase ){
GlobUpperToLower(c);
GlobUpperToLower(c2);
}
if( c!=c2 ){
return 0;
}
prevEscape = 0;
}
if( c==matchOther ){
if( esc ){
c = sqlite3Utf8Read(&zPattern);
if( c==0 ) return 0;
zEscaped = zPattern;
}else{
u32 prior_c = 0;
int seen = 0;
int invert = 0;
c = sqlite3Utf8Read(&zString);
if( c==0 ) return 0;
c2 = sqlite3Utf8Read(&zPattern);
if( c2=='^' ){
invert = 1;
c2 = sqlite3Utf8Read(&zPattern);
}
if( c2==']' ){
if( c==']' ) seen = 1;
c2 = sqlite3Utf8Read(&zPattern);
}
while( c2 && c2!=']' ){
if( c2=='-' && zPattern[0]!=']' && zPattern[0]!=0 && prior_c>0 ){
c2 = sqlite3Utf8Read(&zPattern);
if( c>=prior_c && c<=c2 ) seen = 1;
prior_c = 0;
}else{
if( c==c2 ){
seen = 1;
}
prior_c = c2;
}
c2 = sqlite3Utf8Read(&zPattern);
}
if( c2==0 || (seen ^ invert)==0 ){
return 0;
}
continue;
}
}
c2 = sqlite3Utf8Read(&zString);
if( c==c2 ) continue;
if( noCase && c<0x80 && c2<0x80 && sqlite3Tolower(c)==sqlite3Tolower(c2) ){
continue;
}
if( c==matchOne && zPattern!=zEscaped && c2!=0 ) continue;
return 0;
}
return *zString==0;
}

View File

@ -4951,7 +4951,7 @@ static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){
** * An I/O method finder function called FINDER that returns a pointer
** to the METHOD object in the previous bullet.
*/
#define IOMETHODS(FINDER, METHOD, VERSION, CLOSE, LOCK, UNLOCK, CKLOCK) \
#define IOMETHODS(FINDER, METHOD, VERSION, CLOSE, LOCK, UNLOCK, CKLOCK, SHMMAP) \
static const sqlite3_io_methods METHOD = { \
VERSION, /* iVersion */ \
CLOSE, /* xClose */ \
@ -4966,7 +4966,7 @@ static const sqlite3_io_methods METHOD = { \
unixFileControl, /* xFileControl */ \
unixSectorSize, /* xSectorSize */ \
unixDeviceCharacteristics, /* xDeviceCapabilities */ \
unixShmMap, /* xShmMap */ \
SHMMAP, /* xShmMap */ \
unixShmLock, /* xShmLock */ \
unixShmBarrier, /* xShmBarrier */ \
unixShmUnmap, /* xShmUnmap */ \
@ -4992,7 +4992,8 @@ IOMETHODS(
unixClose, /* xClose method */
unixLock, /* xLock method */
unixUnlock, /* xUnlock method */
unixCheckReservedLock /* xCheckReservedLock method */
unixCheckReservedLock, /* xCheckReservedLock method */
unixShmMap /* xShmMap method */
)
IOMETHODS(
nolockIoFinder, /* Finder function name */
@ -5001,7 +5002,8 @@ IOMETHODS(
nolockClose, /* xClose method */
nolockLock, /* xLock method */
nolockUnlock, /* xUnlock method */
nolockCheckReservedLock /* xCheckReservedLock method */
nolockCheckReservedLock, /* xCheckReservedLock method */
0 /* xShmMap method */
)
IOMETHODS(
dotlockIoFinder, /* Finder function name */
@ -5010,7 +5012,8 @@ IOMETHODS(
dotlockClose, /* xClose method */
dotlockLock, /* xLock method */
dotlockUnlock, /* xUnlock method */
dotlockCheckReservedLock /* xCheckReservedLock method */
dotlockCheckReservedLock, /* xCheckReservedLock method */
0 /* xShmMap method */
)
#if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS
@ -5021,7 +5024,8 @@ IOMETHODS(
flockClose, /* xClose method */
flockLock, /* xLock method */
flockUnlock, /* xUnlock method */
flockCheckReservedLock /* xCheckReservedLock method */
flockCheckReservedLock, /* xCheckReservedLock method */
0 /* xShmMap method */
)
#endif
@ -5033,7 +5037,8 @@ IOMETHODS(
semClose, /* xClose method */
semLock, /* xLock method */
semUnlock, /* xUnlock method */
semCheckReservedLock /* xCheckReservedLock method */
semCheckReservedLock, /* xCheckReservedLock method */
0 /* xShmMap method */
)
#endif
@ -5045,7 +5050,8 @@ IOMETHODS(
afpClose, /* xClose method */
afpLock, /* xLock method */
afpUnlock, /* xUnlock method */
afpCheckReservedLock /* xCheckReservedLock method */
afpCheckReservedLock, /* xCheckReservedLock method */
0 /* xShmMap method */
)
#endif
@ -5070,7 +5076,8 @@ IOMETHODS(
proxyClose, /* xClose method */
proxyLock, /* xLock method */
proxyUnlock, /* xUnlock method */
proxyCheckReservedLock /* xCheckReservedLock method */
proxyCheckReservedLock, /* xCheckReservedLock method */
0 /* xShmMap method */
)
#endif
@ -5083,7 +5090,8 @@ IOMETHODS(
unixClose, /* xClose method */
unixLock, /* xLock method */
nfsUnlock, /* xUnlock method */
unixCheckReservedLock /* xCheckReservedLock method */
unixCheckReservedLock, /* xCheckReservedLock method */
0 /* xShmMap method */
)
#endif

View File

@ -2665,6 +2665,7 @@ struct AuthContext {
** those bits are never used on the same opcode, the overlap is harmless.
*/
#define OPFLAG_NCHANGE 0x01 /* Set to update db->nChange */
#define OPFLAG_EPHEM 0x01 /* OP_Column: Ephemeral output is ok */
#define OPFLAG_LASTROWID 0x02 /* Set to update db->lastRowid */
#define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */
#define OPFLAG_APPEND 0x08 /* This is likely to be an append */
@ -2999,6 +3000,7 @@ int sqlite3CantopenError(int);
# define sqlite3Isxdigit(x) isxdigit((unsigned char)(x))
# define sqlite3Tolower(x) tolower((unsigned char)(x))
#endif
int sqlite3IsIdChar(u8);
/*
** Internal function prototypes
@ -3297,7 +3299,7 @@ void sqlite3CloseSavepoints(sqlite3 *);
void sqlite3LeaveMutexAndCloseZombie(sqlite3*);
int sqlite3ExprIsConstant(Expr*);
int sqlite3ExprIsConstantNotJoin(Expr*);
int sqlite3ExprIsConstantOrFunction(Expr*);
int sqlite3ExprIsConstantOrFunction(Expr*, u8);
int sqlite3ExprIsInteger(Expr*, int*);
int sqlite3ExprCanBeNull(const Expr*);
int sqlite3ExprNeedsNoAffinityChange(const Expr*, char);

View File

@ -1002,6 +1002,26 @@ static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
/* no-op these */
rc = SQLITE_OK;
break;
case SQLITE_FCNTL_PRAGMA: {
char **aFcntl = (char**)pArg;
if( aFcntl[1] && sqlite3_stricmp(aFcntl[1],"multiplex_truncate")==0 ){
if( aFcntl[2] && aFcntl[2][0] ){
if( sqlite3_stricmp(aFcntl[2], "on")==0
|| sqlite3_stricmp(aFcntl[2], "1")==0 ){
pGroup->bTruncate = 1;
}else
if( sqlite3_stricmp(aFcntl[2], "off")==0
|| sqlite3_stricmp(aFcntl[2], "0")==0 ){
pGroup->bTruncate = 0;
}
}
aFcntl[0] = sqlite3_mprintf(pGroup->bTruncate ? "on" : "off");
rc = SQLITE_OK;
break;
}
/* If the multiplexor does not handle the pragma, pass it through
** into the default case. */
}
default:
pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
if( pSubOpen ){

View File

@ -102,6 +102,7 @@ const char sqlite3IsEbcdicIdChar[] = {
};
#define IdChar(C) (((c=C)>=0x42 && sqlite3IsEbcdicIdChar[c-0x40]))
#endif
int sqlite3IsIdChar(u8 c){ return IdChar(c); }
/*

View File

@ -1567,17 +1567,10 @@ case OP_Function: {
ctx.iOp = pc;
ctx.pVdbe = p;
MemSetTypeFlag(ctx.pOut, MEM_Null);
ctx.fErrorOrAux = 0;
if( ctx.pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
assert( pOp>aOp );
assert( pOp[-1].p4type==P4_COLLSEQ );
assert( pOp[-1].opcode==OP_CollSeq );
ctx.pColl = pOp[-1].p4.pColl;
}
db->lastRowid = lastRowid;
assert( db->lastRowid==lastRowid );
(*ctx.pFunc->xFunc)(&ctx, n, apVal); /* IMP: R-24505-23230 */
lastRowid = db->lastRowid;
lastRowid = db->lastRowid; /* Remember rowid changes made by xFunc */
/* If the function returned an error, throw an exception */
if( ctx.fErrorOrAux ){
@ -3297,10 +3290,6 @@ case OP_OpenWrite: {
assert( OPFLAG_BULKCSR==BTREE_BULKLOAD );
sqlite3BtreeCursorHints(pCur->pCursor, (pOp->p5 & OPFLAG_BULKCSR));
/* Since it performs no memory allocation or IO, the only value that
** sqlite3BtreeCursor() may return is SQLITE_OK. */
assert( rc==SQLITE_OK );
/* Set the VdbeCursor.isTable variable. Previous versions of
** SQLite used to check if the root-page flags were sane at this point
** and report database corruption if they were not, but this check has
@ -4034,25 +4023,14 @@ case OP_NewRowid: { /* out2-prerelease */
** it finds one that is not previously used. */
assert( pOp->p3==0 ); /* We cannot be in random rowid mode if this is
** an AUTOINCREMENT table. */
/* on the first attempt, simply do one more than previous */
v = lastRowid;
v &= (MAX_ROWID>>1); /* ensure doesn't go negative */
v++; /* ensure non-zero */
cnt = 0;
while( ((rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)v,
do{
sqlite3_randomness(sizeof(v), &v);
v &= (MAX_ROWID>>1); v++; /* Ensure that v is greater than zero */
}while( ((rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)v,
0, &res))==SQLITE_OK)
&& (res==0)
&& (++cnt<100)){
/* collision - try another random rowid */
sqlite3_randomness(sizeof(v), &v);
if( cnt<5 ){
/* try "small" random rowids for the initial attempts */
v &= 0xffffff;
}else{
v &= (MAX_ROWID>>1); /* ensure doesn't go negative */
}
v++; /* ensure non-zero */
}
&& (++cnt<100));
if( rc==SQLITE_OK && res==0 ){
rc = SQLITE_FULL; /* IMP: R-38219-53002 */
goto abort_due_to_error;
@ -5699,14 +5677,9 @@ case OP_AggStep: {
sqlite3VdbeMemInit(&t, db, MEM_Null);
ctx.pOut = &t;
ctx.isError = 0;
ctx.pColl = 0;
ctx.pVdbe = p;
ctx.iOp = pc;
ctx.skipFlag = 0;
if( ctx.pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
assert( pOp>p->aOp );
assert( pOp[-1].p4type==P4_COLLSEQ );
assert( pOp[-1].opcode==OP_CollSeq );
ctx.pColl = pOp[-1].p4.pColl;
}
(ctx.pFunc->xStep)(&ctx, n, apVal); /* IMP: R-24505-23230 */
if( ctx.isError ){
sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&t));

View File

@ -272,7 +272,6 @@ struct sqlite3_context {
Mem *pOut; /* The return value is stored here */
FuncDef *pFunc; /* Pointer to function information */
Mem *pMem; /* Memory cell used to store aggregate context */
CollSeq *pColl; /* Collating sequence */
Vdbe *pVdbe; /* The VM that owns this context */
int iOp; /* Instruction number of OP_Function */
int isError; /* Error code returned by the function. */

View File

@ -4359,11 +4359,14 @@ static int whereLoopAddBtreeIndex(
nIter = pProbe->aiRowLogEst[saved_nEq] - pProbe->aiRowLogEst[saved_nEq+1];
if( pTerm ){
/* TUNING: When estimating skip-scan for a term that is also indexable,
** increase the cost of the skip-scan by 2x, to make it a little less
** multiply the cost of the skip-scan by 2.0, to make it a little less
** desirable than the regular index lookup. */
nIter += 10; assert( 10==sqlite3LogEst(2) );
}
pNew->nOut -= nIter;
/* TUNING: Because uncertainties in the estimates for skip-scan queries,
** add a 1.375 fudge factor to make skip-scan slightly less likely. */
nIter += 5;
whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nIter + nInMul);
pNew->nOut = saved_nOut;
pNew->u.btree.nEq = saved_nEq;
@ -4718,9 +4721,17 @@ static int whereLoopAddBtree(
pNew->nLTerm = 1;
pNew->aLTerm[0] = pTerm;
/* TUNING: One-time cost for computing the automatic index is
** approximately 7*N*log2(N) where N is the number of rows in
** the table being indexed. */
pNew->rSetup = rLogSize + rSize + 28; assert( 28==sqlite3LogEst(7) );
** estimated to be X*N*log2(N) where N is the number of rows in
** the table being indexed and where X is 7 (LogEst=28) for normal
** tables or 1.375 (LogEst=4) for views and subqueries. The value
** of X is smaller for views and subqueries so that the query planner
** will be more aggressive about generating automatic indexes for
** those objects, since there is no opportunity to add schema
** indexes on subqueries and views. */
pNew->rSetup = rLogSize + rSize + 4;
if( pTab->pSelect==0 && (pTab->tabFlags & TF_Ephemeral)==0 ){
pNew->rSetup += 24;
}
ApplyCostMultiplier(pNew->rSetup, pTab->costMult);
/* TUNING: Each index lookup yields 20 rows in the table. This
** is more than the usual guess of 10 rows, since we have no way

View File

@ -413,4 +413,101 @@ do_execsql_test autoindex1-801 {
WHERE mimetypes._id=10 AND data14 IS NOT NULL;
} {/SEARCH TABLE data .*SEARCH TABLE raw_contacts/}
# Another test case from an important user of SQLite. The key feature of
# this test is that the "aggindex" subquery should make use of an
# automatic index. If it does, the query is fast. If it does not, the
# query is deathly slow. It worked OK in 3.7.17 but started going slow
# with version 3.8.0. The problem was fixed for 3.8.7 by reducing the
# cost estimate for automatic indexes on views and subqueries.
#
db close
forcedelete test.db
sqlite3 db test.db
do_execsql_test autoindex1-900 {
CREATE TABLE messages (ROWID INTEGER PRIMARY KEY AUTOINCREMENT, message_id, document_id BLOB, in_reply_to, remote_id INTEGER, sender INTEGER, subject_prefix, subject INTEGER, date_sent INTEGER, date_received INTEGER, date_created INTEGER, date_last_viewed INTEGER, mailbox INTEGER, remote_mailbox INTEGER, original_mailbox INTEGER, flags INTEGER, read, flagged, size INTEGER, color, encoding, type INTEGER, pad, conversation_id INTEGER DEFAULT -1, snippet TEXT DEFAULT NULL, fuzzy_ancestor INTEGER DEFAULT NULL, automated_conversation INTEGER DEFAULT 0, root_status INTEGER DEFAULT -1, conversation_position INTEGER DEFAULT -1);
CREATE INDEX date_index ON messages(date_received);
CREATE INDEX date_last_viewed_index ON messages(date_last_viewed);
CREATE INDEX date_created_index ON messages(date_created);
CREATE INDEX message_message_id_mailbox_index ON messages(message_id, mailbox);
CREATE INDEX message_document_id_index ON messages(document_id);
CREATE INDEX message_read_index ON messages(read);
CREATE INDEX message_flagged_index ON messages(flagged);
CREATE INDEX message_mailbox_index ON messages(mailbox, date_received);
CREATE INDEX message_remote_mailbox_index ON messages(remote_mailbox, remote_id);
CREATE INDEX message_type_index ON messages(type);
CREATE INDEX message_conversation_id_conversation_position_index ON messages(conversation_id, conversation_position);
CREATE INDEX message_fuzzy_ancestor_index ON messages(fuzzy_ancestor);
CREATE INDEX message_subject_fuzzy_ancestor_index ON messages(subject, fuzzy_ancestor);
CREATE INDEX message_sender_subject_automated_conversation_index ON messages(sender, subject, automated_conversation);
CREATE INDEX message_sender_index ON messages(sender);
CREATE INDEX message_root_status ON messages(root_status);
CREATE TABLE subjects (ROWID INTEGER PRIMARY KEY, subject COLLATE RTRIM, normalized_subject COLLATE RTRIM);
CREATE INDEX subject_subject_index ON subjects(subject);
CREATE INDEX subject_normalized_subject_index ON subjects(normalized_subject);
CREATE TABLE addresses (ROWID INTEGER PRIMARY KEY, address COLLATE NOCASE, comment, UNIQUE(address, comment));
CREATE INDEX addresses_address_index ON addresses(address);
CREATE TABLE mailboxes (ROWID INTEGER PRIMARY KEY, url UNIQUE, total_count INTEGER DEFAULT 0, unread_count INTEGER DEFAULT 0, unseen_count INTEGER DEFAULT 0, deleted_count INTEGER DEFAULT 0, unread_count_adjusted_for_duplicates INTEGER DEFAULT 0, change_identifier, source INTEGER, alleged_change_identifier);
CREATE INDEX mailboxes_source_index ON mailboxes(source);
CREATE TABLE labels (ROWID INTEGER PRIMARY KEY, message_id INTEGER NOT NULL, mailbox_id INTEGER NOT NULL, UNIQUE(message_id, mailbox_id));
CREATE INDEX labels_message_id_mailbox_id_index ON labels(message_id, mailbox_id);
CREATE INDEX labels_mailbox_id_index ON labels(mailbox_id);
explain query plan
SELECT messages.ROWID,
messages.message_id,
messages.remote_id,
messages.date_received,
messages.date_sent,
messages.flags,
messages.size,
messages.color,
messages.date_last_viewed,
messages.subject_prefix,
subjects.subject,
sender.comment,
sender.address,
NULL,
messages.mailbox,
messages.original_mailbox,
NULL,
NULL,
messages.type,
messages.document_id,
sender,
NULL,
messages.conversation_id,
messages.conversation_position,
agglabels.labels
FROM mailboxes AS mailbox
JOIN messages ON mailbox.ROWID = messages.mailbox
LEFT OUTER JOIN subjects ON messages.subject = subjects.ROWID
LEFT OUTER JOIN addresses AS sender ON messages.sender = sender.ROWID
LEFT OUTER JOIN (
SELECT message_id, group_concat(mailbox_id) as labels
FROM labels GROUP BY message_id
) AS agglabels ON messages.ROWID = agglabels.message_id
WHERE (mailbox.url = 'imap://email.app@imap.gmail.com/%5BGmail%5D/All%20Mail')
AND (messages.ROWID IN (
SELECT labels.message_id
FROM labels JOIN mailboxes ON labels.mailbox_id = mailboxes.ROWID
WHERE mailboxes.url = 'imap://email.app@imap.gmail.com/INBOX'))
AND messages.mailbox in (6,12,18,24,30,36,42,1,7,13,19,25,31,37,43,2,8,
14,20,26,32,38,3,9,15,21,27,33,39,4,10,16,22,28,
34,40,5,11,17,23,35,41)
ORDER BY date_received DESC;
} {/agglabels USING AUTOMATIC COVERING INDEX/}
# A test case for VIEWs
#
do_execsql_test autoindex1-901 {
CREATE TABLE t1(x INTEGER PRIMARY KEY, y, z);
CREATE TABLE t2(a, b);
CREATE VIEW agg2 AS SELECT a, sum(b) AS m FROM t2 GROUP BY a;
EXPLAIN QUERY PLAN
SELECT t1.z, agg2.m
FROM t1 JOIN agg2 ON t1.y=agg2.m
WHERE t1.x IN (1,2,3);
} {/USING AUTOMATIC COVERING INDEX/}
finish_test

View File

@ -99,4 +99,32 @@ do_execsql_test default-3.3 {
SELECT * FROM t300;
} {2147483647 2147483648 9223372036854775807 -2147483647 -2147483648 -9223372036854775808 9.22337203685478e+18 9223372036854775807}
# Do now allow bound parameters in new DEFAULT values.
# Silently convert bound parameters to NULL in DEFAULT causes
# in the sqlite_master table, for backwards compatibility.
#
db close
forcedelete test.db
sqlite3 db test.db
do_execsql_test default-4.0 {
CREATE TABLE t1(a TEXT, b TEXT DEFAULT(99));
PRAGMA writable_schema=ON;
UPDATE sqlite_master SET sql='CREATE TABLE t1(a TEXT, b TEXT DEFAULT(:xyz))';
} {}
db close
sqlite3 db test.db
do_execsql_test default-4.1 {
INSERT INTO t1(a) VALUES('xyzzy');
SELECT a, quote(b) FROM t1;
} {xyzzy NULL}
do_catchsql_test default-4.2 {
CREATE TABLE t2(a TEXT, b TEXT DEFAULT(:xyz));
} {1 {default value of column [b] is not constant}}
do_catchsql_test default-4.3 {
CREATE TABLE t2(a TEXT, b TEXT DEFAULT(abs(:xyz)));
} {1 {default value of column [b] is not constant}}
do_catchsql_test default-4.4 {
CREATE TABLE t2(a TEXT, b TEXT DEFAULT(98+coalesce(5,:xyz)));
} {1 {default value of column [b] is not constant}}
finish_test

114
test/multiplex4.test Normal file
View File

@ -0,0 +1,114 @@
# 2014-09-25
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file contains tests for the "truncate" option in the multiplexor.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix multiplex4
db close
sqlite3_shutdown
sqlite3_multiplex_initialize {} 0
# delete all filesl with the base name of $basename
#
proc multiplex_delete_db {basename} {
foreach file [glob -nocomplain $basename.*] {
forcedelete $file
}
}
# Return a sorted list of all files with the base name of $basename.
# Except, delete all text from the end of $basename through the NNN
# suffix on the end of the filename.
#
proc multiplex_file_list {basename} {
set x {}
foreach file [glob -nocomplain $basename.*] {
regsub "^$basename\\..*(\\d\\d\\d)\$" $file $basename.\\1 file
lappend x $file
}
return [lsort $x]
}
do_test multiplex4-1.0 {
multiplex_delete_db mx4test
sqlite3 db {file:mx4test.db?chunksize=10&truncate=1} -uri 1 -vfs multiplex
db eval {
CREATE TABLE t1(x);
INSERT INTO t1(x) VALUES(randomblob(250000));
}
multiplex_file_list mx4test
} {mx4test.001 mx4test.db}
do_test multiplex4-1.1 {
db eval {
DELETE FROM t1;
VACUUM;
}
multiplex_file_list mx4test
} {mx4test.db}
do_test multiplex4-1.2 {
db eval {PRAGMA multiplex_truncate}
} {on}
do_test multiplex4-1.3 {
db eval {PRAGMA multiplex_truncate=off}
} {off}
do_test multiplex4-1.4 {
db eval {PRAGMA multiplex_truncate}
} {off}
do_test multiplex4-1.5 {
db eval {PRAGMA multiplex_truncate=on}
} {on}
do_test multiplex4-1.6 {
db eval {PRAGMA multiplex_truncate}
} {on}
do_test multiplex4-1.7 {
db eval {PRAGMA multiplex_truncate=0}
} {off}
do_test multiplex4-1.8 {
db eval {PRAGMA multiplex_truncate=1}
} {on}
do_test multiplex4-1.9 {
db eval {PRAGMA multiplex_truncate=0}
} {off}
do_test multiplex4-1.10 {
db eval {
INSERT INTO t1(x) VALUES(randomblob(250000));
}
multiplex_file_list mx4test
} {mx4test.001 mx4test.db}
do_test multiplex4-1.11 {
db eval {
DELETE FROM t1;
VACUUM;
}
multiplex_file_list mx4test
} {mx4test.001 mx4test.db}
do_test multiplex4-1.12 {
db eval {
PRAGMA multiplex_truncate=ON;
DROP TABLE t1;
VACUUM;
}
multiplex_file_list mx4test
} {mx4test.db}
catch { db close }
forcedelete mx4test.db
sqlite3_multiplex_shutdown
finish_test

View File

@ -679,9 +679,9 @@ do_test rowid-12.2 {
save_prng_state
execsql {
INSERT INTO t7 VALUES(NULL,'b');
SELECT x, y FROM t7;
SELECT x, y FROM t7 ORDER BY x;
}
} {1 b 9223372036854775807 a}
} {/\d+ b 9223372036854775807 a/}
execsql {INSERT INTO t7 VALUES(2,'y');}
for {set i 1} {$i<100} {incr i} {
do_test rowid-12.3.$i {
@ -701,5 +701,19 @@ do_test rowid-12.4 {
}
} {1 {database or disk is full}}
# INSERTs that happen inside of nested function calls are recorded
# by last_insert_rowid.
#
proc rowid_addrow_func {n} {
db eval {INSERT INTO t13(rowid,x) VALUES($n,$n*$n)}
return [db last_insert_rowid]
}
db function addrow rowid_addrow_func
do_execsql_test rowid-13.1 {
CREATE TABLE t13(x);
INSERT INTO t13(rowid,x) VALUES(1234,5);
SELECT rowid, x, addrow(rowid+1000), '|' FROM t13 LIMIT 3;
SELECT last_insert_rowid();
} {1234 5 2234 | 2234 4990756 3234 | 3234 10458756 4234 | 4234}
finish_test

View File

@ -245,6 +245,32 @@ do_execsql_test skipscan1-5.3 {
SELECT xh, loc FROM t5 WHERE loc >= 'M' AND loc < 'N';
} {/.*COVERING INDEX t5i1 .*/}
# The column used by the skip-scan needs to be sufficiently selective.
# See the private email from Adi Zaimi to drh@sqlite.org on 2014-09-22.
#
db close
forcedelete test.db
sqlite3 db test.db
do_execsql_test skipscan1-6.1 {
CREATE TABLE t1(a,b,c,d,e,f,g,h varchar(300));
CREATE INDEX t1ab ON t1(a,b);
ANALYZE sqlite_master;
-- Only two distinct values for the skip-scan column. Skip-scan is not used.
INSERT INTO sqlite_stat1 VALUES('t1','t1ab','500000 250000 125000');
ANALYZE sqlite_master;
EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=1;
} {~/ANY/}
do_execsql_test skipscan1-6.2 {
-- Four distinct values for the skip-scan column. Skip-scan is used.
UPDATE sqlite_stat1 SET stat='500000 250000 62500';
ANALYZE sqlite_master;
EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=1;
} {/ANY.a. AND b=/}
do_execsql_test skipscan1-6.3 {
-- Two distinct values for the skip-scan column again. Skip-scan is not used.
UPDATE sqlite_stat1 SET stat='500000 125000 62500';
ANALYZE sqlite_master;
EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=1;
} {~/ANY/}
finish_test

View File

@ -108,7 +108,7 @@ foreach {tn dbenc coll} {
3 { c > 'q' } {/*ANY(a) AND ANY(b) AND c>?*/}
4 { c > 'e' } {/*SCAN TABLE t2*/}
5 { c < 'q' } {/*SCAN TABLE t2*/}
4 { c < 'e' } {/*ANY(a) AND ANY(b) AND c<?*/}
6 { c < 'c' } {/*ANY(a) AND ANY(b) AND c<?*/}
} {
set sql "EXPLAIN QUERY PLAN SELECT * FROM t2 WHERE $q"
do_execsql_test 2.$tn.$tn2 $sql $res
@ -180,7 +180,3 @@ foreach {tn q res} {
}
finish_test