Changes to btree and pager in preparation for moving to run-time page

size determination. (CVS 1374)

FossilOrigin-Name: f63fb6dd4e8e33d4c1983396b1a0305836ee4df7
This commit is contained in:
drh 2004-05-14 01:58:11 +00:00
parent e7c8d58a1f
commit b6f4148607
6 changed files with 154 additions and 138 deletions

View File

@ -109,7 +109,7 @@ SRC = \
# Source code to the test files.
#
TESTSRC = \
TESTSRC_SUBSET = \
$(TOP)/src/os.c \
$(TOP)/src/pager.c \
$(TOP)/src/test1.c \
@ -118,7 +118,7 @@ TESTSRC = \
$(TOP)/src/test5.c \
$(TOP)/src/md5.c
TESTSRC_ORIG = \
TESTSRC = \
$(TOP)/src/btree.c \
$(TOP)/src/func.c \
$(TOP)/src/os.c \

View File

@ -1,5 +1,5 @@
C Changes\sto\smake\sregression\stests\sin\srowid.test\spass.\s(CVS\s1373)
D 2004-05-13T13:38:52
C Changes\sto\sbtree\sand\spager\sin\spreparation\sfor\smoving\sto\srun-time\spage\nsize\sdetermination.\s(CVS\s1374)
D 2004-05-14T01:58:12
F Makefile.in ab7b0d5118e2da97bac66be8684a1034e3500f5a
F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@ -15,7 +15,7 @@ F doc/lemon.html f0f682f50210928c07e562621c3b7e8ab912a538
F doc/report1.txt a031aaf37b185e4fa540223cb516d3bccec7eeac
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895
F ltmain.sh f6b283068efa69f06eb8aa1fe4bddfdbdeb35826
F main.mk 08bc0f1706964e029ef0319af0271ad79427cc71
F main.mk fbc906044c1b891be4bd9e48a74a9fd5bcd5fc81
F publish.sh 1cd5c982388560fa91eedf6a338e210f713b35c8
F spec.template a38492f1c1dd349fc24cb0565e08afc53045304b
F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea
@ -23,7 +23,7 @@ F sqlite.def fc4f5734786fe4743cfe2aa98eb2da4b089edb5f
F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2
F src/attach.c c315c58cb16fd6e913b3bfa6412aedecb4567fa5
F src/auth.c 5c2f0bea4729c98c2be3b69d6b466fc51448fe79
F src/btree.c bda57fe687cfa28905e5daebb0b706187649bd6b
F src/btree.c 2b85dc8f6b169bbe6bc0dab1730757f77d72811b
F src/btree.h 6f51ad0ffebfba71295fcacdbe86007512200050
F src/btree_rb.c 9d7973e266ee6f9c61ce592f68742ce9cd5b10e5
F src/build.c f25e4ac9f102efd70188bc09a459c2b461fe2135
@ -40,8 +40,8 @@ F src/main.c 4b82d7e78f4c9799343b02740a5ba9768d5e464d
F src/md5.c 8e39fdae6d8776b87558e91dcc94740c9b635a9c
F src/os.c ddcda92f7fd71b4513c57c1ec797917f206d504e
F src/os.h fbb2f6595fc34fa351830d88fe1c6b85118f0383
F src/pager.c fa60e566b370de0ed400859ba681134948b027bc
F src/pager.h 0c95b18f2785b58bfbb2b6f6a221f23caf378687
F src/pager.c 6ff6b906427d4824099140776cb8768f922f3dc5
F src/pager.h 78a00ac280899bcba1a89dc51585dcae6b7b3253
F src/parse.y d0258aa3cc8b0c5742b07b699d10fa98f3caea7d
F src/pragma.c 2ab2a12b62ec5370b9221f44b4743a633a90bfa8
F src/printf.c ef750e8e2398ca7e8b58be991075f08c6a7f0e53
@ -191,7 +191,7 @@ F www/sqlite.tcl 3c83b08cf9f18aa2d69453ff441a36c40e431604
F www/tclsqlite.tcl b9271d44dcf147a93c98f8ecf28c927307abd6da
F www/vdbe.tcl 9b9095d4495f37697fd1935d10e14c6015e80aa1
F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4
P da9b3dce334bde99360db45b4a3d41be519ec2d1
R b8ea62c9a072cbdad240805f1fb27046
U danielk1977
Z 37e1d6f2ec906eb83a05d292d4e622d2
P 790226c94493a6d58a7e52fd3ed35ef495fab11e
R 1732d9617f45de2d10dd8c0de779aa14
U drh
Z 4d4be7c36b4d139bc06baaeae3481d2d

View File

@ -1 +1 @@
790226c94493a6d58a7e52fd3ed35ef495fab11e
f63fb6dd4e8e33d4c1983396b1a0305836ee4df7

View File

