Merge recent trunk changes (performance enhancements) into the sessions
branch. FossilOrigin-Name: 497367cb57345dd37793e5f369b34d12be56172e
This commit is contained in:
commit
6382b6dc71
49
manifest
49
manifest
@ -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
|
||||
|
@ -1 +1 @@
|
||||
09985fa6b60a0bf38e23bbccd4d8e1d1cbf66124
|
||||
497367cb57345dd37793e5f369b34d12be56172e
|
416
src/btree.c
416
src/btree.c
@ -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.
|
||||
*/
|
||||
|
@ -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 */
|
||||
};
|
||||
|
||||
|
@ -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{
|
||||
|
@ -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;
|
||||
}
|
||||
|
44
src/expr.c
44
src/expr.c
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
216
src/func.c
216
src/func.c
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 ){
|
||||
|
@ -102,6 +102,7 @@ const char sqlite3IsEbcdicIdChar[] = {
|
||||
};
|
||||
#define IdChar(C) (((c=C)>=0x42 && sqlite3IsEbcdicIdChar[c-0x40]))
|
||||
#endif
|
||||
int sqlite3IsIdChar(u8 c){ return IdChar(c); }
|
||||
|
||||
|
||||
/*
|
||||
|
45
src/vdbe.c
45
src/vdbe.c
@ -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));
|
||||
|
@ -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. */
|
||||
|
19
src/where.c
19
src/where.c
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
114
test/multiplex4.test
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user