Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| da0f75eb00 | |||
| 39c4b82b5a | |||
| 7682a476f6 | |||
| 843e4cdea9 | |||
| 867db83159 | |||
| 3b130beb15 | |||
| d863346194 | |||
| a15cc47f81 | |||
| 2c4dc635a1 | |||
| 9fdfdc893b | |||
| 88b3322f27 | |||
| 97348b37c2 | |||
| 328d913cbd | |||
| 3fbb022b98 | |||
| b2325b72df | |||
| feada2df39 | |||
| 9bfdc25062 | |||
| 3f3874030c | |||
| 3e28ff5cb5 | |||
| c46838309c | |||
| 6200c88123 | |||
| ab1cc58b70 | |||
| 3b8fea9ec6 | |||
| 14f0e2128a | |||
| a4bb5b0726 | |||
| dc41d60aaa | |||
| 7e07433fe7 | |||
| d9f9441d7d | |||
| 6656630b63 |
@@ -1,5 +1,5 @@
|
||||
C Add\stest\scases\sfor\sticket\s[89398880bcfff9].
|
||||
D 2014-09-21T20:31:26.194
|
||||
C Add\sthe\sOPFLAG_MULTICOLUMN\sflat\sto\sthe\sOP_Column\sopcode.\s\sRearrange\sOP_Column\ninstrunctions\sto\stake\sadvantage\sof\sthe\snew\sflag\sfor\sa\ssmall\sperformance\s\nincrease
|
||||
D 2014-09-29T18:47:20.017
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in cf57f673d77606ab0f2d9627ca52a9ba1464146a
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@@ -172,19 +172,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 ede8348a7d623257ee6c06ca4796ceaee13b8657
|
||||
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 fae81cc2eb14b75267d4f47d3cfc9ae02aae726f
|
||||
F src/expr.c 4f101c8ddc6d5a22303c88278069f5261562a9a8
|
||||
F src/expr.c 622ca88bb258292690a550ca0c290ec7b071bed4
|
||||
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
|
||||
@@ -212,7 +212,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
|
||||
@@ -227,12 +227,12 @@ F src/printf.c 3a47f526b173813d9a7f4e7044007771ba68cde1
|
||||
F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece
|
||||
F src/resolve.c a3466128b52a86c466e47ac1a19e2174f7b5cf89
|
||||
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
|
||||
F src/select.c a83ed8bc2a31c131e3addb6f0488b68334085e7b
|
||||
F src/select.c 6e7d7a277307ebee1a284d8811a8a3351ffc39b2
|
||||
F src/shell.c dad23987c34faddb061a339da3e92e05ccc6935e
|
||||
F src/sqlite.h.in 8b018219ce988913e5977d5de9ab4beb33be23b6
|
||||
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
|
||||
F src/sqlite3ext.h 17d487c3c91b0b8c584a32fbeb393f6f795eea7d
|
||||
F src/sqliteInt.h 5ecde2191721a94cdce0d5248e26a373e0b17a70
|
||||
F src/sqliteInt.h ba443d9af2cef399e2ec641d44d005212b1476ef
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
|
||||
F src/table.c 2e99ef7ef16187e17033d9398dc962ce22dab5cb
|
||||
@@ -262,7 +262,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
|
||||
@@ -283,17 +283,17 @@ 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 729f6f18fc27740591d085e1172cebe311144bf0
|
||||
F src/update.c dcc1733c8f9d455a3127e2b151a211fa7f9ef053
|
||||
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
|
||||
F src/util.c 4006c01772bd8d8ac4306d523bbcee41d3e392d8
|
||||
F src/vacuum.c 59f03f92bcff57faa6a8ca256eb29ccddfb0614a
|
||||
F src/vdbe.c de1af1795bebdad20c23e82bafa2f531e9893198
|
||||
F src/vdbe.h 09f5b4e3719fa454f252322b1cdab5cf1f361327
|
||||
F src/vdbeInt.h f177bed1ec8d4eb5c7089f012aeb95f374745735
|
||||
F src/vdbe.c 89636b004debc955452f6e0f26dfcc13cc96676b
|
||||
F src/vdbe.h d5825ae515ab108331739bd4910eb66dd0eb33f0
|
||||
F src/vdbeInt.h bb7f7ecfdead1a2ae0251b59f86f5724838d975c
|
||||
F src/vdbeapi.c e9e33b59834e3edc8790209765e069874c269d9d
|
||||
F src/vdbeaux.c a05adc3c96abdaf3db14768ddd63132fc9678060
|
||||
F src/vdbeaux.c f8df1050cb0e5f7d537bda4dc6caa44d44ab87bc
|
||||
F src/vdbeblob.c 848238dc73e93e48432991bb5651bf87d865eca4
|
||||
F src/vdbemem.c 1e105dacf5190fc85a8ec2107c0dcc1884e75099
|
||||
F src/vdbesort.c 5c1bacf90578d22b630fbf6ed98ccf60d83435ef
|
||||
@@ -302,7 +302,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
|
||||
@@ -341,7 +341,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
|
||||
@@ -438,7 +438,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
|
||||
@@ -469,7 +469,7 @@ F test/enc3.test 90683ad0e6ea587b9d5542ca93568af9a9858c40
|
||||
F test/enc4.test c8f1ce3618508fd0909945beb8b8831feef2c020
|
||||
F test/eqp.test 85873fa5816c48915c82c4e74cb5c35a5b48160f
|
||||
F test/errmsg.test f31592a594b44ee121371d25ddd5d63497bb3401
|
||||
F test/eval.test bc269c365ba877554948441e91ad5373f9f91be3
|
||||
F test/eval.test a64c9105d6ff163df7cf09d6ac29cdad5922078c
|
||||
F test/exclusive.test c7ebbc756eacf544c108b15eed64d7d4e5f86b75
|
||||
F test/exclusive2.test 32798111aae78a5deec980eee383213f189df308
|
||||
F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7
|
||||
@@ -729,6 +729,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
|
||||
@@ -783,7 +784,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
|
||||
@@ -836,10 +837,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
|
||||
@@ -1199,7 +1200,10 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P 28d52c1c38d849f099bc777f5987d1ef89680c2a
|
||||
R df45958fe8f93552efb788581c98f0f7
|
||||
P 7fb1626866c2f8dad84c7e6184824be3efd71ca2
|
||||
R 58d61b9e8ed6b972c827aa4b2de6a9d8
|
||||
T *branch * faster-OP_Column
|
||||
T *sym-faster-OP_Column *
|
||||
T -sym-trunk *
|
||||
U drh
|
||||
Z e77b30925e1036bea887cc5097672af2
|
||||
Z bc7d4115a8f75585e68aa7b7bc067fe4
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
9683e001ed38b41979220eef0bdfcb54df5f3191
|
||||
5e5d6e8680b253b4de5c04df155c27f15fedc0c1
|
||||
+240
-179
@@ -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
|
||||
@@ -4981,8 +4985,7 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur, int *pRes){
|
||||
assert( (pCur->curFlags & (BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey))==0 );
|
||||
assert( pCur->info.nSize==0 );
|
||||
if( pCur->eState!=CURSOR_VALID ){
|
||||
assert( pCur->eState>=CURSOR_REQUIRESEEK );
|
||||
rc = btreeRestoreCursorPosition(pCur);
|
||||
rc = restoreCursorPosition(pCur);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
@@ -5287,7 +5290,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 +5323,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 +5522,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 +5540,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 +5624,6 @@ static int fillInCell(
|
||||
BtShared *pBt = pPage->pBt;
|
||||
Pgno pgnoOvfl = 0;
|
||||
int nHeader;
|
||||
CellInfo info;
|
||||
|
||||
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
|
||||
|
||||
@@ -5624,23 +5633,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 +5652,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 +6408,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 +6984,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 +7103,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 +7137,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 +7157,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 +7175,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 +7237,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 +7286,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 +7304,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 +7517,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 +7533,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 +7879,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 +7891,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 +7931,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 +7955,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 +7964,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 +7984,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 +7993,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 +8008,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 +8020,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 +8036,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 +8068,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 +8078,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 +8105,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 +8122,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 +8146,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 +8158,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 +8171,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 +8212,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 +8224,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 +8242,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 +8265,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 +8323,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 +8345,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 +8356,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 +8369,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 +8377,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 +8391,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 +8587,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.
|
||||
*/
|
||||
|
||||
+10
-9
@@ -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 */
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -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{
|
||||
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
|
||||
+34
-11
@@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2359,6 +2381,7 @@ void sqlite3ExprCodeGetColumnOfTable(
|
||||
}
|
||||
if( iCol>=0 ){
|
||||
sqlite3ColumnDefault(v, pTab, iCol, regOut);
|
||||
sqlite3VdbeOptimizeColumnOpcodes(v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+124
-92
@@ -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;
|
||||
}
|
||||
|
||||
+18
-10
@@ -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
|
||||
|
||||
|
||||
+4
-3
@@ -706,7 +706,7 @@ static void selectInnerLoop(
|
||||
pDest->nSdst = nResultCol;
|
||||
regResult = pDest->iSdst;
|
||||
if( srcTab>=0 ){
|
||||
for(i=0; i<nResultCol; i++){
|
||||
for(i=nResultCol-1; i>=0; i--){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, srcTab, i, regResult+i);
|
||||
VdbeComment((v, "%s", pEList->a[i].zName));
|
||||
}
|
||||
@@ -1225,10 +1225,11 @@ static void generateSortTail(
|
||||
p5 = 0;
|
||||
bSeq = 1;
|
||||
}
|
||||
for(i=0; i<nSortData; i++){
|
||||
for(i=nSortData-1; i>=0; i--){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iSortTab, nKey+bSeq+i, regRow+i);
|
||||
if( i==0 ) sqlite3VdbeChangeP5(v, p5);
|
||||
sqlite3VdbeChangeP5(v, p5);
|
||||
VdbeComment((v, "%s", aOutEx[i].zName ? aOutEx[i].zName : aOutEx[i].zSpan));
|
||||
p5 = 0;
|
||||
}
|
||||
switch( eDest ){
|
||||
case SRT_Table:
|
||||
|
||||
+4
-1
@@ -2654,6 +2654,7 @@ struct AuthContext {
|
||||
** Bitfield flags for P5 value in various opcodes.
|
||||
*/
|
||||
#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 */
|
||||
@@ -2664,6 +2665,7 @@ struct AuthContext {
|
||||
#define OPFLAG_BULKCSR 0x01 /* OP_Open** used to open bulk cursor */
|
||||
#define OPFLAG_P2ISREG 0x02 /* P2 to OP_Open** is a register number */
|
||||
#define OPFLAG_PERMUTE 0x01 /* OP_Compare: use the permutation */
|
||||
#define OPFLAG_MULTICOLUMN 0x10 /* OP_Column followed by another */
|
||||
|
||||
/*
|
||||
* Each trigger present in the database schema is stored as an instance of
|
||||
@@ -2987,6 +2989,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
|
||||
@@ -3285,7 +3288,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); }
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@@ -73,6 +73,7 @@ void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){
|
||||
}
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){
|
||||
sqlite3VdbeOptimizeColumnOpcodes(v);
|
||||
sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg);
|
||||
}
|
||||
#endif
|
||||
|
||||
+42
-40
@@ -1557,17 +1557,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 ){
|
||||
@@ -2273,9 +2266,11 @@ case OP_Column: {
|
||||
u32 avail; /* Number of bytes of available data */
|
||||
u32 t; /* A type code from the record header */
|
||||
u16 fx; /* pDest->flags value */
|
||||
u8 p5;
|
||||
Mem *pReg; /* PseudoTable input register */
|
||||
|
||||
p2 = pOp->p2;
|
||||
p5 = 0;
|
||||
assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
|
||||
pDest = &aMem[pOp->p3];
|
||||
memAboutToChange(p, pDest);
|
||||
@@ -2443,22 +2438,32 @@ case OP_Column: {
|
||||
}
|
||||
}
|
||||
|
||||
/* Extract the content for the p2+1-th column. Control can only
|
||||
** reach this point if aOffset[p2], aOffset[p2+1], and pC->aType[p2] are
|
||||
** all valid.
|
||||
/* At this point, all of the following values are set:
|
||||
**
|
||||
** p2 Index of column to extract. pOp->p2.
|
||||
** pDest Memory register into which to write result
|
||||
** pC->aRow Binary row content from the btree. Might be incomplete
|
||||
** pC->szRow Number of bytes in pC->szRow
|
||||
** aOffset[p2] Offset into the binary row where P2-th column starts
|
||||
** aOffste[p2+1] Offset into binary row of first byte past P2-th column
|
||||
** pC->aType[p2] Datatype of the P2-th column (as stored on disk)
|
||||
**
|
||||
** Extract the content for column number p2.
|
||||
*/
|
||||
op_column_decode:
|
||||
assert( p2<pC->nHdrParsed );
|
||||
assert( rc==SQLITE_OK );
|
||||
assert( sqlite3VdbeCheckMemInvariants(pDest) );
|
||||
if( VdbeMemDynamic(pDest) ) sqlite3VdbeMemSetNull(pDest);
|
||||
t = pC->aType[p2];
|
||||
p5 = pOp->p5;
|
||||
if( pC->szRow>=aOffset[p2+1] ){
|
||||
/* This is the common case where the desired content fits on the original
|
||||
** page - where the content is not on an overflow page */
|
||||
sqlite3VdbeSerialGet(pC->aRow+aOffset[p2], t, pDest);
|
||||
}else{
|
||||
/* This branch happens only when content is on overflow pages */
|
||||
if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0
|
||||
if( ((p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0
|
||||
&& ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0))
|
||||
|| (len = sqlite3VdbeSerialTypeLen(t))==0
|
||||
){
|
||||
@@ -2502,6 +2507,23 @@ op_column_out:
|
||||
op_column_error:
|
||||
UPDATE_MAX_BLOBSIZE(pDest);
|
||||
REGISTER_TRACE(pOp->p3, pDest);
|
||||
|
||||
/* If the OPFLAG_MULTICOLUMN bit is set on P5, that means that this
|
||||
** OP_Column is immediately followed by another OP_Column with the same
|
||||
** P1 and a P2 that is no larger than the current P2. In that case,
|
||||
** process the following OP_Column as part of this instruction, without
|
||||
** returning to the main instruction dispatch loop.
|
||||
*/
|
||||
if( (p5 & OPFLAG_MULTICOLUMN)!=0 && rc==0 ){
|
||||
pc++;
|
||||
assert( pOp[1].opcode==OP_Column );
|
||||
assert( pOp[1].p1==pOp[0].p1 );
|
||||
assert( pOp[1].p2<=pOp[0].p2 );
|
||||
pOp++;
|
||||
p2 = pOp->p2;
|
||||
pDest = &aMem[pOp->p3];
|
||||
goto op_column_decode;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3287,10 +3309,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
|
||||
@@ -4024,25 +4042,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;
|
||||
@@ -5638,14 +5645,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));
|
||||
|
||||
@@ -179,6 +179,7 @@ void sqlite3VdbeChangeP5(Vdbe*, u8 P5);
|
||||
void sqlite3VdbeJumpHere(Vdbe*, int addr);
|
||||
void sqlite3VdbeChangeToNoop(Vdbe*, int addr);
|
||||
int sqlite3VdbeDeletePriorOpcode(Vdbe*, u8 op);
|
||||
void sqlite3VdbeOptimizeColumnOpcodes(Vdbe*);
|
||||
void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N);
|
||||
void sqlite3VdbeSetP4KeyInfo(Parse*, Index*);
|
||||
void sqlite3VdbeUsesBtree(Vdbe*, int);
|
||||
|
||||
@@ -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. */
|
||||
|
||||
+35
-1
@@ -502,6 +502,15 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
|
||||
pOp->p4type = P4_ADVANCE;
|
||||
break;
|
||||
}
|
||||
case OP_Column: {
|
||||
if( pOp[1].opcode==OP_Column
|
||||
&& pOp[1].p1==pOp->p1
|
||||
&& pOp[1].p2<=pOp->p2
|
||||
){
|
||||
pOp->p5 |= OPFLAG_MULTICOLUMN;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pOp->opflags = sqlite3OpcodeProperty[opcode];
|
||||
@@ -752,7 +761,8 @@ void sqlite3VdbeChangeToNoop(Vdbe *p, int addr){
|
||||
}
|
||||
|
||||
/*
|
||||
** Remove the last opcode inserted
|
||||
** If the last opcode is "op" and it is not a jump destination,
|
||||
** then remove it. Return true if and only if an opcode was removed.
|
||||
*/
|
||||
int sqlite3VdbeDeletePriorOpcode(Vdbe *p, u8 op){
|
||||
if( (p->nOp-1)>(p->pParse->iFixedOp) && p->aOp[p->nOp-1].opcode==op ){
|
||||
@@ -763,6 +773,30 @@ int sqlite3VdbeDeletePriorOpcode(Vdbe *p, u8 op){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** When running multiple OP_Column opcodes in a row, it is advantagous
|
||||
** to run the one with the largest P2 value (the largest column number)
|
||||
** first. This routine checks the last few OP_Column opcodes and
|
||||
** might reorder them so that a larger P2 value opertion occurs at
|
||||
** the start of the list.
|
||||
*/
|
||||
void sqlite3VdbeOptimizeColumnOpcodes(Vdbe *p){
|
||||
VdbeOp *aOp, tempOp;
|
||||
int i;
|
||||
aOp = p->aOp;
|
||||
i = p->nOp-2;
|
||||
while( i>p->pParse->iFixedOp
|
||||
&& aOp[i+1].opcode==OP_Column
|
||||
&& aOp[i].opcode==OP_Column
|
||||
&& aOp[i].p1==aOp[i+1].p1
|
||||
&& aOp[i].p2<aOp[i+1].p2 ){
|
||||
tempOp = aOp[i];
|
||||
aOp[i] = aOp[i+1];
|
||||
aOp[i+1] = tempOp;
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Change the value of the P4 operand for a specific instruction.
|
||||
** This routine is useful when a large program is loaded from a
|
||||
|
||||
+15
-4
@@ -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
|
||||
|
||||
@@ -58,6 +58,18 @@ do_test eval-2.2 {
|
||||
SELECT * FROM t2
|
||||
}
|
||||
} {}
|
||||
do_test eval-2.3 {
|
||||
execsql {
|
||||
INSERT INTO t2 SELECT x, x+1 FROM t1 WHERE x<5;
|
||||
SELECT x, test_eval('DELETE FROM t2 WHERE x='||x), y FROM t2
|
||||
ORDER BY rowid DESC;
|
||||
}
|
||||
} {4 {} {} 3 {} {} 2 {} {} 1 {} {}}
|
||||
do_test eval-2.4 {
|
||||
execsql {
|
||||
SELECT * FROM t2
|
||||
}
|
||||
} {}
|
||||
|
||||
# Modify a row while it is being read.
|
||||
#
|
||||
|
||||
@@ -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
|
||||
+16
-2
@@ -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
|
||||
|
||||
+27
-1
@@ -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
|
||||
|
||||
+1
-5
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user