@ -9,7 +9,7 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.133 2004/05/13 13:38:52 danielk1977 Exp $
** $Id: btree.c,v 1.134 2004/05/14 01:58:13 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
@ -267,7 +267,8 @@ struct Btree {
u8 inTrans; /* True if a transaction is in progress */
u8 inStmt; /* True if there is a checkpoint on the transaction */
u8 readOnly; /* True if the underlying file is readonly */
int pageSize; /* Number of usable bytes on each page */
int pageSize; /* Total number of bytes on a page */
int usableSize; /* Number of usable bytes on each page */
int maxLocal; /* Maximum local payload in non-LEAFDATA tables */
int minLocal; /* Minimum local payload in non-LEAFDATA tables */
int maxLeaf; /* Maximum local payload in a LEAFDATA table */
@ -332,17 +333,6 @@ static void put4byte(unsigned char *p, u32 v){
p[3] = v;
}
#if 0 /* NOT_USED */
static u64 get8byte(unsigned char *p){
u64 v = get4byte(p);
return (v<<32) | get4byte(&p[4]);
}
static void put8byte(unsigned char *p, u64 v){
put4byte(&p[4], v>>32);
put4byte(p, v);
}
#endif
/*
** Read a variable-length integer. Store the result in *pResult.
** Return the number of bytes in the integer.
@ -420,7 +410,7 @@ static void parseCell(
pBt = pPage->pBt;
if( pPage->leafData ){
minLocal = pBt->minLeaf;
maxLocal = pBt->pageSize - 23;
maxLocal = pBt->usableSize - 23;
}else{
minLocal = pBt->minLocal;
maxLocal = pBt->maxLocal;
@ -430,7 +420,7 @@ static void parseCell(
pInfo->iOverflow = 0;
pInfo->nSize = nPayload + n;
}else{
int surplus = minLocal + (nPayload - minLocal)%(pBt->pageSize - 4);
int surplus = minLocal + (nPayload - minLocal)%(pBt->usableSize - 4);
if( surplus <= maxLocal ){
pInfo->nLocal = surplus;
}else{
@ -464,13 +454,13 @@ static int cellSize(MemPage *pPage, unsigned char *pCell){
*/
#if defined(BTREE_DEBUG) && !defined(NDEBUG) && 0
static void _pageIntegrity(MemPage *pPage){
int pageSize;
int usableSize;
u8 *data;
int i, idx, c, pc, hdr, nFree;
u8 used[MX_PAGE_SIZE];
pageSize = pPage->pBt->pageSize;
assert( pPage->aData==&((unsigned char*)pPage)[-pageSize] );
usableSize = pPage->pBt->usableSize;
assert( pPage->aData==&((unsigned char*)pPage)[-pPage->pBt->pageSize] );
hdr = pPage->hdrOffset;
assert( hdr==(pPage->pgno==1 ? 100 : 0) );
assert( pPage->pgno==sqlite3pager_pagenumber(pPage->aData) );
@ -484,15 +474,15 @@ static void _pageIntegrity(MemPage *pPage){
!(pPage->zeroData || (!pPage->leaf && pPage->leafData)) );
}
data = pPage->aData;
memset(used, 0, pageSize);
memset(used, 0, usableSize);
for(i=0; i<hdr+10-pPage->leaf*4; i++) used[i] = 1;
nFree = 0;
pc = get2byte(&data[hdr+1]);
while( pc ){
int size;
assert( pc>0 && pc<pageSize-4 );
assert( pc>0 && pc<usableSize-4 );
size = get2byte(&data[pc+2]);
assert( pc+size<=pageSize );
assert( pc+size<=usableSize );
nFree += size;
for(i=pc; i<pc+size; i++){
assert( used[i]==0 );
@ -506,10 +496,10 @@ static void _pageIntegrity(MemPage *pPage){
while( pc ){
int size;
assert( pPage->isInit==0 || idx<pPage->nCell );
assert( pc>0 && pc<pageSize-4 );
assert( pc>0 && pc<usableSize-4 );
assert( pPage->isInit==0 || pPage->aCell[idx]==&data[pc] );
size = cellSize(pPage, &data[pc]);
assert( pc+size<=pageSize );
assert( pc+size<=usableSize );
for(i=pc; i<pc+size; i++){
assert( used[i]==0 );
used[i] = 1;
@ -519,7 +509,7 @@ static void _pageIntegrity(MemPage *pPage){
}
assert( idx==pPage->nCell );
nFree = 0;
for(i=0; i<pageSize; i++){
for(i=0; i<usableSize; i++){
assert( used[i]<=1 );
if( used[i]==0 ) nFree++;
}
@ -544,7 +534,7 @@ static void defragmentPage(MemPage *pPage){
assert( sqlite3pager_iswriteable(pPage->aData) );
assert( pPage->pBt!=0 );
assert( pPage->pBt->pageSize <= MX_PAGE_SIZE );
assert( pPage->pBt->usableSize <= MX_PAGE_SIZE );
assert( !pPage->needRelink );
assert( !pPage->isOverfull );
oldPage = pPage->aData;
@ -559,7 +549,7 @@ static void defragmentPage(MemPage *pPage){
pc = get2byte(&oldPage[addr]);
i = 0;
while( pc>0 ){
assert( n<pPage->pBt->pageSize );
assert( n<pPage->pBt->usableSize );
size = cellSize(pPage, &oldPage[pc]);
memcpy(&newPage[n], &oldPage[pc], size);
put2byte(&newPage[addr],n);
@ -570,13 +560,13 @@ static void defragmentPage(MemPage *pPage){
pc = get2byte(&oldPage[pc]);
}
assert( i==pPage->nCell );
leftover = pPage->pBt->pageSize - n;
leftover = pPage->pBt->usableSize - n;
assert( leftover>=0 );
assert( pPage->nFree==leftover );
if( leftover<4 ){
oldPage[hdr+5] = leftover;
leftover = 0;
n = pPage->pBt->pageSize;
n = pPage->pBt->usableSize;
}
memcpy(&oldPage[hdr], &newPage[hdr], n-hdr);
if( leftover==0 ){
@ -628,11 +618,11 @@ static int allocateSpace(MemPage *pPage, int nByte){
addr = hdr+1;
pc = get2byte(&data[addr]);
assert( addr<pc );
assert( pc<=pPage->pBt->pageSize-4 );
assert( pc<=pPage->pBt->usableSize-4 );
while( (size = get2byte(&data[pc+2]))<nByte ){
addr = pc;
pc = get2byte(&data[addr]);
assert( pc<=pPage->pBt->pageSize-4 );
assert( pc<=pPage->pBt->usableSize-4 );
assert( pc>=addr+size+4 || pc==0 );
if( pc==0 ){
assert( (cnt++)==0 );
@ -643,7 +633,7 @@ static int allocateSpace(MemPage *pPage, int nByte){
}
}
assert( pc>0 && size>=nByte );
assert( pc+size<=pPage->pBt->pageSize );
assert( pc+size<=pPage->pBt->usableSize );
if( size>nByte+4 ){
int newStart = pc+nByte;
put2byte(&data[addr], newStart);
@ -677,17 +667,17 @@ static void freeSpace(MemPage *pPage, int start, int size){
assert( pPage->pBt!=0 );
assert( sqlite3pager_iswriteable(data) );
assert( start>=pPage->hdrOffset+6+(pPage->leaf?0:4) );
assert( end<=pPage->pBt->pageSize );
assert( end<=pPage->pBt->usableSize );
if( size<4 ) size = 4;
/* Add the space back into the linked list of freeblocks */
addr = pPage->hdrOffset + 1;
while( (pbegin = get2byte(&data[addr]))<start && pbegin>0 ){
assert( pbegin<=pPage->pBt->pageSize-4 );
assert( pbegin<=pPage->pBt->usableSize-4 );
assert( pbegin>addr );
addr = pbegin;
}
assert( pbegin<=pPage->pBt->pageSize-4 );
assert( pbegin<=pPage->pBt->usableSize-4 );
assert( pbegin>addr || pbegin==0 );
put2byte(&data[addr], start);
put2byte(&data[start], pbegin);
@ -699,7 +689,7 @@ static void freeSpace(MemPage *pPage, int start, int size){
while( (pbegin = get2byte(&data[addr]))>0 ){
int pnext, psize;
assert( pbegin>addr );
assert( pbegin<pPage->pBt->pageSize-4 );
assert( pbegin<pPage->pBt->usableSize-4 );
pnext = get2byte(&data[pbegin]);
psize = get2byte(&data[pbegin+2]);
if( pbegin + psize + 3 >= pnext && pnext>0 ){
@ -750,7 +740,7 @@ static int initPage(
){
int c, pc, i, hdr;
unsigned char *data;
int pageSize;
int usableSize;
int sumCell = 0; /* Total size of all cells */
assert( pPage->pBt!=0 );
@ -776,13 +766,13 @@ static int initPage(
pPage->isOverfull = 0;
pPage->needRelink = 0;
pPage->idxShift = 0;
pageSize = pPage->pBt->pageSize;
usableSize = pPage->pBt->usableSize;
/* Initialize the cell count and cell pointers */
pc = get2byte(&data[hdr+3]);
while( pc>0 ){
if( pc>=pageSize ) return SQLITE_CORRUPT;
if( pPage->nCell>pageSize ) return SQLITE_CORRUPT;
if( pc>=usableSize ) return SQLITE_CORRUPT;
if( pPage->nCell>usableSize ) return SQLITE_CORRUPT;
pPage->nCell++;
pc = get2byte(&data[pc]);
}
@ -801,18 +791,18 @@ static int initPage(
pc = get2byte(&data[hdr+1]);
while( pc>0 ){
int next, size;
if( pc>=pageSize ) return SQLITE_CORRUPT;
if( pc>=usableSize ) return SQLITE_CORRUPT;
next = get2byte(&data[pc]);
size = get2byte(&data[pc+2]);
if( next>0 && next<=pc+size+3 ) return SQLITE_CORRUPT;
pPage->nFree += size;
pc = next;
}
if( pPage->nFree>=pageSize ) return SQLITE_CORRUPT;
if( pPage->nFree>=usableSize ) return SQLITE_CORRUPT;
/* Sanity check: Cells and freespace and header must sum to the size
** a page. */
if( sumCell+pPage->nFree+hdr+10-pPage->leaf*4 != pageSize ){
if( sumCell+pPage->nFree+hdr+10-pPage->leaf*4 != usableSize ){
return SQLITE_CORRUPT;
}
@ -834,16 +824,16 @@ static void zeroPage(MemPage *pPage, int flags){
assert( sqlite3pager_pagenumber(data)==pPage->pgno );
assert( &data[pBt->pageSize] == (unsigned char*)pPage );
assert( sqlite3pager_iswriteable(data) );
memset(&data[hdr], 0, pBt->pageSize - hdr);
memset(&data[hdr], 0, pBt->usableSize - hdr);
data[hdr] = flags;
first = hdr + 6 + 4*((flags&PTF_LEAF)==0);
put2byte(&data[hdr+1], first);
put2byte(&data[first+2], pBt->pageSize - first);
put2byte(&data[first+2], pBt->usableSize - first);
sqliteFree(pPage->aCell);
pPage->aCell = 0;
pPage->nCell = 0;
pPage->nCellAlloc = 0;
pPage->nFree = pBt->pageSize - first;
pPage->nFree = pBt->usableSize - first;
pPage->intKey = (flags & (PTF_INTKEY|PTF_LEAFDATA))!=0;
pPage->zeroData = (flags & PTF_ZERODATA)!=0;
pPage->leafData = (flags & PTF_LEAFDATA)!=0;
@ -913,8 +903,8 @@ static void releasePage(MemPage *pPage){
** reaches zero. We need to unref the pParent pointer when that
** happens.
*/
static void pageDestructor(void *pData){
MemPage *pPage = (MemPage*)&((char*)pData)[SQLITE_PAGE_SIZE];
static void pageDestructor(void *pData, int pageSize){
MemPage *pPage = (MemPage*)&((char*)pData)[pageSize];
assert( pPage->isInit==0 || pPage->needRelink==0 );
if( pPage->pParent ){
MemPage *pParent = pPage->pParent;
@ -978,29 +968,11 @@ int sqlite3BtreeOpen(
pBt->pPage1 = 0;
pBt->readOnly = sqlite3pager_isreadonly(pBt->pPager);
pBt->pageSize = SQLITE_PAGE_SIZE; /* FIX ME - read from header */
pBt->usableSize = pBt->pageSize;
pBt->maxEmbedFrac = 64; /* FIX ME - read from header */
pBt->minEmbedFrac = 32; /* FIX ME - read from header */
pBt->minLeafFrac = 32; /* FIX ME - read from header */
/* maxLocal is the maximum amount of payload to store locally for
** a cell. Make sure it is small enough so that at least minFanout
** cells can will fit on one page. We assume a 10-byte page header.
** Besides the payload, the cell must store:
** 2-byte pointer to next cell
** 4-byte child pointer
** 9-byte nKey value
** 4-byte nData value
** 4-byte overflow page pointer
** So a cell consists of a header which is as much as 19 bytes long,
** 0 to N bytes of payload, and an optional 4 byte overflow page pointer.
*/
assert(pBt->maxEmbedFrac>0 && 255/pBt->maxEmbedFrac>=3 );
pBt->maxLocal = (pBt->pageSize-10)*pBt->maxEmbedFrac/255 - 23;
pBt->minLocal = (pBt->pageSize-10)*pBt->minEmbedFrac/255 - 23;
pBt->maxLeaf = pBt->pageSize - 33;
pBt->minLeaf = (pBt->pageSize-10)*pBt->minLeafFrac/255 - 23;
assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE );
*ppBtree = pBt;
return SQLITE_OK;
}
@ -1071,15 +1043,47 @@ static int lockBtree(Btree *pBt){
/* Do some checking to help insure the file we opened really is
** a valid database file.
*/
rc = SQLITE_NOTADB;
if( sqlite3pager_pagecount(pBt->pPager)>0 ){
if( memcmp(pPage1->aData, zMagicHeader, 16)!=0 ){
rc = SQLITE_NOTADB;
u8 *page1 = pPage1->aData;
if( memcmp(page1, zMagicHeader, 16)!=0 ){
goto page1_init_failed;
}
/*** TBD: Other header checks such as page size ****/
if( page1[18]>1 || page1[19]>1 ){
goto page1_init_failed;
}
pBt->pageSize = get2byte(&page1[16]);
pBt->usableSize = pBt->pageSize - page1[20];
if( pBt->usableSize<500 ){
goto page1_init_failed;
}
pBt->maxEmbedFrac = page1[21];
pBt->minEmbedFrac = page1[22];
pBt->minLeafFrac = page1[23];
}
/* maxLocal is the maximum amount of payload to store locally for
** a cell. Make sure it is small enough so that at least minFanout
** cells can will fit on one page. We assume a 10-byte page header.
** Besides the payload, the cell must store:
** 2-byte pointer to next cell
** 4-byte child pointer
** 9-byte nKey value
** 4-byte nData value
** 4-byte overflow page pointer
** So a cell consists of a header which is as much as 19 bytes long,
** 0 to N bytes of payload, and an optional 4 byte overflow page pointer.
*/
pBt->maxLocal = (pBt->usableSize-10)*pBt->maxEmbedFrac/255 - 23;
pBt->minLocal = (pBt->usableSize-10)*pBt->minEmbedFrac/255 - 23;
pBt->maxLeaf = pBt->usableSize - 33;
pBt->minLeaf = (pBt->usableSize-10)*pBt->minLeafFrac/255 - 23;
if( pBt->minLocal>pBt->maxLocal || pBt->maxLocal<0 ){
goto page1_init_failed;
}
assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE );
pBt->pPage1 = pPage1;
return rc;
return SQLITE_OK;
page1_init_failed:
releasePage(pPage1);
@ -1122,10 +1126,14 @@ static int newDatabase(Btree *pBt){
if( rc ) return rc;
memcpy(data, zMagicHeader, sizeof(zMagicHeader));
assert( sizeof(zMagicHeader)==16 );
put2byte(&data[16], SQLITE_PAGE_SIZE);
put2byte(&data[16], pBt->pageSize);
data[18] = 1;
data[19] = 1;
put2byte(&data[22], (SQLITE_PAGE_SIZE-10)/4-12);
data[20] = pBt->pageSize - pBt->usableSize;
data[21] = pBt->maxEmbedFrac;
data[22] = pBt->minEmbedFrac;
data[23] = pBt->minLeafFrac;
memset(&data[24], 0, 100-24);
zeroPage(pP1, PTF_INTKEY|PTF_LEAF );
return SQLITE_OK;
}
@ -1605,7 +1613,7 @@ static int getPayload(
if( amt>0 ){
nextPage = get4byte(&aPayload[info.nLocal]);
}
ovflSize = pBt->pageSize - 4;
ovflSize = pBt->usableSize - 4;
while( amt>0 && nextPage ){
rc = sqlite3pager_get(pBt->pPager, nextPage, (void**)&aPayload);
if( rc!=0 ){
@ -2330,7 +2338,7 @@ static int freePage(MemPage *pPage){
rc = getPage(pBt, get4byte(&pPage1->aData[32]), &pTrunk);
if( rc ) return rc;
k = get4byte(&pTrunk->aData[4]);
if( k==pBt->pageSize/4 - 8 ){
if( k==pBt->usableSize/4 - 8 ){
/* The trunk is full. Turn the page being freed into a new
** trunk page with no leaves. */
rc = sqlite3pager_write(pPage->aData);
@ -2458,7 +2466,7 @@ static int fillInCell(
pPrior = pOvfl->aData;
put4byte(pPrior, 0);
pPayload = &pOvfl->aData[4];
spaceLeft = pBt->pageSize - 4;
spaceLeft = pBt->usableSize - 4;
}
n = nPayload;
if( n>spaceLeft ) n = spaceLeft;
@ -2491,7 +2499,7 @@ static void reparentPage(Btree *pBt, Pgno pgno, MemPage *pNewParent, int idx){
assert( pBt->pPager!=0 );
aData = sqlite3pager_lookup(pBt->pPager, pgno);
if( aData ){
pThis = (MemPage*)&aData[pBt->pageSize];
pThis = (MemPage*)&aData[pBt->usableSize];
if( pThis->isInit ){
if( pThis->pParent!=pNewParent ){
if( pThis->pParent ) sqlite3pager_unref(pThis->pParent->aData);
@ -2548,10 +2556,10 @@ static void dropCell(MemPage *pPage, int idx, int sz){
assert( sz==cellSize(pPage, pPage->aCell[idx]) );
assert( sqlite3pager_iswriteable(pPage->aData) );
assert( pPage->aCell[idx]>=pPage->aData );
assert( pPage->aCell[idx]<=&pPage->aData[pPage->pBt->pageSize-sz] );
assert( pPage->aCell[idx]<=&pPage->aData[pPage->pBt->usableSize-sz] );
data = pPage->aData;
pc = Addr(pPage->aCell[idx]) - Addr(data);
assert( pc>pPage->hdrOffset && pc+sz<=pPage->pBt->pageSize );
assert( pc>pPage->hdrOffset && pc+sz<=pPage->pBt->usableSize );
freeSpace(pPage, pc, sz);
for(j=idx; j<pPage->nCell-1; j++){
pPage->aCell[j] = pPage->aCell[j+1];
@ -2654,7 +2662,7 @@ static void relinkCellList(MemPage *pPage){
idxFrom = pPage->hdrOffset+3;
for(i=0; i<pPage->nCell; i++){
int idx = Addr(pPage->aCell[i]) - Addr(pPage->aData);
assert( idx>pPage->hdrOffset && idx<pPage->pBt->pageSize );
assert( idx>pPage->hdrOffset && idx<pPage->pBt->usableSize );
put2byte(&pPage->aData[idxFrom], idx);
idxFrom = idx;
}
@ -2681,15 +2689,15 @@ static void relinkCellList(MemPage *pPage){
static void movePage(MemPage *pTo, MemPage *pFrom){
uptr from, to;
int i;
int pageSize;
int usableSize;
int ofst;
assert( pTo->hdrOffset==0 );
assert( pFrom->isInit );
ofst = pFrom->hdrOffset;
pageSize = pFrom->pBt->pageSize;
usableSize = pFrom->pBt->usableSize;
sqliteFree(pTo->aCell);
memcpy(pTo->aData, &pFrom->aData[ofst], pageSize - ofst);
memcpy(pTo->aData, &pFrom->aData[ofst], usableSize - ofst);
memcpy(pTo, pFrom, offsetof(MemPage, aData));
pFrom->isInit = 0;
pFrom->aCell = 0;
@ -2700,7 +2708,7 @@ static void movePage(MemPage *pTo, MemPage *pFrom){
from = Addr(&pFrom->aData[ofst]);
for(i=0; i<pTo->nCell; i++){
uptr x = Addr(pTo->aCell[i]);
if( x>from && x<from+pageSize-ofst ){
if( x>from && x<from+usableSize-ofst ){
*((uptr*)&pTo->aCell[i]) = x + to - from;
}
}
@ -2772,6 +2780,7 @@ static int balance(MemPage *pPage){
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 iSpace = 0; /* First unused byte of aSpace[] */
MemPage *extraUnref = 0; /* Unref this page if not zero */
MemPage *apOld[NB]; /* pPage and up to two siblings */
Pgno pgnoOld[NB]; /* Page numbers for each page in apOld[] */
@ -2780,13 +2789,12 @@ static int balance(MemPage *pPage){
Pgno pgnoNew[NB+1]; /* Page numbers for each page in apNew[] */
int idxDiv[NB]; /* Indices of divider cells in pParent */
u8 *apDiv[NB]; /* Divider cells in pParent */
u8 aTemp[NB][MX_CELL_SIZE]; /* Temporary holding area for apDiv[] */
u8 aInsBuf[NB][MX_CELL_SIZE];/* Space to hold dividers cells during insert */
int cntNew[NB+1]; /* Index in aCell[] of cell after i-th page */
int szNew[NB+1]; /* Combined size of cells place on i-th page */
u8 *apCell[(MX_CELL+2)*NB]; /* All cells from pages being balanced */
int szCell[(MX_CELL+2)*NB]; /* Local size of all cells */
u8 aCopy[NB][MX_PAGE_SIZE+sizeof(MemPage)]; /* Space for apCopy[] */
u8 aSpace[MX_PAGE_SIZE*4]; /* Space to copies of divider cells */
/*
** Return without doing any work if pPage is neither overfull nor
@ -2795,7 +2803,7 @@ static int balance(MemPage *pPage){
assert( pPage->isInit );
assert( sqlite3pager_iswriteable(pPage->aData) );
pBt = pPage->pBt;
if( !pPage->isOverfull && pPage->nFree<pBt->pageSize*2/3 && pPage->nCell>=2){
if( !pPage->isOverfull && pPage->nFree<pBt->usableSize*2/3 && pPage->nCell>=2){
relinkCellList(pPage);
return SQLITE_OK;
}
@ -2851,7 +2859,7 @@ static int balance(MemPage *pPage){
TRACE(("BALANCE: child %d will not fit on page 1\n", pChild->pgno));
}
}else{
memcpy(pPage->aData, pChild->aData, pBt->pageSize);
memcpy(pPage->aData, pChild->aData, pBt->usableSize);
pPage->isInit = 0;
pPage->pParent = 0;
rc = initPage(pPage, 0);
@ -2974,7 +2982,7 @@ static int balance(MemPage *pPage){
*/
for(i=0; i<nOld; i++){
MemPage *p = apCopy[i] = (MemPage*)&aCopy[i+1][-sizeof(MemPage)];
p->aData = &((u8*)p)[-pBt->pageSize];
p->aData = &((u8*)p)[-pBt->usableSize];
p->aCell = 0;
p->hdrOffset = 0;
movePage(p, apOld[i]);
@ -2983,11 +2991,12 @@ static int balance(MemPage *pPage){
/*
** Load pointers to all cells on sibling pages and the divider cells
** into the local apCell[] array. Make copies of the divider cells
** into aTemp[] and remove the the divider Cells from pParent.
** into space obtained form aSpace[] and remove the the divider Cells
** 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 aTemp[]. In this wall, all cells in apCell[] are without
** into aSpace[]. In this wall, all cells in apCell[] are without
** child pointers. If siblings are not leaves, then all cell in
** apCell[] include child pointers. Either way, all cells in apCell[]
** are alike.
@ -3003,16 +3012,20 @@ static int balance(MemPage *pPage){
nCell++;
}
if( i<nOld-1 ){
int sz = cellSize(pParent, apDiv[i]);
if( leafData ){
int sz = cellSize(pParent, apDiv[i]);
dropCell(pParent, nxDiv, sz);
}else{
szCell[nCell] = cellSize(pParent, apDiv[i]);
memcpy(aTemp[i], apDiv[i], szCell[nCell]);
apCell[nCell] = &aTemp[i][leafCorrection];
dropCell(pParent, nxDiv, szCell[nCell]);
u8 *pTemp;
szCell[nCell] = sz;
pTemp = &aSpace[iSpace];
iSpace += sz;
assert( iSpace<=sizeof(aSpace) );
memcpy(pTemp, apDiv[i], sz);
apCell[nCell] = pTemp+leafCorrection;
dropCell(pParent, nxDiv, sz);
szCell[nCell] -= leafCorrection;
assert( get4byte(&aTemp[i][2])==pgnoOld[i] );
assert( get4byte(pTemp+2)==pgnoOld[i] );
if( !pOld->leaf ){
assert( leafCorrection==0 );
/* The right pointer of the child page pOld becomes the left
@ -3036,7 +3049,7 @@ static int balance(MemPage *pPage){
** This little patch of code is critical for keeping the tree
** balanced.
*/
usableSpace = pBt->pageSize - 10 + leafCorrection;
usableSpace = pBt->usableSize - 10 + leafCorrection;
for(subtotal=k=i=0; i<nCell; i++){
subtotal += szCell[i];
if( subtotal > usableSpace ){
@ -3165,12 +3178,16 @@ static int balance(MemPage *pPage){
CellInfo info;
j--;
parseCell(pNew, apCell[j], &info);
pCell = aInsBuf[i];
pCell = &aSpace[iSpace];
fillInCell(pParent, pCell, 0, info.nKey, 0, 0, &sz);
iSpace += sz;
assert( iSpace<=sizeof(aSpace) );
pTemp = 0;
}else{
pCell -= 4;
pTemp = aInsBuf[i];
pTemp = &aSpace[iSpace];
iSpace += sz;
assert( iSpace<=sizeof(aSpace) );
}
insertCell(pParent, nxDiv, pCell, sz, pTemp);
put4byte(&pParent->aCell[nxDiv][2], pNew->pgno);
@ -3630,7 +3647,7 @@ int sqlite3BtreePageDump(Btree *pBt, int pgno, int recursive){
i = 0;
assert( hdr == (pgno==1 ? 100 : 0) );
idx = get2byte(&data[hdr+3]);
while( idx>0 && idx<=pBt->pageSize ){
while( idx>0 && idx<=pBt->usableSize ){
CellInfo info;
Pgno child;
unsigned char *pCell = &data[idx];
@ -3672,7 +3689,7 @@ int sqlite3BtreePageDump(Btree *pBt, int pgno, int recursive){
nFree = 0;
i = 0;
idx = get2byte(&data[hdr+1]);
while( idx>0 && idx<pPage->pBt->pageSize ){
while( idx>0 && idx<pPage->pBt->usableSize ){
int sz = get2byte(&data[idx+2]);
sprintf(range,"%d..%d", idx, idx+sz-1);
nFree += sz;
@ -3686,7 +3703,7 @@ int sqlite3BtreePageDump(Btree *pBt, int pgno, int recursive){
}
if( recursive && !pPage->leaf ){
idx = get2byte(&data[hdr+3]);
while( idx>0 && idx<pBt->pageSize ){
while( idx>0 && idx<pBt->usableSize ){
unsigned char *pCell = &data[idx];
sqlite3BtreePageDump(pBt, get4byte(&pCell[2]), 1);
idx = get2byte(pCell);
@ -3735,7 +3752,7 @@ int sqlite3BtreeCursorInfo(BtCursor *pCur, int *aResult){
aResult[4] = pPage->nFree;
cnt = 0;
idx = get2byte(&pPage->aData[pPage->hdrOffset+1]);
while( idx>0 && idx<pPage->pBt->pageSize ){
while( idx>0 && idx<pPage->pBt->usableSize ){
cnt++;
idx = get2byte(&pPage->aData[idx]);
}
@ -3880,7 +3897,7 @@ static int checkTreePage(
u8 *data;
BtCursor cur;
Btree *pBt;
int maxLocal, pageSize;
int maxLocal, usableSize;
char zMsg[100];
char zContext[100];
char hit[MX_PAGE_SIZE];
@ -3888,7 +3905,7 @@ static int checkTreePage(
/* Check that the page exists
*/
cur.pBt = pBt = pCheck->pBt;
pageSize = pBt->pageSize;
usableSize = pBt->usableSize;
if( iPage==0 ) return 0;
if( checkRef(pCheck, iPage, zParentContext) ) return 0;
if( (rc = getPage(pBt, (Pgno)iPage, &pPage))!=0 ){
@ -3921,7 +3938,7 @@ static int checkTreePage(
sz = info.nData;
if( !pPage->intKey ) sz += info.nKey;
if( sz>info.nLocal ){
int nPage = (sz - info.nLocal + pageSize - 5)/(pageSize - 4);
int nPage = (sz - info.nLocal + usableSize - 5)/(usableSize - 4);
checkList(pCheck, 0, get4byte(&pCell[info.iOverflow]),nPage,zContext);
}
@ -3944,23 +3961,23 @@ static int checkTreePage(
/* Check for complete coverage of the page
*/
memset(hit, 0, pageSize);
memset(hit, 0, usableSize);
memset(hit, 1, pPage->hdrOffset+10-4*(pPage->leaf));
data = pPage->aData;
hdr = pPage->hdrOffset;
for(cnt=0, i=get2byte(&data[hdr+3]); i>0 && i<pageSize && cnt<10000; cnt++){
for(cnt=0, i=get2byte(&data[hdr+3]); i>0 && i<usableSize && cnt<10000; cnt++){
int size = cellSize(pPage, &data[i]);
int j;
for(j=i+size-1; j>=i; j--) hit[j]++;
i = get2byte(&data[i]);
}
for(cnt=0, i=get2byte(&data[hdr+1]); i>0 && i<pageSize && cnt<10000; cnt++){
for(cnt=0, i=get2byte(&data[hdr+1]); i>0 && i<usableSize && cnt<10000; cnt++){
int size = get2byte(&data[i+2]);
int j;
for(j=i+size-1; j>=i; j--) hit[j]++;
i = get2byte(&data[i]);
}
for(i=cnt=0; i<pageSize; i++){
for(i=cnt=0; i<usableSize; i++){
if( hit[i]==0 ){
cnt++;
}else if( hit[i]>1 ){
@ -4070,7 +4087,7 @@ int sqlite3BtreeCopyFile(Btree *pBtTo, Btree *pBtFrom){
if( !pBtTo->inTrans || !pBtFrom->inTrans ) return SQLITE_ERROR;
if( pBtTo->pCursor ) return SQLITE_BUSY;
memcpy(pBtTo->pPage1, pBtFrom->pPage1, pBtFrom->pageSize);
memcpy(pBtTo->pPage1, pBtFrom->pPage1, pBtFrom->usableSize);
rc = sqlite3pager_overwrite(pBtTo->pPager, 1, pBtFrom->pPage1);
nToPage = sqlite3pager_pagecount(pBtTo->pPager);
nPage = sqlite3pager_pagecount(pBtFrom->pPager);

View File

@ -18,7 +18,7 @@
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.107 2004/05/12 13:30:08 drh Exp $
** @(#) $Id: pager.c,v 1.108 2004/05/14 01:58:13 drh Exp $
*/
#include "os.h" /* Must be first to enable large file support */
#include "sqliteInt.h"
@ -181,14 +181,14 @@ struct Pager {
u32 cksumInit; /* Quasi-random value added to every checksum */
int stmtNRec; /* Number of records in stmt subjournal */
int nExtra; /* Add this many bytes to each in-memory page */
void (*xDestructor)(void*); /* Call this routine when freeing pages */
void (*xDestructor)(void*,int); /* Call this routine when freeing pages */
int pageSize; /* Number of bytes in a page */
int nPage; /* Total number of in-memory pages */
int nRef; /* Number of in-memory pages with PgHdr.nRef>0 */
int mxPage; /* Maximum number of pages to hold in cache */
int nHit, nMiss, nOvfl; /* Cache hits, missing, and LRU overflows */
void (*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */
void *pCodecArg; /* First argument to xCodec() */
int pageSize; /* Page size in bytes */
u8 journalOpen; /* True if journal file descriptors is valid */
u8 journalStarted; /* True if header of journal is synced */
u8 useJournal; /* Use a rollback journal on this file */
@ -587,14 +587,16 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int format){
** 1 which is held in use in order to keep the lock on the database
** active.
*/
void *pData;
assert( pPg->nRef==0 || pPg->pgno==1 );
memcpy(PGHDR_TO_DATA(pPg), pgRec.aData, SQLITE_PAGE_SIZE);
pData = PGHDR_TO_DATA(pPg);
memcpy(pData, pgRec.aData, pPager->pageSize);
if( pPager->xDestructor ){
pPager->xDestructor(PGHDR_TO_DATA(pPg));
pPager->xDestructor(pData, pPager->pageSize);
}
pPg->dirty = 0;
pPg->needSync = 0;
CODEC(pPager, PGHDR_TO_DATA(pPg), pPg->pgno, 3);
CODEC(pPager, pData, pPg->pgno, 3);
}
return rc;
}
@ -1034,7 +1036,7 @@ int sqlite3pager_open(
** The destructor is not called as a result sqlite3pager_close().
** Destructors are only called by sqlite3pager_unref().
*/
void sqlite3pager_set_destructor(Pager *pPager, void (*xDesc)(void*)){
void sqlite3pager_set_destructor(Pager *pPager, void (*xDesc)(void*,int)){
pPager->xDestructor = xDesc;
}
@ -1708,7 +1710,7 @@ int sqlite3pager_unref(void *pData){
pPager->pFirstSynced = pPg;
}
if( pPager->xDestructor ){
pPager->xDestructor(pData);
pPager->xDestructor(pData, pPager->pageSize);
}
/* When all pages reach the freelist, drop the read lock from

View File

@ -13,11 +13,11 @@
** subsystem. The page cache subsystem reads and writes a file a page
** at a time and provides a journal for rollback.
**
** @(#) $Id: pager.h,v 1.28 2004/05/08 08:23:30 danielk1977 Exp $
** @(#) $Id: pager.h,v 1.29 2004/05/14 01:58:13 drh Exp $
*/
/*
** The size of one page
** The size of a page.
**
** You can change this value to another (reasonable) value you want.
** It need not be a power of two, though the interface to the disk
@ -71,7 +71,7 @@ typedef struct Pager Pager;
*/
int sqlite3pager_open(Pager **ppPager, const char *zFilename,
int nPage, int nExtra, int useJournal);
void sqlite3pager_set_destructor(Pager*, void(*)(void*));
void sqlite3pager_set_destructor(Pager*, void(*)(void*,int));
void sqlite3pager_set_cachesize(Pager*, int);
int sqlite3pager_close(Pager *pPager);
int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage);
@ -103,6 +103,3 @@ void sqlite3pager_set_codec(Pager*,void(*)(void*,void*,Pgno,int),void*);
void sqlite3pager_refdump(Pager*);
int pager3_refinfo_enable;
#endif