Merge latest trunk changes into this branch.
FossilOrigin-Name: fcd8f7ce601729dc51d880d16b97040c1be16aa2
This commit is contained in:
commit
f32fa3116b
@ -2655,6 +2655,31 @@ static int spellfix1Rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called by the xUpdate() method. It returns a string
|
||||
** containing the conflict mode that xUpdate() should use for the current
|
||||
** operation. One of: "ROLLBACK", "IGNORE", "ABORT" or "REPLACE".
|
||||
*/
|
||||
static const char *spellfix1GetConflict(sqlite3 *db){
|
||||
static const char *azConflict[] = {
|
||||
/* Note: Instead of "FAIL" - "ABORT". */
|
||||
"ROLLBACK", "IGNORE", "ABORT", "ABORT", "REPLACE"
|
||||
};
|
||||
int eConflict = sqlite3_vtab_on_conflict(db);
|
||||
|
||||
assert( eConflict==SQLITE_ROLLBACK || eConflict==SQLITE_IGNORE
|
||||
|| eConflict==SQLITE_FAIL || eConflict==SQLITE_ABORT
|
||||
|| eConflict==SQLITE_REPLACE
|
||||
);
|
||||
assert( SQLITE_ROLLBACK==1 );
|
||||
assert( SQLITE_IGNORE==2 );
|
||||
assert( SQLITE_FAIL==3 );
|
||||
assert( SQLITE_ABORT==4 );
|
||||
assert( SQLITE_REPLACE==5 );
|
||||
|
||||
return azConflict[eConflict-1];
|
||||
}
|
||||
|
||||
/*
|
||||
** The xUpdate() method.
|
||||
*/
|
||||
@ -2686,6 +2711,7 @@ static int spellfix1Update(
|
||||
char *zK1, *zK2;
|
||||
int i;
|
||||
char c;
|
||||
const char *zConflict = spellfix1GetConflict(db);
|
||||
|
||||
if( zWord==0 ){
|
||||
/* Inserts of the form: INSERT INTO table(command) VALUES('xyzzy');
|
||||
@ -2746,10 +2772,10 @@ static int spellfix1Update(
|
||||
}else{
|
||||
newRowid = sqlite3_value_int64(argv[1]);
|
||||
spellfix1DbExec(&rc, db,
|
||||
"INSERT INTO \"%w\".\"%w_vocab\"(id,rank,langid,word,k1,k2) "
|
||||
"VALUES(%lld,%d,%d,%Q,%Q,%Q)",
|
||||
p->zDbName, p->zTableName,
|
||||
newRowid, iRank, iLang, zWord, zK1, zK2
|
||||
"INSERT OR %s INTO \"%w\".\"%w_vocab\"(id,rank,langid,word,k1,k2) "
|
||||
"VALUES(%lld,%d,%d,%Q,%Q,%Q)",
|
||||
zConflict, p->zDbName, p->zTableName,
|
||||
newRowid, iRank, iLang, zWord, zK1, zK2
|
||||
);
|
||||
}
|
||||
*pRowid = sqlite3_last_insert_rowid(db);
|
||||
@ -2757,9 +2783,9 @@ static int spellfix1Update(
|
||||
rowid = sqlite3_value_int64(argv[0]);
|
||||
newRowid = *pRowid = sqlite3_value_int64(argv[1]);
|
||||
spellfix1DbExec(&rc, db,
|
||||
"UPDATE \"%w\".\"%w_vocab\" SET id=%lld, rank=%d, langid=%d,"
|
||||
"UPDATE OR %s \"%w\".\"%w_vocab\" SET id=%lld, rank=%d, langid=%d,"
|
||||
" word=%Q, k1=%Q, k2=%Q WHERE id=%lld",
|
||||
p->zDbName, p->zTableName, newRowid, iRank, iLang,
|
||||
zConflict, p->zDbName, p->zTableName, newRowid, iRank, iLang,
|
||||
zWord, zK1, zK2, rowid
|
||||
);
|
||||
}
|
||||
|
30
manifest
30
manifest
@ -1,5 +1,5 @@
|
||||
C Add\sa\sscript\sto\scombine\sall\sfts5\scode\sinto\sa\ssingle\sfile\s-\sfts5.c\s-\sthat\scan\sbe\sused\sto\sbuild\san\sSQLite\sloadable\sextension.
|
||||
D 2015-06-25T20:10:24.657
|
||||
C Merge\slatest\strunk\schanges\sinto\sthis\sbranch.
|
||||
D 2015-06-25T20:16:23.149
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 4757ec5c89c420d90f18b0afa6e63d7b884c881d
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@ -113,7 +113,7 @@ F ext/fts5/fts5_config.c c232d181d6324f0ae3a2a31319924473999e5816
|
||||
F ext/fts5/fts5_expr.c 549bda1f7edcf10365fbfbc002bdea1be3c287bb
|
||||
F ext/fts5/fts5_hash.c c1cfdb2cae0fad00b06fae38a40eaf9261563ccc
|
||||
F ext/fts5/fts5_index.c 438b245e9e44be3cfa848c71351a4e769b75876a
|
||||
F ext/fts5/fts5_main.c 35e90d3d8fafe4c936f232108e891d5f9a6294ca w ext/fts5/fts5.c
|
||||
F ext/fts5/fts5_main.c 35e90d3d8fafe4c936f232108e891d5f9a6294ca
|
||||
F ext/fts5/fts5_storage.c b2fa301fce865d582d367a5e1bb438fe60c03cb5
|
||||
F ext/fts5/fts5_tcl.c 7ea165878e4ae3598e89acd470a0ee1b5a00e33c
|
||||
F ext/fts5/fts5_tokenize.c 97251d68d7a6a9415bde1203f9382864dfc1f989
|
||||
@ -195,7 +195,7 @@ F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63
|
||||
F ext/misc/regexp.c af92cdaa5058fcec1451e49becc7ba44dba023dc
|
||||
F ext/misc/rot13.c 1ac6f95f99b575907b9b09c81a349114cf9be45a
|
||||
F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
|
||||
F ext/misc/spellfix.c 25810dda37fc904b0772a13efd8ca072fb09e355
|
||||
F ext/misc/spellfix.c de9181ec188294dd2a1087b329ca55cfaa76a29d
|
||||
F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512
|
||||
F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95
|
||||
F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e
|
||||
@ -269,9 +269,9 @@ F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240
|
||||
F src/backup.c ff743689c4d6c5cb55ad42ed9d174b2b3e71f1e3
|
||||
F src/bitvec.c 5eb7958c3bf65210211cbcfc44eff86d0ded7c9d
|
||||
F src/btmutex.c 45a968cc85afed9b5e6cf55bf1f42f8d18107f79
|
||||
F src/btree.c 173c2ba1b8cf941971683f584965369791125f12
|
||||
F src/btree.c 40e98c10725c2cec5429068e21c17924f4bf06cc
|
||||
F src/btree.h 969adc948e89e449220ff0ff724c94bb2a52e9f1
|
||||
F src/btreeInt.h 6ece2dd9c8e2eac05f0a8ded8772a44e96486c65
|
||||
F src/btreeInt.h fdd1aff02fb2a63812bd95716e7f579fc3759107
|
||||
F src/build.c b3f15255d5b16e42dafeaa638fd4f8a47c94ed70
|
||||
F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0
|
||||
F src/complete.c a5cf5b4b56390cfb7b8636e8f7ddef90258dd575
|
||||
@ -327,7 +327,7 @@ F src/printf.c db11b5960105ee661dcac690f2ae6276e49bf251
|
||||
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
|
||||
F src/resolve.c 2d47554370de8de6dd5be060cef9559eec315005
|
||||
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
|
||||
F src/select.c 09865f89997db6ec617a78440cc18d84855e3053
|
||||
F src/select.c 9baeda79f93cfd180d471273a2f9c82c682a37a2
|
||||
F src/shell.c 8af3cced094aebb5f57a8ad739b9dafc7867eed7
|
||||
F src/sqlite.h.in 76d2f5637eb795b6300d9dd3c3ec3632ffafd721
|
||||
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
|
||||
@ -390,7 +390,7 @@ F src/update.c 487747b328b7216bb7f6af0695d6937d5c9e605f
|
||||
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
|
||||
F src/util.c a6431c92803b975b7322724a7b433e538d243539
|
||||
F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701
|
||||
F src/vdbe.c c9b8985dfc5df9bd512342ea2e56af4be30cb31a
|
||||
F src/vdbe.c 3af2d06e2b36012631dc3331957df52febdf8678
|
||||
F src/vdbe.h 90048aea1910f9df93e6044592bd4a466dc9c5e7
|
||||
F src/vdbeInt.h 20295e482121d13437f69985f77db211cdc8bac1
|
||||
F src/vdbeapi.c 6a0d7757987018ff6b1b81bc5293219cd26bb299
|
||||
@ -734,10 +734,10 @@ F test/fuzz2.test 76dc35b32b6d6f965259508508abce75a6c4d7e1
|
||||
F test/fuzz3.test efd384b896c647b61a2c1848ba70d42aad60a7b3
|
||||
F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b
|
||||
F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26
|
||||
F test/fuzzcheck.c 5805b2236292f8643d56e727a3a6e4d88e0856a5
|
||||
F test/fuzzcheck.c b973b06b500e6fc052d7059257cdf70df1f3a986
|
||||
F test/fuzzdata1.db 7ee3227bad0e7ccdeb08a9e6822916777073c664
|
||||
F test/fuzzdata2.db f03a420d3b822cc82e4f894ca957618fbe9c4973
|
||||
F test/fuzzdata3.db 77bed4fc8c4945124ed5616daf2dc4f4c3bf762a
|
||||
F test/fuzzdata3.db 1d6044c33a114007f02b6e6846f1fa232f607bfd
|
||||
F test/fuzzer1.test d4c52aaf3ef923da293a2653cfab33d02f718a36
|
||||
F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536
|
||||
F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98
|
||||
@ -822,7 +822,7 @@ F test/make-where7.tcl 05c16b5d4f5d6512881dfec560cb793915932ef9
|
||||
F test/malloc.test 21c213365f2cca95ab2d7dc078dc8525f96065f8
|
||||
F test/malloc3.test e3b32c724b5a124b57cb0ed177f675249ad0c66a
|
||||
F test/malloc4.test 957337613002b7058a85116493a262f679f3a261
|
||||
F test/malloc5.test 79182b8bffd6d62f77b1a5a8ba8e6bf0e5053b8e
|
||||
F test/malloc5.test e9a9116f80ab6b7a9ca258876c6f3dedb08cb08b
|
||||
F test/malloc6.test 2f039d9821927eacae43e1831f815e157659a151
|
||||
F test/malloc7.test 7c68a32942858bc715284856c5507446bba88c3a
|
||||
F test/malloc8.test 9b7a3f8cb9cf0b12fff566e80a980b1767bd961d
|
||||
@ -1006,7 +1006,7 @@ F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
|
||||
F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
|
||||
F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b
|
||||
F test/speedtest1.c f42fd04a34a0c1dc289cbe536ef62d706227a736
|
||||
F test/spellfix.test 24f676831acddd2f4056a598fd731a72c6311f49
|
||||
F test/spellfix.test 0597065ff57042df1f138e6a2611ae19c2698135
|
||||
F test/sqldiff1.test 8f6bc7c6a5b3585d350d779c6078869ba402f8f5
|
||||
F test/sqllimits1.test e05786eaed7950ff6a2d00031d001d8a26131e68
|
||||
F test/stat.test 8de91498c99f5298b303f70f1d1f3b9557af91bf
|
||||
@ -1364,7 +1364,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P df5ccea80e8f0da83af5e595b539687006085120
|
||||
R 274e8476f9e52d5f62a7ac4e2d65cc10
|
||||
P 46e86b0637248fb4d623c97778cc041eabe3636c 7d02e6c992ef92e1f77ebc13889e17c028454b06
|
||||
R 86f1f29b38c83a25da1125d5a5eacdb5
|
||||
U dan
|
||||
Z 3dd1c662504ec180da9a3ceeda958cfb
|
||||
Z c3d1693711def65e5756dddc3dd0ec59
|
||||
|
@ -1 +1 @@
|
||||
46e86b0637248fb4d623c97778cc041eabe3636c
|
||||
fcd8f7ce601729dc51d880d16b97040c1be16aa2
|
482
src/btree.c
482
src/btree.c
@ -490,13 +490,15 @@ static void invalidateIncrblobCursors(
|
||||
int isClearTable /* True if all rows are being deleted */
|
||||
){
|
||||
BtCursor *p;
|
||||
BtShared *pBt = pBtree->pBt;
|
||||
if( pBtree->hasIncrblobCur==0 ) return;
|
||||
assert( sqlite3BtreeHoldsMutex(pBtree) );
|
||||
for(p=pBt->pCursor; p; p=p->pNext){
|
||||
if( (p->curFlags & BTCF_Incrblob)!=0
|
||||
&& (isClearTable || p->info.nKey==iRow)
|
||||
){
|
||||
p->eState = CURSOR_INVALID;
|
||||
pBtree->hasIncrblobCur = 0;
|
||||
for(p=pBtree->pBt->pCursor; p; p=p->pNext){
|
||||
if( (p->curFlags & BTCF_Incrblob)!=0 ){
|
||||
pBtree->hasIncrblobCur = 1;
|
||||
if( isClearTable || p->info.nKey==iRow ){
|
||||
p->eState = CURSOR_INVALID;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -956,28 +958,6 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
|
||||
*/
|
||||
#define findCell(P,I) \
|
||||
((P)->aData + ((P)->maskPage & get2byte(&(P)->aCellIdx[2*(I)])))
|
||||
#define findCellv2(D,M,O,I) (D+(M&get2byte(D+(O+2*(I)))))
|
||||
|
||||
|
||||
/*
|
||||
** This a more complex version of findCell() that works for
|
||||
** pages that do contain overflow cells.
|
||||
*/
|
||||
static u8 *findOverflowCell(MemPage *pPage, int iCell){
|
||||
int i;
|
||||
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
|
||||
for(i=pPage->nOverflow-1; i>=0; i--){
|
||||
int k;
|
||||
k = pPage->aiOvfl[i];
|
||||
if( k<=iCell ){
|
||||
if( k==iCell ){
|
||||
return pPage->apOvfl[i];
|
||||
}
|
||||
iCell--;
|
||||
}
|
||||
}
|
||||
return findCell(pPage, iCell);
|
||||
}
|
||||
|
||||
/*
|
||||
** This is common tail processing for btreeParseCellPtr() and
|
||||
@ -1371,18 +1351,20 @@ static int defragmentPage(MemPage *pPage){
|
||||
** This function may detect corruption within pPg. If corruption is
|
||||
** detected then *pRc is set to SQLITE_CORRUPT and NULL is returned.
|
||||
**
|
||||
** If a slot of at least nByte bytes is found but cannot be used because
|
||||
** there are already at least 60 fragmented bytes on the page, return NULL.
|
||||
** In this case, if pbDefrag parameter is not NULL, set *pbDefrag to true.
|
||||
** Slots on the free list that are between 1 and 3 bytes larger than nByte
|
||||
** will be ignored if adding the extra space to the fragmentation count
|
||||
** causes the fragmentation count to exceed 60.
|
||||
*/
|
||||
static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc, int *pbDefrag){
|
||||
static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){
|
||||
const int hdr = pPg->hdrOffset;
|
||||
u8 * const aData = pPg->aData;
|
||||
int iAddr;
|
||||
int pc;
|
||||
int iAddr = hdr + 1;
|
||||
int pc = get2byte(&aData[iAddr]);
|
||||
int x;
|
||||
int usableSize = pPg->pBt->usableSize;
|
||||
|
||||
for(iAddr=hdr+1; (pc = get2byte(&aData[iAddr]))>0; iAddr=pc){
|
||||
assert( pc>0 );
|
||||
do{
|
||||
int size; /* Size of the free slot */
|
||||
/* EVIDENCE-OF: R-06866-39125 Freeblocks are always connected in order of
|
||||
** increasing offset. */
|
||||
@ -1394,8 +1376,7 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc, int *pbDefrag){
|
||||
** freeblock form a big-endian integer which is the size of the freeblock
|
||||
** in bytes, including the 4-byte header. */
|
||||
size = get2byte(&aData[pc+2]);
|
||||
if( size>=nByte ){
|
||||
int x = size - nByte;
|
||||
if( (x = size - nByte)>=0 ){
|
||||
testcase( x==4 );
|
||||
testcase( x==3 );
|
||||
if( pc < pPg->cellOffset+2*pPg->nCell || size+pc > usableSize ){
|
||||
@ -1404,10 +1385,8 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc, int *pbDefrag){
|
||||
}else if( x<4 ){
|
||||
/* EVIDENCE-OF: R-11498-58022 In a well-formed b-tree page, the total
|
||||
** number of bytes in fragments may not exceed 60. */
|
||||
if( aData[hdr+7]>=60 ){
|
||||
if( pbDefrag ) *pbDefrag = 1;
|
||||
return 0;
|
||||
}
|
||||
if( aData[hdr+7]>57 ) return 0;
|
||||
|
||||
/* Remove the slot from the free-list. Update the number of
|
||||
** fragmented bytes within the page. */
|
||||
memcpy(&aData[iAddr], &aData[pc], 2);
|
||||
@ -1419,7 +1398,9 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc, int *pbDefrag){
|
||||
}
|
||||
return &aData[pc + x];
|
||||
}
|
||||
}
|
||||
iAddr = pc;
|
||||
pc = get2byte(&aData[pc]);
|
||||
}while( pc );
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1460,11 +1441,14 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
|
||||
** then the cell content offset of an empty page wants to be 65536.
|
||||
** However, that integer is too large to be stored in a 2-byte unsigned
|
||||
** integer, so a value of 0 is used in its place. */
|
||||
top = get2byteNotZero(&data[hdr+5]);
|
||||
if( gap>top || NEVER((u32)top>pPage->pBt->usableSize) ){
|
||||
/* The NEVER() is because a oversize "top" value will be blocked from
|
||||
** reaching this point by btreeInitPage() or btreeGetUnusedPage() */
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
top = get2byte(&data[hdr+5]);
|
||||
assert( top<=pPage->pBt->usableSize ); /* Prevent by getAndInitPage() */
|
||||
if( gap>top ){
|
||||
if( top==0 && pPage->pBt->usableSize==65536 ){
|
||||
top = 65536;
|
||||
}else{
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
}
|
||||
|
||||
/* If there is enough space between gap and top for one more cell pointer
|
||||
@ -1474,15 +1458,14 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
|
||||
testcase( gap+2==top );
|
||||
testcase( gap+1==top );
|
||||
testcase( gap==top );
|
||||
if( gap+2<=top && (data[hdr+1] || data[hdr+2]) ){
|
||||
int bDefrag = 0;
|
||||
u8 *pSpace = pageFindSlot(pPage, nByte, &rc, &bDefrag);
|
||||
if( rc ) return rc;
|
||||
if( bDefrag ) goto defragment_page;
|
||||
if( (data[hdr+2] || data[hdr+1]) && gap+2<=top ){
|
||||
u8 *pSpace = pageFindSlot(pPage, nByte, &rc);
|
||||
if( pSpace ){
|
||||
assert( pSpace>=data && (pSpace - data)<65536 );
|
||||
*pIdx = (int)(pSpace - data);
|
||||
return SQLITE_OK;
|
||||
}else if( rc ){
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1491,7 +1474,6 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
|
||||
*/
|
||||
testcase( gap+2+nByte==top );
|
||||
if( gap+2+nByte>top ){
|
||||
defragment_page:
|
||||
assert( pPage->nCell>0 || CORRUPT_DB );
|
||||
rc = defragmentPage(pPage);
|
||||
if( rc ) return rc;
|
||||
@ -5447,6 +5429,7 @@ static int allocateBtreePage(
|
||||
/* There are pages on the freelist. Reuse one of those pages. */
|
||||
Pgno iTrunk;
|
||||
u8 searchList = 0; /* If the free-list must be searched for 'nearby' */
|
||||
u32 nSearch = 0; /* Count of the number of search attempts */
|
||||
|
||||
/* If eMode==BTALLOC_EXACT and a query of the pointer-map
|
||||
** shows that the page 'nearby' is somewhere on the free-list, then
|
||||
@ -5495,7 +5478,7 @@ static int allocateBtreePage(
|
||||
iTrunk = get4byte(&pPage1->aData[32]);
|
||||
}
|
||||
testcase( iTrunk==mxPage );
|
||||
if( iTrunk>mxPage ){
|
||||
if( iTrunk>mxPage || nSearch++ > n ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
}else{
|
||||
rc = btreeGetUnusedPage(pBt, iTrunk, &pTrunk, 0);
|
||||
@ -6214,10 +6197,8 @@ static void insertCell(
|
||||
){
|
||||
int idx = 0; /* Where to write new cell content in data[] */
|
||||
int j; /* Loop counter */
|
||||
int end; /* First byte past the last cell pointer in data[] */
|
||||
int ins; /* Index in data[] where new cell pointer is inserted */
|
||||
int cellOffset; /* Address of first cell pointer in data[] */
|
||||
u8 *data; /* The content of the whole page */
|
||||
u8 *pIns; /* The point in pPage->aCellIdx[] where no cell inserted */
|
||||
|
||||
if( *pRC ) return;
|
||||
|
||||
@ -6245,6 +6226,14 @@ static void insertCell(
|
||||
assert( j<(int)(sizeof(pPage->apOvfl)/sizeof(pPage->apOvfl[0])) );
|
||||
pPage->apOvfl[j] = pCell;
|
||||
pPage->aiOvfl[j] = (u16)i;
|
||||
|
||||
/* When multiple overflows occur, they are always sequential and in
|
||||
** sorted order. This invariants arise because multiple overflows can
|
||||
** only occur when inserting divider cells into the parent page during
|
||||
** balancing, and the dividers are adjacent and sorted.
|
||||
*/
|
||||
assert( j==0 || pPage->aiOvfl[j-1]<(u16)i ); /* Overflows in sorted order */
|
||||
assert( j==0 || i==pPage->aiOvfl[j-1]+1 ); /* Overflows are sequential */
|
||||
}else{
|
||||
int rc = sqlite3PagerWrite(pPage->pDbPage);
|
||||
if( rc!=SQLITE_OK ){
|
||||
@ -6253,24 +6242,26 @@ static void insertCell(
|
||||
}
|
||||
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
|
||||
data = pPage->aData;
|
||||
cellOffset = pPage->cellOffset;
|
||||
end = cellOffset + 2*pPage->nCell;
|
||||
ins = cellOffset + 2*i;
|
||||
assert( &data[pPage->cellOffset]==pPage->aCellIdx );
|
||||
rc = allocateSpace(pPage, sz, &idx);
|
||||
if( rc ){ *pRC = rc; return; }
|
||||
/* The allocateSpace() routine guarantees the following properties
|
||||
** if it returns successfully */
|
||||
assert( idx >= 0 && (idx >= end+2 || CORRUPT_DB) );
|
||||
assert( idx >= 0 );
|
||||
assert( idx >= pPage->cellOffset+2*pPage->nCell+2 || CORRUPT_DB );
|
||||
assert( idx+sz <= (int)pPage->pBt->usableSize );
|
||||
pPage->nCell++;
|
||||
pPage->nFree -= (u16)(2 + sz);
|
||||
memcpy(&data[idx], pCell, sz);
|
||||
if( iChild ){
|
||||
put4byte(&data[idx], iChild);
|
||||
}
|
||||
memmove(&data[ins+2], &data[ins], end-ins);
|
||||
put2byte(&data[ins], idx);
|
||||
put2byte(&data[pPage->hdrOffset+3], pPage->nCell);
|
||||
pIns = pPage->aCellIdx + i*2;
|
||||
memmove(pIns+2, pIns, 2*(pPage->nCell - i));
|
||||
put2byte(pIns, idx);
|
||||
pPage->nCell++;
|
||||
/* increment the cell count */
|
||||
if( (++data[pPage->hdrOffset+4])==0 ) data[pPage->hdrOffset+3]++;
|
||||
assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell );
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
if( pPage->pBt->autoVacuum ){
|
||||
/* The cell may contain a pointer to an overflow page. If so, write
|
||||
@ -6282,6 +6273,52 @@ static void insertCell(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** A CellArray object contains a cache of pointers and sizes for a
|
||||
** consecutive sequence of cells that might be held multiple pages.
|
||||
*/
|
||||
typedef struct CellArray CellArray;
|
||||
struct CellArray {
|
||||
int nCell; /* Number of cells in apCell[] */
|
||||
MemPage *pRef; /* Reference page */
|
||||
u8 **apCell; /* All cells begin balanced */
|
||||
u16 *szCell; /* Local size of all cells in apCell[] */
|
||||
};
|
||||
|
||||
/*
|
||||
** Make sure the cell sizes at idx, idx+1, ..., idx+N-1 have been
|
||||
** computed.
|
||||
*/
|
||||
static void populateCellCache(CellArray *p, int idx, int N){
|
||||
assert( idx>=0 && idx+N<=p->nCell );
|
||||
while( N>0 ){
|
||||
assert( p->apCell[idx]!=0 );
|
||||
if( p->szCell[idx]==0 ){
|
||||
p->szCell[idx] = p->pRef->xCellSize(p->pRef, p->apCell[idx]);
|
||||
}else{
|
||||
assert( CORRUPT_DB ||
|
||||
p->szCell[idx]==p->pRef->xCellSize(p->pRef, p->apCell[idx]) );
|
||||
}
|
||||
idx++;
|
||||
N--;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the size of the Nth element of the cell array
|
||||
*/
|
||||
static SQLITE_NOINLINE u16 computeCellSize(CellArray *p, int N){
|
||||
assert( N>=0 && N<p->nCell );
|
||||
assert( p->szCell[N]==0 );
|
||||
p->szCell[N] = p->pRef->xCellSize(p->pRef, p->apCell[N]);
|
||||
return p->szCell[N];
|
||||
}
|
||||
static u16 cachedCellSize(CellArray *p, int N){
|
||||
assert( N>=0 && N<p->nCell );
|
||||
if( p->szCell[N] ) return p->szCell[N];
|
||||
return computeCellSize(p, N);
|
||||
}
|
||||
|
||||
/*
|
||||
** Array apCell[] contains pointers to nCell b-tree page cells. The
|
||||
** szCell[] array contains the size in bytes of each cell. This function
|
||||
@ -6295,7 +6332,7 @@ static void insertCell(
|
||||
** The MemPage.nFree field is invalidated by this function. It is the
|
||||
** responsibility of the caller to set it correctly.
|
||||
*/
|
||||
static void rebuildPage(
|
||||
static int rebuildPage(
|
||||
MemPage *pPg, /* Edit this page */
|
||||
int nCell, /* Final number of cells on page */
|
||||
u8 **apCell, /* Array of cells */
|
||||
@ -6320,11 +6357,12 @@ static void rebuildPage(
|
||||
pCell = &pTmp[pCell - aData];
|
||||
}
|
||||
pData -= szCell[i];
|
||||
memcpy(pData, pCell, szCell[i]);
|
||||
put2byte(pCellptr, (pData - aData));
|
||||
pCellptr += 2;
|
||||
if( pData < pCellptr ) return SQLITE_CORRUPT_BKPT;
|
||||
memcpy(pData, pCell, szCell[i]);
|
||||
assert( szCell[i]==pPg->xCellSize(pPg, pCell) || CORRUPT_DB );
|
||||
testcase( szCell[i]==pPg->xCellSize(pPg,pCell) );
|
||||
testcase( szCell[i]!=pPg->xCellSize(pPg,pCell) );
|
||||
}
|
||||
|
||||
/* The pPg->nFree field is now set incorrectly. The caller will fix it. */
|
||||
@ -6335,6 +6373,7 @@ static void rebuildPage(
|
||||
put2byte(&aData[hdr+3], pPg->nCell);
|
||||
put2byte(&aData[hdr+5], pData - aData);
|
||||
aData[hdr+7] = 0x00;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -6367,25 +6406,25 @@ static int pageInsertArray(
|
||||
u8 *pBegin, /* End of cell-pointer array */
|
||||
u8 **ppData, /* IN/OUT: Page content -area pointer */
|
||||
u8 *pCellptr, /* Pointer to cell-pointer area */
|
||||
int iFirst, /* Index of first cell to add */
|
||||
int nCell, /* Number of cells to add to pPg */
|
||||
u8 **apCell, /* Array of cells */
|
||||
u16 *szCell /* Array of cell sizes */
|
||||
CellArray *pCArray /* Array of cells */
|
||||
){
|
||||
int i;
|
||||
u8 *aData = pPg->aData;
|
||||
u8 *pData = *ppData;
|
||||
const int bFreelist = aData[1] || aData[2];
|
||||
int iEnd = iFirst + nCell;
|
||||
assert( CORRUPT_DB || pPg->hdrOffset==0 ); /* Never called on page 1 */
|
||||
for(i=0; i<nCell; i++){
|
||||
int sz = szCell[i];
|
||||
int rc;
|
||||
for(i=iFirst; i<iEnd; i++){
|
||||
int sz, rc;
|
||||
u8 *pSlot;
|
||||
if( bFreelist==0 || (pSlot = pageFindSlot(pPg, sz, &rc, 0))==0 ){
|
||||
sz = cachedCellSize(pCArray, i);
|
||||
if( (aData[1]==0 && aData[2]==0) || (pSlot = pageFindSlot(pPg,sz,&rc))==0 ){
|
||||
pData -= sz;
|
||||
if( pData<pBegin ) return 1;
|
||||
pSlot = pData;
|
||||
}
|
||||
memcpy(pSlot, apCell[i], sz);
|
||||
memcpy(pSlot, pCArray->apCell[i], sz);
|
||||
put2byte(pCellptr, (pSlot - aData));
|
||||
pCellptr += 2;
|
||||
}
|
||||
@ -6404,22 +6443,27 @@ static int pageInsertArray(
|
||||
*/
|
||||
static int pageFreeArray(
|
||||
MemPage *pPg, /* Page to edit */
|
||||
int iFirst, /* First cell to delete */
|
||||
int nCell, /* Cells to delete */
|
||||
u8 **apCell, /* Array of cells */
|
||||
u16 *szCell /* Array of cell sizes */
|
||||
CellArray *pCArray /* Array of cells */
|
||||
){
|
||||
u8 * const aData = pPg->aData;
|
||||
u8 * const pEnd = &aData[pPg->pBt->usableSize];
|
||||
u8 * const pStart = &aData[pPg->hdrOffset + 8 + pPg->childPtrSize];
|
||||
int nRet = 0;
|
||||
int i;
|
||||
int iEnd = iFirst + nCell;
|
||||
u8 *pFree = 0;
|
||||
int szFree = 0;
|
||||
|
||||
for(i=0; i<nCell; i++){
|
||||
u8 *pCell = apCell[i];
|
||||
for(i=iFirst; i<iEnd; i++){
|
||||
u8 *pCell = pCArray->apCell[i];
|
||||
if( pCell>=pStart && pCell<pEnd ){
|
||||
int sz = szCell[i];
|
||||
int sz;
|
||||
/* No need to use cachedCellSize() here. The sizes of all cells that
|
||||
** are to be freed have already been computing while deciding which
|
||||
** cells need freeing */
|
||||
sz = pCArray->szCell[i]; assert( sz>0 );
|
||||
if( pFree!=(pCell + sz) ){
|
||||
if( pFree ){
|
||||
assert( pFree>aData && (pFree - aData)<65536 );
|
||||
@ -6454,13 +6498,12 @@ static int pageFreeArray(
|
||||
** The pPg->nFree field is invalid when this function returns. It is the
|
||||
** responsibility of the caller to set it correctly.
|
||||
*/
|
||||
static void editPage(
|
||||
static int editPage(
|
||||
MemPage *pPg, /* Edit this page */
|
||||
int iOld, /* Index of first cell currently on page */
|
||||
int iNew, /* Index of new first cell on page */
|
||||
int nNew, /* Final number of cells on page */
|
||||
u8 **apCell, /* Array of cells */
|
||||
u16 *szCell /* Array of cell sizes */
|
||||
CellArray *pCArray /* Array of cells and sizes */
|
||||
){
|
||||
u8 * const aData = pPg->aData;
|
||||
const int hdr = pPg->hdrOffset;
|
||||
@ -6479,16 +6522,12 @@ static void editPage(
|
||||
|
||||
/* Remove cells from the start and end of the page */
|
||||
if( iOld<iNew ){
|
||||
int nShift = pageFreeArray(
|
||||
pPg, iNew-iOld, &apCell[iOld], &szCell[iOld]
|
||||
);
|
||||
int nShift = pageFreeArray(pPg, iOld, iNew-iOld, pCArray);
|
||||
memmove(pPg->aCellIdx, &pPg->aCellIdx[nShift*2], nCell*2);
|
||||
nCell -= nShift;
|
||||
}
|
||||
if( iNewEnd < iOldEnd ){
|
||||
nCell -= pageFreeArray(
|
||||
pPg, iOldEnd-iNewEnd, &apCell[iNewEnd], &szCell[iNewEnd]
|
||||
);
|
||||
nCell -= pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray);
|
||||
}
|
||||
|
||||
pData = &aData[get2byteNotZero(&aData[hdr+5])];
|
||||
@ -6502,7 +6541,7 @@ static void editPage(
|
||||
memmove(&pCellptr[nAdd*2], pCellptr, nCell*2);
|
||||
if( pageInsertArray(
|
||||
pPg, pBegin, &pData, pCellptr,
|
||||
nAdd, &apCell[iNew], &szCell[iNew]
|
||||
iNew, nAdd, pCArray
|
||||
) ) goto editpage_fail;
|
||||
nCell += nAdd;
|
||||
}
|
||||
@ -6516,7 +6555,7 @@ static void editPage(
|
||||
nCell++;
|
||||
if( pageInsertArray(
|
||||
pPg, pBegin, &pData, pCellptr,
|
||||
1, &apCell[iCell + iNew], &szCell[iCell + iNew]
|
||||
iCell+iNew, 1, pCArray
|
||||
) ) goto editpage_fail;
|
||||
}
|
||||
}
|
||||
@ -6525,7 +6564,7 @@ static void editPage(
|
||||
pCellptr = &pPg->aCellIdx[nCell*2];
|
||||
if( pageInsertArray(
|
||||
pPg, pBegin, &pData, pCellptr,
|
||||
nNew-nCell, &apCell[iNew+nCell], &szCell[iNew+nCell]
|
||||
iNew+nCell, nNew-nCell, pCArray
|
||||
) ) goto editpage_fail;
|
||||
|
||||
pPg->nCell = nNew;
|
||||
@ -6536,19 +6575,21 @@ static void editPage(
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
for(i=0; i<nNew && !CORRUPT_DB; i++){
|
||||
u8 *pCell = apCell[i+iNew];
|
||||
u8 *pCell = pCArray->apCell[i+iNew];
|
||||
int iOff = get2byte(&pPg->aCellIdx[i*2]);
|
||||
if( pCell>=aData && pCell<&aData[pPg->pBt->usableSize] ){
|
||||
pCell = &pTmp[pCell - aData];
|
||||
}
|
||||
assert( 0==memcmp(pCell, &aData[iOff], szCell[i+iNew]) );
|
||||
assert( 0==memcmp(pCell, &aData[iOff],
|
||||
pCArray->pRef->xCellSize(pCArray->pRef, pCArray->apCell[i+iNew])) );
|
||||
}
|
||||
#endif
|
||||
|
||||
return;
|
||||
return SQLITE_OK;
|
||||
editpage_fail:
|
||||
/* Unable to edit this page. Rebuild it from scratch instead. */
|
||||
rebuildPage(pPg, nNew, &apCell[iNew], &szCell[iNew]);
|
||||
populateCellCache(pCArray, iNew, nNew);
|
||||
return rebuildPage(pPg, nNew, &pCArray->apCell[iNew], &pCArray->szCell[iNew]);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -6620,7 +6661,8 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
|
||||
assert( sqlite3PagerIswriteable(pNew->pDbPage) );
|
||||
assert( pPage->aData[0]==(PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF) );
|
||||
zeroPage(pNew, PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF);
|
||||
rebuildPage(pNew, 1, &pCell, &szCell);
|
||||
rc = rebuildPage(pNew, 1, &pCell, &szCell);
|
||||
if( NEVER(rc) ) return rc;
|
||||
pNew->nFree = pBt->usableSize - pNew->cellOffset - 2 - szCell;
|
||||
|
||||
/* If this is an auto-vacuum database, update the pointer map
|
||||
@ -6824,7 +6866,6 @@ static int balance_nonroot(
|
||||
int bBulk /* True if this call is part of a bulk load */
|
||||
){
|
||||
BtShared *pBt; /* The whole database */
|
||||
int nCell = 0; /* Number of cells in apCell[] */
|
||||
int nMaxCells = 0; /* Allocated size of apCell, szCell, aFrom. */
|
||||
int nNew = 0; /* Number of pages in apNew[] */
|
||||
int nOld; /* Number of pages in apOld[] */
|
||||
@ -6835,7 +6876,6 @@ static int balance_nonroot(
|
||||
int leafData; /* True if pPage is a leaf of a LEAFDATA tree */
|
||||
int usableSpace; /* Bytes in pPage beyond the header */
|
||||
int pageFlags; /* Value of pPage->aData[0] */
|
||||
int subtotal; /* Subtotal of bytes in cells on one page */
|
||||
int iSpace1 = 0; /* First unused byte of aSpace1[] */
|
||||
int iOvflSpace = 0; /* First unused byte of aOvflSpace[] */
|
||||
int szScratch; /* Size of scratch memory requested */
|
||||
@ -6843,19 +6883,20 @@ static int balance_nonroot(
|
||||
MemPage *apNew[NB+2]; /* pPage and up to NB siblings after balancing */
|
||||
u8 *pRight; /* Location in parent of right-sibling pointer */
|
||||
u8 *apDiv[NB-1]; /* Divider cells in pParent */
|
||||
int cntNew[NB+2]; /* Index in aCell[] of cell after i-th page */
|
||||
int cntOld[NB+2]; /* Old index in aCell[] after i-th page */
|
||||
int cntNew[NB+2]; /* Index in b.paCell[] of cell after i-th page */
|
||||
int cntOld[NB+2]; /* Old index in b.apCell[] */
|
||||
int szNew[NB+2]; /* Combined size of cells placed on i-th page */
|
||||
u8 **apCell = 0; /* All cells begin balanced */
|
||||
u16 *szCell; /* Local size of all cells in apCell[] */
|
||||
u8 *aSpace1; /* Space for copies of dividers cells */
|
||||
Pgno pgno; /* Temp var to store a page number in */
|
||||
u8 abDone[NB+2]; /* True after i'th new page is populated */
|
||||
Pgno aPgno[NB+2]; /* Page numbers of new pages before shuffling */
|
||||
Pgno aPgOrder[NB+2]; /* Copy of aPgno[] used for sorting pages */
|
||||
u16 aPgFlags[NB+2]; /* flags field of new pages before shuffling */
|
||||
CellArray b; /* Parsed information on cells being balanced */
|
||||
|
||||
memset(abDone, 0, sizeof(abDone));
|
||||
b.nCell = 0;
|
||||
b.apCell = 0;
|
||||
pBt = pParent->pBt;
|
||||
assert( sqlite3_mutex_held(pBt->mutex) );
|
||||
assert( sqlite3PagerIswriteable(pParent->pDbPage) );
|
||||
@ -6964,43 +7005,48 @@ static int balance_nonroot(
|
||||
** Allocate space for memory structures
|
||||
*/
|
||||
szScratch =
|
||||
nMaxCells*sizeof(u8*) /* apCell */
|
||||
+ nMaxCells*sizeof(u16) /* szCell */
|
||||
nMaxCells*sizeof(u8*) /* b.apCell */
|
||||
+ nMaxCells*sizeof(u16) /* b.szCell */
|
||||
+ pBt->pageSize; /* aSpace1 */
|
||||
|
||||
/* EVIDENCE-OF: R-28375-38319 SQLite will never request a scratch buffer
|
||||
** that is more than 6 times the database page size. */
|
||||
assert( szScratch<=6*(int)pBt->pageSize );
|
||||
apCell = sqlite3ScratchMalloc( szScratch );
|
||||
if( apCell==0 ){
|
||||
b.apCell = sqlite3ScratchMalloc( szScratch );
|
||||
if( b.apCell==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto balance_cleanup;
|
||||
}
|
||||
szCell = (u16*)&apCell[nMaxCells];
|
||||
aSpace1 = (u8*)&szCell[nMaxCells];
|
||||
b.szCell = (u16*)&b.apCell[nMaxCells];
|
||||
aSpace1 = (u8*)&b.szCell[nMaxCells];
|
||||
assert( EIGHT_BYTE_ALIGNMENT(aSpace1) );
|
||||
|
||||
/*
|
||||
** Load pointers to all cells on sibling pages and the divider cells
|
||||
** into the local apCell[] array. Make copies of the divider cells
|
||||
** into the local b.apCell[] array. Make copies of the divider cells
|
||||
** into space obtained from aSpace1[]. The divider cells have already
|
||||
** been removed from pParent.
|
||||
**
|
||||
** If the siblings are on leaf pages, then the child pointers of the
|
||||
** divider cells are stripped from the cells before they are copied
|
||||
** into aSpace1[]. In this way, all cells in apCell[] are without
|
||||
** into aSpace1[]. In this way, all cells in b.apCell[] are without
|
||||
** child pointers. If siblings are not leaves, then all cell in
|
||||
** apCell[] include child pointers. Either way, all cells in apCell[]
|
||||
** b.apCell[] include child pointers. Either way, all cells in b.apCell[]
|
||||
** are alike.
|
||||
**
|
||||
** leafCorrection: 4 if pPage is a leaf. 0 if pPage is not a leaf.
|
||||
** leafData: 1 if pPage holds key+data and pParent holds only keys.
|
||||
*/
|
||||
leafCorrection = apOld[0]->leaf*4;
|
||||
leafData = apOld[0]->intKeyLeaf;
|
||||
b.pRef = apOld[0];
|
||||
leafCorrection = b.pRef->leaf*4;
|
||||
leafData = b.pRef->intKeyLeaf;
|
||||
for(i=0; i<nOld; i++){
|
||||
int limit;
|
||||
MemPage *pOld = apOld[i];
|
||||
int limit = pOld->nCell;
|
||||
u8 *aData = pOld->aData;
|
||||
u16 maskPage = pOld->maskPage;
|
||||
u8 *piCell = aData + pOld->cellOffset;
|
||||
u8 *piEnd;
|
||||
|
||||
/* Verify that all sibling pages are of the same "type" (table-leaf,
|
||||
** table-interior, index-leaf, or index-interior).
|
||||
@ -7010,92 +7056,150 @@ static int balance_nonroot(
|
||||
goto balance_cleanup;
|
||||
}
|
||||
|
||||
limit = pOld->nCell+pOld->nOverflow;
|
||||
/* Load b.apCell[] with pointers to all cells in pOld. If pOld
|
||||
** constains overflow cells, include them in the b.apCell[] array
|
||||
** in the correct spot.
|
||||
**
|
||||
** Note that when there are multiple overflow cells, it is always the
|
||||
** case that they are sequential and adjacent. This invariant arises
|
||||
** because multiple overflows can only occurs when inserting divider
|
||||
** cells into a parent on a prior balance, and divider cells are always
|
||||
** adjacent and are inserted in order. There is an assert() tagged
|
||||
** with "NOTE 1" in the overflow cell insertion loop to prove this
|
||||
** invariant.
|
||||
**
|
||||
** This must be done in advance. Once the balance starts, the cell
|
||||
** offset section of the btree page will be overwritten and we will no
|
||||
** long be able to find the cells if a pointer to each cell is not saved
|
||||
** first.
|
||||
*/
|
||||
memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*limit);
|
||||
if( pOld->nOverflow>0 ){
|
||||
memset(&b.szCell[b.nCell+limit], 0, sizeof(b.szCell[0])*pOld->nOverflow);
|
||||
limit = pOld->aiOvfl[0];
|
||||
for(j=0; j<limit; j++){
|
||||
assert( nCell<nMaxCells );
|
||||
apCell[nCell] = findOverflowCell(pOld, j);
|
||||
szCell[nCell] = pOld->xCellSize(pOld, apCell[nCell]);
|
||||
nCell++;
|
||||
b.apCell[b.nCell] = aData + (maskPage & get2byte(piCell));
|
||||
piCell += 2;
|
||||
b.nCell++;
|
||||
}
|
||||
}else{
|
||||
u8 *aData = pOld->aData;
|
||||
u16 maskPage = pOld->maskPage;
|
||||
u16 cellOffset = pOld->cellOffset;
|
||||
for(j=0; j<limit; j++){
|
||||
assert( nCell<nMaxCells );
|
||||
apCell[nCell] = findCellv2(aData, maskPage, cellOffset, j);
|
||||
szCell[nCell] = pOld->xCellSize(pOld, apCell[nCell]);
|
||||
nCell++;
|
||||
for(k=0; k<pOld->nOverflow; k++){
|
||||
assert( k==0 || pOld->aiOvfl[k-1]+1==pOld->aiOvfl[k] );/* NOTE 1 */
|
||||
b.apCell[b.nCell] = pOld->apOvfl[k];
|
||||
b.nCell++;
|
||||
}
|
||||
}
|
||||
cntOld[i] = nCell;
|
||||
}
|
||||
piEnd = aData + pOld->cellOffset + 2*pOld->nCell;
|
||||
while( piCell<piEnd ){
|
||||
assert( b.nCell<nMaxCells );
|
||||
b.apCell[b.nCell] = aData + (maskPage & get2byte(piCell));
|
||||
piCell += 2;
|
||||
b.nCell++;
|
||||
}
|
||||
|
||||
cntOld[i] = b.nCell;
|
||||
if( i<nOld-1 && !leafData){
|
||||
u16 sz = (u16)szNew[i];
|
||||
u8 *pTemp;
|
||||
assert( nCell<nMaxCells );
|
||||
szCell[nCell] = sz;
|
||||
assert( b.nCell<nMaxCells );
|
||||
b.szCell[b.nCell] = sz;
|
||||
pTemp = &aSpace1[iSpace1];
|
||||
iSpace1 += sz;
|
||||
assert( sz<=pBt->maxLocal+23 );
|
||||
assert( iSpace1 <= (int)pBt->pageSize );
|
||||
memcpy(pTemp, apDiv[i], sz);
|
||||
apCell[nCell] = pTemp+leafCorrection;
|
||||
b.apCell[b.nCell] = pTemp+leafCorrection;
|
||||
assert( leafCorrection==0 || leafCorrection==4 );
|
||||
szCell[nCell] = szCell[nCell] - leafCorrection;
|
||||
b.szCell[b.nCell] = b.szCell[b.nCell] - leafCorrection;
|
||||
if( !pOld->leaf ){
|
||||
assert( leafCorrection==0 );
|
||||
assert( pOld->hdrOffset==0 );
|
||||
/* The right pointer of the child page pOld becomes the left
|
||||
** pointer of the divider cell */
|
||||
memcpy(apCell[nCell], &pOld->aData[8], 4);
|
||||
memcpy(b.apCell[b.nCell], &pOld->aData[8], 4);
|
||||
}else{
|
||||
assert( leafCorrection==4 );
|
||||
while( szCell[nCell]<4 ){
|
||||
while( b.szCell[b.nCell]<4 ){
|
||||
/* Do not allow any cells smaller than 4 bytes. If a smaller cell
|
||||
** does exist, pad it with 0x00 bytes. */
|
||||
assert( szCell[nCell]==3 || CORRUPT_DB );
|
||||
assert( apCell[nCell]==&aSpace1[iSpace1-3] || CORRUPT_DB );
|
||||
assert( b.szCell[b.nCell]==3 || CORRUPT_DB );
|
||||
assert( b.apCell[b.nCell]==&aSpace1[iSpace1-3] || CORRUPT_DB );
|
||||
aSpace1[iSpace1++] = 0x00;
|
||||
szCell[nCell]++;
|
||||
b.szCell[b.nCell]++;
|
||||
}
|
||||
}
|
||||
nCell++;
|
||||
b.nCell++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Figure out the number of pages needed to hold all nCell cells.
|
||||
** Figure out the number of pages needed to hold all b.nCell cells.
|
||||
** Store this number in "k". Also compute szNew[] which is the total
|
||||
** size of all cells on the i-th page and cntNew[] which is the index
|
||||
** in apCell[] of the cell that divides page i from page i+1.
|
||||
** cntNew[k] should equal nCell.
|
||||
** in b.apCell[] of the cell that divides page i from page i+1.
|
||||
** cntNew[k] should equal b.nCell.
|
||||
**
|
||||
** Values computed by this block:
|
||||
**
|
||||
** k: The total number of sibling pages
|
||||
** szNew[i]: Spaced used on the i-th sibling page.
|
||||
** cntNew[i]: Index in apCell[] and szCell[] for the first cell to
|
||||
** cntNew[i]: Index in b.apCell[] and b.szCell[] for the first cell to
|
||||
** the right of the i-th sibling page.
|
||||
** usableSpace: Number of bytes of space available on each sibling.
|
||||
**
|
||||
*/
|
||||
usableSpace = pBt->usableSize - 12 + leafCorrection;
|
||||
for(subtotal=k=i=0; i<nCell; i++){
|
||||
assert( i<nMaxCells );
|
||||
subtotal += szCell[i] + 2;
|
||||
if( subtotal > usableSpace ){
|
||||
szNew[k] = subtotal - szCell[i] - 2;
|
||||
cntNew[k] = i;
|
||||
if( leafData ){ i--; }
|
||||
subtotal = 0;
|
||||
k++;
|
||||
if( k>NB+1 ){ rc = SQLITE_CORRUPT_BKPT; goto balance_cleanup; }
|
||||
for(i=0; i<nOld; i++){
|
||||
MemPage *p = apOld[i];
|
||||
szNew[i] = usableSpace - p->nFree;
|
||||
if( szNew[i]<0 ){ rc = SQLITE_CORRUPT_BKPT; goto balance_cleanup; }
|
||||
for(j=0; j<p->nOverflow; j++){
|
||||
szNew[i] += 2 + p->xCellSize(p, p->apOvfl[j]);
|
||||
}
|
||||
cntNew[i] = cntOld[i];
|
||||
}
|
||||
k = nOld;
|
||||
for(i=0; i<k; i++){
|
||||
int sz;
|
||||
while( szNew[i]>usableSpace ){
|
||||
if( i+1>=k ){
|
||||
k = i+2;
|
||||
if( k>NB+2 ){ rc = SQLITE_CORRUPT_BKPT; goto balance_cleanup; }
|
||||
szNew[k-1] = 0;
|
||||
cntNew[k-1] = b.nCell;
|
||||
}
|
||||
sz = 2 + cachedCellSize(&b, cntNew[i]-1);
|
||||
szNew[i] -= sz;
|
||||
if( !leafData ){
|
||||
if( cntNew[i]<b.nCell ){
|
||||
sz = 2 + cachedCellSize(&b, cntNew[i]);
|
||||
}else{
|
||||
sz = 0;
|
||||
}
|
||||
}
|
||||
szNew[i+1] += sz;
|
||||
cntNew[i]--;
|
||||
}
|
||||
while( cntNew[i]<b.nCell ){
|
||||
sz = 2 + cachedCellSize(&b, cntNew[i]);
|
||||
if( szNew[i]+sz>usableSpace ) break;
|
||||
szNew[i] += sz;
|
||||
cntNew[i]++;
|
||||
if( !leafData ){
|
||||
if( cntNew[i]<b.nCell ){
|
||||
sz = 2 + cachedCellSize(&b, cntNew[i]);
|
||||
}else{
|
||||
sz = 0;
|
||||
}
|
||||
}
|
||||
szNew[i+1] -= sz;
|
||||
}
|
||||
if( cntNew[i]>=b.nCell ){
|
||||
k = i+1;
|
||||
}else if( cntNew[i] <= (i>0 ? cntNew[i-1] : 0) ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto balance_cleanup;
|
||||
}
|
||||
}
|
||||
szNew[k] = subtotal;
|
||||
cntNew[k] = nCell;
|
||||
k++;
|
||||
|
||||
/*
|
||||
** The packing computed by the previous block is biased toward the siblings
|
||||
@ -7116,19 +7220,27 @@ static int balance_nonroot(
|
||||
|
||||
r = cntNew[i-1] - 1;
|
||||
d = r + 1 - leafData;
|
||||
assert( d<nMaxCells );
|
||||
assert( r<nMaxCells );
|
||||
while( szRight==0
|
||||
|| (!bBulk && szRight+szCell[d]+2<=szLeft-(szCell[r]+2))
|
||||
){
|
||||
szRight += szCell[d] + 2;
|
||||
szLeft -= szCell[r] + 2;
|
||||
cntNew[i-1]--;
|
||||
r = cntNew[i-1] - 1;
|
||||
d = r + 1 - leafData;
|
||||
}
|
||||
(void)cachedCellSize(&b, d);
|
||||
do{
|
||||
assert( d<nMaxCells );
|
||||
assert( r<nMaxCells );
|
||||
(void)cachedCellSize(&b, r);
|
||||
if( szRight!=0
|
||||
&& (bBulk || szRight+b.szCell[d]+2 > szLeft-(b.szCell[r]+2)) ){
|
||||
break;
|
||||
}
|
||||
szRight += b.szCell[d] + 2;
|
||||
szLeft -= b.szCell[r] + 2;
|
||||
cntNew[i-1] = r;
|
||||
r--;
|
||||
d--;
|
||||
}while( r>=0 );
|
||||
szNew[i] = szRight;
|
||||
szNew[i-1] = szLeft;
|
||||
if( cntNew[i-1] <= (i>1 ? cntNew[i-2] : 0) ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto balance_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sanity check: For a non-corrupt database file one of the follwing
|
||||
@ -7164,7 +7276,7 @@ static int balance_nonroot(
|
||||
zeroPage(pNew, pageFlags);
|
||||
apNew[i] = pNew;
|
||||
nNew++;
|
||||
cntOld[i] = nCell;
|
||||
cntOld[i] = b.nCell;
|
||||
|
||||
/* Set the pointer-map entry for the new sibling page. */
|
||||
if( ISAUTOVACUUM ){
|
||||
@ -7269,8 +7381,8 @@ static int balance_nonroot(
|
||||
int iNew = 0;
|
||||
int iOld = 0;
|
||||
|
||||
for(i=0; i<nCell; i++){
|
||||
u8 *pCell = apCell[i];
|
||||
for(i=0; i<b.nCell; i++){
|
||||
u8 *pCell = b.apCell[i];
|
||||
if( i==cntOldNext ){
|
||||
MemPage *pOld = (++iOld)<nNew ? apNew[iOld] : apOld[iOld];
|
||||
cntOldNext += pOld->nCell + pOld->nOverflow + !leafData;
|
||||
@ -7295,9 +7407,10 @@ static int balance_nonroot(
|
||||
if( !leafCorrection ){
|
||||
ptrmapPut(pBt, get4byte(pCell), PTRMAP_BTREE, pNew->pgno, &rc);
|
||||
}
|
||||
if( szCell[i]>pNew->minLocal ){
|
||||
if( cachedCellSize(&b,i)>pNew->minLocal ){
|
||||
ptrmapPutOvflPtr(pNew, pCell, &rc);
|
||||
}
|
||||
if( rc ) goto balance_cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7311,20 +7424,21 @@ static int balance_nonroot(
|
||||
j = cntNew[i];
|
||||
|
||||
assert( j<nMaxCells );
|
||||
pCell = apCell[j];
|
||||
sz = szCell[j] + leafCorrection;
|
||||
assert( b.apCell[j]!=0 );
|
||||
pCell = b.apCell[j];
|
||||
sz = b.szCell[j] + leafCorrection;
|
||||
pTemp = &aOvflSpace[iOvflSpace];
|
||||
if( !pNew->leaf ){
|
||||
memcpy(&pNew->aData[8], pCell, 4);
|
||||
}else if( leafData ){
|
||||
/* If the tree is a leaf-data tree, and the siblings are leaves,
|
||||
** then there is no divider cell in apCell[]. Instead, the divider
|
||||
** then there is no divider cell in b.apCell[]. Instead, the divider
|
||||
** cell consists of the integer key for the right-most cell of
|
||||
** the sibling-page assembled above only.
|
||||
*/
|
||||
CellInfo info;
|
||||
j--;
|
||||
pNew->xParseCell(pNew, apCell[j], &info);
|
||||
pNew->xParseCell(pNew, b.apCell[j], &info);
|
||||
pCell = pTemp;
|
||||
sz = 4 + putVarint(&pCell[4], info.nKey);
|
||||
pTemp = 0;
|
||||
@ -7341,7 +7455,7 @@ static int balance_nonroot(
|
||||
** cells are at least 4 bytes. It only happens in b-trees used
|
||||
** to evaluate "IN (SELECT ...)" and similar clauses.
|
||||
*/
|
||||
if( szCell[j]==4 ){
|
||||
if( b.szCell[j]==4 ){
|
||||
assert(leafCorrection==4);
|
||||
sz = pParent->xCellSize(pParent, pCell);
|
||||
}
|
||||
@ -7399,12 +7513,13 @@ static int balance_nonroot(
|
||||
iNew = iOld = 0;
|
||||
nNewCell = cntNew[0];
|
||||
}else{
|
||||
iOld = iPg<nOld ? (cntOld[iPg-1] + !leafData) : nCell;
|
||||
iOld = iPg<nOld ? (cntOld[iPg-1] + !leafData) : b.nCell;
|
||||
iNew = cntNew[iPg-1] + !leafData;
|
||||
nNewCell = cntNew[iPg] - iNew;
|
||||
}
|
||||
|
||||
editPage(apNew[iPg], iOld, iNew, nNewCell, apCell, szCell);
|
||||
rc = editPage(apNew[iPg], iOld, iNew, nNewCell, &b);
|
||||
if( rc ) goto balance_cleanup;
|
||||
abDone[iPg]++;
|
||||
apNew[iPg]->nFree = usableSpace-szNew[iPg];
|
||||
assert( apNew[iPg]->nOverflow==0 );
|
||||
@ -7455,7 +7570,7 @@ static int balance_nonroot(
|
||||
|
||||
assert( pParent->isInit );
|
||||
TRACE(("BALANCE: finished: old=%d new=%d cells=%d\n",
|
||||
nOld, nNew, nCell));
|
||||
nOld, nNew, b.nCell));
|
||||
|
||||
/* Free any old pages that were not reused as new pages.
|
||||
*/
|
||||
@ -7478,7 +7593,7 @@ static int balance_nonroot(
|
||||
** Cleanup before returning.
|
||||
*/
|
||||
balance_cleanup:
|
||||
sqlite3ScratchFree(apCell);
|
||||
sqlite3ScratchFree(b.apCell);
|
||||
for(i=0; i<nOld; i++){
|
||||
releasePage(apOld[i]);
|
||||
}
|
||||
@ -9340,6 +9455,7 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
|
||||
*/
|
||||
void sqlite3BtreeIncrblobCursor(BtCursor *pCur){
|
||||
pCur->curFlags |= BTCF_Incrblob;
|
||||
pCur->pBtree->hasIncrblobCur = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -353,6 +353,7 @@ struct Btree {
|
||||
u8 inTrans; /* TRANS_NONE, TRANS_READ or TRANS_WRITE */
|
||||
u8 sharable; /* True if we can share pBt with another db */
|
||||
u8 locked; /* True if db currently has pBt locked */
|
||||
u8 hasIncrblobCur; /* True if there are one or more Incrblob cursors */
|
||||
int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */
|
||||
int nBackup; /* Number of backup operations reading this btree */
|
||||
u32 iDataVersion; /* Combines with pBt->pPager->iDataVersion */
|
||||
|
16
src/select.c
16
src/select.c
@ -1392,10 +1392,13 @@ static const char *columnTypeImpl(
|
||||
** of the SELECT statement. Return the declaration type and origin
|
||||
** data for the result-set column of the sub-select.
|
||||
*/
|
||||
if( iCol>=0 && iCol<pS->pEList->nExpr ){
|
||||
if( iCol>=0 && ALWAYS(iCol<pS->pEList->nExpr) ){
|
||||
/* If iCol is less than zero, then the expression requests the
|
||||
** rowid of the sub-select or view. This expression is legal (see
|
||||
** test case misc2.2.2) - it always evaluates to NULL.
|
||||
**
|
||||
** The ALWAYS() is because iCol>=pS->pEList->nExpr will have been
|
||||
** caught already by name resolution.
|
||||
*/
|
||||
NameContext sNC;
|
||||
Expr *p = pS->pEList->a[iCol].pExpr;
|
||||
@ -1874,7 +1877,10 @@ static CollSeq *multiSelectCollSeq(Parse *pParse, Select *p, int iCol){
|
||||
pRet = 0;
|
||||
}
|
||||
assert( iCol>=0 );
|
||||
if( pRet==0 && iCol<p->pEList->nExpr ){
|
||||
/* iCol must be less than p->pEList->nExpr. Otherwise an error would
|
||||
** have been thrown during name resolution and we would not have gotten
|
||||
** this far */
|
||||
if( pRet==0 && ALWAYS(iCol<p->pEList->nExpr) ){
|
||||
pRet = sqlite3ExprCollSeq(pParse, p->pEList->a[iCol].pExpr);
|
||||
}
|
||||
return pRet;
|
||||
@ -2855,9 +2861,7 @@ static int multiSelectOrderBy(
|
||||
struct ExprList_item *pItem;
|
||||
for(i=0, pItem=pOrderBy->a; i<nOrderBy; i++, pItem++){
|
||||
assert( pItem->u.x.iOrderByCol>0 );
|
||||
/* assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr ) is also true
|
||||
** but only for well-formed SELECT statements. */
|
||||
testcase( pItem->u.x.iOrderByCol > p->pEList->nExpr );
|
||||
assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr );
|
||||
aPermute[i] = pItem->u.x.iOrderByCol - 1;
|
||||
}
|
||||
pKeyMerge = multiSelectOrderByKeyInfo(pParse, p, 1);
|
||||
@ -3427,10 +3431,10 @@ static int flattenSubquery(
|
||||
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct );
|
||||
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate );
|
||||
assert( pSub->pSrc!=0 );
|
||||
assert( pSub->pEList->nExpr==pSub1->pEList->nExpr );
|
||||
if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0
|
||||
|| (pSub1->pPrior && pSub1->op!=TK_ALL)
|
||||
|| pSub1->pSrc->nSrc<1
|
||||
|| pSub->pEList->nExpr!=pSub1->pEList->nExpr
|
||||
){
|
||||
return 0;
|
||||
}
|
||||
|
@ -582,13 +582,9 @@ int sqlite3VdbeExec(
|
||||
sqlite3VdbeIOTraceSql(p);
|
||||
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
|
||||
if( db->xProgress ){
|
||||
u32 iPrior = p->aCounter[SQLITE_STMTSTATUS_VM_STEP];
|
||||
assert( 0 < db->nProgressOps );
|
||||
nProgressLimit = (unsigned)p->aCounter[SQLITE_STMTSTATUS_VM_STEP];
|
||||
if( nProgressLimit==0 ){
|
||||
nProgressLimit = db->nProgressOps;
|
||||
}else{
|
||||
nProgressLimit %= (unsigned)db->nProgressOps;
|
||||
}
|
||||
nProgressLimit = db->nProgressOps - (iPrior % db->nProgressOps);
|
||||
}
|
||||
#endif
|
||||
#ifdef SQLITE_DEBUG
|
||||
|
121
test/fuzzcheck.c
121
test/fuzzcheck.c
@ -71,6 +71,11 @@
|
||||
#include <ctype.h>
|
||||
#include "sqlite3.h"
|
||||
|
||||
#ifdef __unix__
|
||||
# include <signal.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Files in the virtual file system.
|
||||
*/
|
||||
@ -139,6 +144,43 @@ static void fatalError(const char *zFormat, ...){
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Timeout handler
|
||||
*/
|
||||
#ifdef __unix__
|
||||
static void timeoutHandler(int NotUsed){
|
||||
(void)NotUsed;
|
||||
fatalError("timeout\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Set the an alarm to go off after N seconds. Disable the alarm
|
||||
** if N==0
|
||||
*/
|
||||
static void setAlarm(int N){
|
||||
#ifdef __unix__
|
||||
alarm(N);
|
||||
#else
|
||||
(void)N;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
|
||||
/*
|
||||
** This an SQL progress handler. After an SQL statement has run for
|
||||
** many steps, we want to interrupt it. This guards against infinite
|
||||
** loops from recursive common table expressions.
|
||||
**
|
||||
** *pVdbeLimitFlag is true if the --limit-vdbe command-line option is used.
|
||||
** In that case, hitting the progress handler is a fatal error.
|
||||
*/
|
||||
static int progressHandler(void *pVdbeLimitFlag){
|
||||
if( *(int*)pVdbeLimitFlag ) fatalError("too many VDBE cycles");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Reallocate memory. Show and error and quit if unable.
|
||||
*/
|
||||
@ -615,6 +657,31 @@ static void runSql(sqlite3 *db, const char *zSql, unsigned runFlags){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Rebuild the database file.
|
||||
**
|
||||
** (1) Remove duplicate entries
|
||||
** (2) Put all entries in order
|
||||
** (3) Vacuum
|
||||
*/
|
||||
static void rebuild_database(sqlite3 *db){
|
||||
int rc;
|
||||
rc = sqlite3_exec(db,
|
||||
"BEGIN;\n"
|
||||
"CREATE TEMP TABLE dbx AS SELECT DISTINCT dbcontent FROM db;\n"
|
||||
"DELETE FROM db;\n"
|
||||
"INSERT INTO db(dbid, dbcontent) SELECT NULL, dbcontent FROM dbx ORDER BY 2;\n"
|
||||
"DROP TABLE dbx;\n"
|
||||
"CREATE TEMP TABLE sx AS SELECT DISTINCT sqltext FROM xsql;\n"
|
||||
"DELETE FROM xsql;\n"
|
||||
"INSERT INTO xsql(sqlid,sqltext) SELECT NULL, sqltext FROM sx ORDER BY 2;\n"
|
||||
"DROP TABLE sx;\n"
|
||||
"COMMIT;\n"
|
||||
"PRAGMA page_size=1024;\n"
|
||||
"VACUUM;\n", 0, 0, 0);
|
||||
if( rc ) fatalError("cannot rebuild: %s", sqlite3_errmsg(db));
|
||||
}
|
||||
|
||||
/*
|
||||
** Print sketchy documentation for this utility program
|
||||
*/
|
||||
@ -626,15 +693,18 @@ static void showHelp(void){
|
||||
"Options:\n"
|
||||
" --cell-size-check Set the PRAGMA cell_size_check=ON\n"
|
||||
" --dbid N Use only the database where dbid=N\n"
|
||||
" --help Show this help text\n"
|
||||
" --help Show this help text\n"
|
||||
" -q Reduced output\n"
|
||||
" --quiet Reduced output\n"
|
||||
" --limit-vdbe Panic if an sync SQL runs for more than 100,000 cycles\n"
|
||||
" --load-sql ARGS... Load SQL scripts fro files into SOURCE-DB\n"
|
||||
" --load-db ARGS... Load template databases from files into SOURCE_DB\n"
|
||||
" -m TEXT Add a description to the database\n"
|
||||
" --native-vfs Use the native VFS for initially empty database files\n"
|
||||
" --rebuild Rebuild and vacuum the database file\n"
|
||||
" --result-trace Show the results of each SQL command\n"
|
||||
" --sqlid N Use only SQL where sqlid=N\n"
|
||||
" --timeline N Abort if any single test case needs more than N seconds\n"
|
||||
" -v Increased output\n"
|
||||
" --verbose Increased output\n"
|
||||
);
|
||||
@ -655,6 +725,9 @@ int main(int argc, char **argv){
|
||||
int onlySqlid = -1; /* --sqlid */
|
||||
int onlyDbid = -1; /* --dbid */
|
||||
int nativeFlag = 0; /* --native-vfs */
|
||||
int rebuildFlag = 0; /* --rebuild */
|
||||
int vdbeLimitFlag = 0; /* --limit-vdbe */
|
||||
int timeoutTest = 0; /* undocumented --timeout-test flag */
|
||||
int runFlags = 0; /* Flags sent to runSql() */
|
||||
char *zMsg = 0; /* Add this message */
|
||||
int nSrcDb = 0; /* Number of source databases */
|
||||
@ -664,8 +737,13 @@ int main(int argc, char **argv){
|
||||
char *zDbName = ""; /* Appreviated name of a source database */
|
||||
const char *zFailCode = 0; /* Value of the TEST_FAILURE environment variable */
|
||||
int cellSzCkFlag = 0; /* --cell-size-check */
|
||||
int sqlFuzz = 0; /* True for SQL fuzz testing. False for DB fuzz */
|
||||
int iTimeout = 120; /* Default 120-second timeout */
|
||||
|
||||
iBegin = timeOfDay();
|
||||
#ifdef __unix__
|
||||
signal(SIGALRM, timeoutHandler);
|
||||
#endif
|
||||
g.zArgv0 = argv[0];
|
||||
zFailCode = getenv("TEST_FAILURE");
|
||||
for(i=1; i<argc; i++){
|
||||
@ -684,6 +762,9 @@ int main(int argc, char **argv){
|
||||
showHelp();
|
||||
return 0;
|
||||
}else
|
||||
if( strcmp(z,"limit-vdbe")==0 ){
|
||||
vdbeLimitFlag = 1;
|
||||
}else
|
||||
if( strcmp(z,"load-sql")==0 ){
|
||||
zInsSql = "INSERT INTO xsql(sqltext) VALUES(CAST(readfile(?1) AS text))";
|
||||
iFirstInsArg = i+1;
|
||||
@ -705,6 +786,9 @@ int main(int argc, char **argv){
|
||||
quietFlag = 1;
|
||||
verboseFlag = 0;
|
||||
}else
|
||||
if( strcmp(z,"rebuild")==0 ){
|
||||
rebuildFlag = 1;
|
||||
}else
|
||||
if( strcmp(z,"result-trace")==0 ){
|
||||
runFlags |= SQL_OUTPUT;
|
||||
}else
|
||||
@ -712,6 +796,16 @@ int main(int argc, char **argv){
|
||||
if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]);
|
||||
onlySqlid = atoi(argv[++i]);
|
||||
}else
|
||||
if( strcmp(z,"timeout")==0 ){
|
||||
if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]);
|
||||
iTimeout = atoi(argv[++i]);
|
||||
}else
|
||||
if( strcmp(z,"timeout-test")==0 ){
|
||||
timeoutTest = 1;
|
||||
#ifndef __unix__
|
||||
fatalError("timeout is not available on non-unix systems");
|
||||
#endif
|
||||
}else
|
||||
if( strcmp(z,"verbose")==0 || strcmp(z,"v")==0 ){
|
||||
quietFlag = 0;
|
||||
verboseFlag = 1;
|
||||
@ -743,7 +837,7 @@ int main(int argc, char **argv){
|
||||
fatalError("cannot open source database %s - %s",
|
||||
azSrcDb[iSrcDb], sqlite3_errmsg(db));
|
||||
}
|
||||
rc = sqlite3_exec(db,
|
||||
rc = sqlite3_exec(db,
|
||||
"CREATE TABLE IF NOT EXISTS db(\n"
|
||||
" dbid INTEGER PRIMARY KEY, -- database id\n"
|
||||
" dbcontent BLOB -- database disk file image\n"
|
||||
@ -781,6 +875,7 @@ int main(int argc, char **argv){
|
||||
sqlite3_finalize(pStmt);
|
||||
rc = sqlite3_exec(db, "COMMIT", 0, 0, 0);
|
||||
if( rc ) fatalError("cannot commit the transaction: %s", sqlite3_errmsg(db));
|
||||
rebuild_database(db);
|
||||
sqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
@ -799,6 +894,7 @@ int main(int argc, char **argv){
|
||||
g.pFirstDb->id = 1;
|
||||
g.pFirstDb->seq = 0;
|
||||
g.nDb = 1;
|
||||
sqlFuzz = 1;
|
||||
}
|
||||
|
||||
/* Print the description, if there is one */
|
||||
@ -814,6 +910,16 @@ int main(int argc, char **argv){
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
|
||||
/* Rebuild the database, if requested */
|
||||
if( rebuildFlag ){
|
||||
if( !quietFlag ){
|
||||
printf("%s: rebuilding... ", zDbName);
|
||||
fflush(stdout);
|
||||
}
|
||||
rebuild_database(db);
|
||||
if( !quietFlag ) printf("done\n");
|
||||
}
|
||||
|
||||
/* Close the source database. Verify that no SQLite memory allocations are
|
||||
** outstanding.
|
||||
@ -859,7 +965,16 @@ int main(int argc, char **argv){
|
||||
rc = sqlite3_open_v2("main.db", &db, openFlags, zVfs);
|
||||
if( rc ) fatalError("cannot open inmem database");
|
||||
if( cellSzCkFlag ) runSql(db, "PRAGMA cell_size_check=ON", runFlags);
|
||||
runSql(db, (char*)pSql->a, runFlags);
|
||||
setAlarm(iTimeout);
|
||||
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
|
||||
if( sqlFuzz || vdbeLimitFlag ){
|
||||
sqlite3_progress_handler(db, 100000, progressHandler, &vdbeLimitFlag);
|
||||
}
|
||||
#endif
|
||||
do{
|
||||
runSql(db, (char*)pSql->a, runFlags);
|
||||
}while( timeoutTest );
|
||||
setAlarm(0);
|
||||
sqlite3_close(db);
|
||||
if( sqlite3_memory_used()>0 ) fatalError("memory leak");
|
||||
reformatVfs();
|
||||
|
Binary file not shown.
@ -189,8 +189,8 @@ do_test malloc5-3.1 {
|
||||
BEGIN;
|
||||
SELECT * FROM def;
|
||||
} db2
|
||||
sqlite3_release_memory
|
||||
} [expr $::pgalloc * 2]
|
||||
value_in_range [expr $::pgalloc*2] 0.99 [sqlite3_release_memory]
|
||||
} [value_in_range [expr $::pgalloc * 2] 0.99]
|
||||
do_test malloc5-3.2 {
|
||||
concat \
|
||||
[execsql {SELECT * FROM abc; COMMIT}] \
|
||||
|
@ -283,7 +283,124 @@ ifcapable trace {
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that the spellfix1 table supports conflict handling (OR REPLACE
|
||||
# and so on).
|
||||
#
|
||||
do_execsql_test 7.1 {
|
||||
CREATE VIRTUAL TABLE t4 USING spellfix1;
|
||||
PRAGMA table_info = t4;
|
||||
} {
|
||||
0 word {} 0 {} 0
|
||||
1 rank {} 0 {} 0
|
||||
2 distance {} 0 {} 0
|
||||
3 langid {} 0 {} 0
|
||||
4 score {} 0 {} 0
|
||||
5 matchlen {} 0 {} 0
|
||||
}
|
||||
|
||||
do_execsql_test 7.2.1 {
|
||||
INSERT INTO t4(rowid, word) VALUES(1, 'Archilles');
|
||||
INSERT INTO t4(rowid, word) VALUES(2, 'Pluto');
|
||||
INSERT INTO t4(rowid, word) VALUES(3, 'Atrides');
|
||||
INSERT OR REPLACE INTO t4(rowid, word) VALUES(2, 'Apollo');
|
||||
SELECT rowid, word FROM t4;
|
||||
} {
|
||||
1 Archilles 2 Apollo 3 Atrides
|
||||
}
|
||||
do_catchsql_test 7.2.2 {
|
||||
INSERT OR ABORT INTO t4(rowid, word) VALUES(1, 'Leto');
|
||||
} {1 {constraint failed}}
|
||||
do_catchsql_test 7.2.3 {
|
||||
INSERT OR ROLLBACK INTO t4(rowid, word) VALUES(3, 'Zeus');
|
||||
} {1 {constraint failed}}
|
||||
do_catchsql_test 7.2.4 {
|
||||
INSERT OR FAIL INTO t4(rowid, word) VALUES(3, 'Zeus');
|
||||
} {1 {constraint failed}}
|
||||
do_execsql_test 7.2.5 {
|
||||
INSERT OR IGNORE INTO t4(rowid, word) VALUES(3, 'Zeus');
|
||||
SELECT rowid, word FROM t4;
|
||||
} {
|
||||
1 Archilles 2 Apollo 3 Atrides
|
||||
}
|
||||
|
||||
do_execsql_test 7.3.1 {
|
||||
UPDATE OR REPLACE t4 SET rowid=3 WHERE rowid=1;
|
||||
SELECT rowid, word FROM t4;
|
||||
} {2 Apollo 3 Archilles}
|
||||
do_catchsql_test 7.3.2 {
|
||||
UPDATE OR ABORT t4 SET rowid=3 WHERE rowid=2;
|
||||
} {1 {constraint failed}}
|
||||
do_catchsql_test 7.3.3 {
|
||||
UPDATE OR ROLLBACK t4 SET rowid=3 WHERE rowid=2;
|
||||
} {1 {constraint failed}}
|
||||
do_catchsql_test 7.3.4 {
|
||||
UPDATE OR FAIL t4 SET rowid=3 WHERE rowid=2;
|
||||
} {1 {constraint failed}}
|
||||
do_execsql_test 7.3.5 {
|
||||
UPDATE OR IGNORE t4 SET rowid=3 WHERE rowid=2;
|
||||
SELECT rowid, word FROM t4;
|
||||
} {2 Apollo 3 Archilles}
|
||||
|
||||
do_execsql_test 7.4.1 {
|
||||
DELETE FROM t4;
|
||||
INSERT INTO t4(rowid, word) VALUES(10, 'Agamemnon');
|
||||
INSERT INTO t4(rowid, word) VALUES(20, 'Patroclus');
|
||||
INSERT INTO t4(rowid, word) VALUES(30, 'Chryses');
|
||||
|
||||
CREATE TABLE t5(i, w);
|
||||
INSERT INTO t5 VALUES(5, 'Poseidon');
|
||||
INSERT INTO t5 VALUES(20, 'Chronos');
|
||||
INSERT INTO t5 VALUES(30, 'Hera');
|
||||
}
|
||||
|
||||
db_save_and_close
|
||||
foreach {tn conflict err bRollback res} {
|
||||
0 "" {1 {constraint failed}} 0
|
||||
{10 Agamemnon 20 Patroclus 30 Chryses}
|
||||
1 "OR REPLACE" {0 {}} 0
|
||||
{5 Poseidon 10 Agamemnon 20 Chronos 30 Hera}
|
||||
2 "OR ABORT" {1 {constraint failed}} 0
|
||||
{10 Agamemnon 20 Patroclus 30 Chryses}
|
||||
3 "OR ROLLBACK" {1 {constraint failed}} 1
|
||||
{10 Agamemnon 20 Patroclus 30 Chryses}
|
||||
5 "OR IGNORE" {0 {}} 0
|
||||
{5 Poseidon 10 Agamemnon 20 Patroclus 30 Chryses}
|
||||
} {
|
||||
db_restore_and_reopen
|
||||
load_static_extension db spellfix nextchar
|
||||
|
||||
execsql BEGIN
|
||||
set sql "INSERT $conflict INTO t4(rowid, word) SELECT i, w FROM t5"
|
||||
do_catchsql_test 7.4.2.$tn.1 $sql $err
|
||||
do_execsql_test 7.4.2.$tn.2 { SELECT rowid, word FROM t4 } $res
|
||||
|
||||
do_test 7.4.2.$tn.3 { sqlite3_get_autocommit db } $bRollback
|
||||
catchsql ROLLBACK
|
||||
}
|
||||
|
||||
foreach {tn conflict err bRollback res} {
|
||||
0 "" {1 {constraint failed}} 0
|
||||
{10 Agamemnon 20 Patroclus 30 Chryses}
|
||||
1 "OR REPLACE" {0 {}} 0
|
||||
{15 Agamemnon 45 Chryses}
|
||||
2 "OR ABORT" {1 {constraint failed}} 0
|
||||
{10 Agamemnon 20 Patroclus 30 Chryses}
|
||||
3 "OR ROLLBACK" {1 {constraint failed}} 1
|
||||
{10 Agamemnon 20 Patroclus 30 Chryses}
|
||||
5 "OR IGNORE" {0 {}} 0
|
||||
{15 Agamemnon 20 Patroclus 45 Chryses}
|
||||
} {
|
||||
db_restore_and_reopen
|
||||
load_static_extension db spellfix nextchar
|
||||
|
||||
execsql BEGIN
|
||||
set sql "UPDATE $conflict t4 SET rowid=rowid + (rowid/2)"
|
||||
do_catchsql_test 7.5.2.$tn.1 $sql $err
|
||||
do_execsql_test 7.5.2.$tn.2 { SELECT rowid, word FROM t4 } $res
|
||||
do_test 7.5.2.$tn.3 { sqlite3_get_autocommit db } $bRollback
|
||||
catchsql ROLLBACK
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user