Merge latest trunk changes into this branch.

FossilOrigin-Name: fcd8f7ce601729dc51d880d16b97040c1be16aa2
This commit is contained in:
dan 2015-06-25 20:16:23 +00:00
commit f32fa3116b
11 changed files with 597 additions and 222 deletions

View File

@ -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
);
}

View File

@ -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

View File

@ -1 +1 @@
46e86b0637248fb4d623c97778cc041eabe3636c
fcd8f7ce601729dc51d880d16b97040c1be16aa2

View File

@ -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

View File

@ -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 */

View File

@ -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;
}

View File

@ -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

View File

@ -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.

View File

@ -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}] \

View File

@ -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