Index: src/btree.c ================================================================== --- src/btree.c +++ src/btree.c @@ -7,11 +7,11 @@ ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btree.c,v 1.192 2004/10/05 02:41:42 drh Exp $ +** $Id: btree.c,v 1.193 2004/10/22 16:22:58 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to ** ** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3: @@ -209,10 +209,15 @@ #include "pager.h" #include "btree.h" #include "os.h" #include +/* +** This macro rounds values up so that if the value is an address it +** is guaranteed to be an address that is aligned to an 8-byte boundary. +*/ +#define FORCE_ALIGNMENT(X) (((X)+7)&~7) /* The following value is the maximum cell size assuming a maximum page ** size give above. */ #define MX_CELL_SIZE(pBt) (pBt->pageSize-8) @@ -298,10 +303,11 @@ u8 maxEmbedFrac; /* Maximum payload as % of total page size */ u8 minEmbedFrac; /* Minimum payload as % of total page size */ u8 minLeafFrac; /* Minimum leaf payload as % of total page size */ u8 pageSizeFixed; /* True if the page size can no longer be changed */ u16 pageSize; /* Total number of bytes on a page */ + u16 psAligned; /* pageSize rounded up to a multiple of 8 */ u16 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 */ int minLeaf; /* Minimum local payload in a LEAFDATA table */ @@ -531,11 +537,11 @@ u8 *used; used = sqliteMallocRaw( pPage->pBt->pageSize ); if( used==0 ) return; usableSize = pPage->pBt->usableSize; - assert( pPage->aData==&((unsigned char*)pPage)[-pPage->pBt->pageSize] ); + assert( pPage->aData==&((unsigned char*)pPage)[-pPage->pBt->psAligned] ); hdr = pPage->hdrOffset; assert( hdr==(pPage->pgno==1 ? 100 : 0) ); assert( pPage->pgno==sqlite3pager_pagenumber(pPage->aData) ); c = pPage->aData[hdr]; if( pPage->isInit ){ @@ -835,11 +841,11 @@ pBt = pPage->pBt; assert( pBt!=0 ); assert( pParent==0 || pParent->pBt==pBt ); assert( pPage->pgno==sqlite3pager_pagenumber(pPage->aData) ); - assert( pPage->aData == &((unsigned char*)pPage)[-pBt->pageSize] ); + assert( pPage->aData == &((unsigned char*)pPage)[-pBt->psAligned] ); if( pPage->pParent!=pParent && (pPage->pParent!=0 || pPage->isInit) ){ /* The parent page should never change unless the file is corrupt */ return SQLITE_CORRUPT; /* bkpt-CORRUPT */ } if( pPage->isInit ) return SQLITE_OK; @@ -908,11 +914,11 @@ Btree *pBt = pPage->pBt; int hdr = pPage->hdrOffset; int first; assert( sqlite3pager_pagenumber(data)==pPage->pgno ); - assert( &data[pBt->pageSize] == (unsigned char*)pPage ); + assert( &data[pBt->psAligned] == (unsigned char*)pPage ); assert( sqlite3pager_iswriteable(data) ); memset(&data[hdr], 0, pBt->usableSize - hdr); data[hdr] = flags; first = hdr + 8 + 4*((flags&PTF_LEAF)==0); memset(&data[hdr+1], 0, 4); @@ -937,11 +943,11 @@ int rc; unsigned char *aData; MemPage *pPage; rc = sqlite3pager_get(pBt->pPager, pgno, (void**)&aData); if( rc ) return rc; - pPage = (MemPage*)&aData[pBt->pageSize]; + pPage = (MemPage*)&aData[pBt->psAligned]; pPage->aData = aData; pPage->pBt = pBt; pPage->pgno = pgno; pPage->hdrOffset = pPage->pgno==1 ? 100 : 0; *ppPage = pPage; @@ -976,11 +982,11 @@ */ static void releasePage(MemPage *pPage){ if( pPage ){ assert( pPage->aData ); assert( pPage->pBt ); - assert( &pPage->aData[pPage->pBt->pageSize]==(unsigned char*)pPage ); + assert( &pPage->aData[pPage->pBt->psAligned]==(unsigned char*)pPage ); sqlite3pager_unref(pPage->aData); } } /* @@ -987,11 +993,11 @@ ** This routine is called when the reference count for a page ** reaches zero. We need to unref the pParent pointer when that ** happens. */ static void pageDestructor(void *pData, int pageSize){ - MemPage *pPage = (MemPage*)&((char*)pData)[pageSize]; + MemPage *pPage = (MemPage*)&((char*)pData)[FORCE_ALIGNMENT(pageSize)]; if( pPage->pParent ){ MemPage *pParent = pPage->pParent; pPage->pParent = 0; releasePage(pParent); } @@ -1005,11 +1011,11 @@ ** ** This routine needs to reset the extra data section at the end of the ** page to agree with the restored data. */ static void pageReinit(void *pData, int pageSize){ - MemPage *pPage = (MemPage*)&((char*)pData)[pageSize]; + MemPage *pPage = (MemPage*)&((char*)pData)[FORCE_ALIGNMENT(pageSize)]; if( pPage->isInit ){ pPage->isInit = 0; initPage(pPage, pPage->pParent); } } @@ -1076,10 +1082,11 @@ pBt->minEmbedFrac = zDbHeader[22]; pBt->minLeafFrac = zDbHeader[23]; pBt->pageSizeFixed = 1; } pBt->usableSize = pBt->pageSize - nReserve; + pBt->psAligned = FORCE_ALIGNMENT(pBt->pageSize); sqlite3pager_set_pagesize(pBt->pPager, pBt->pageSize); *ppBtree = pBt; return SQLITE_OK; } @@ -1146,10 +1153,11 @@ if( nReserve<0 ){ nReserve = pBt->pageSize - pBt->usableSize; } if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE ){ pBt->pageSize = pageSize; + pBt->psAligned = FORCE_ALIGNMENT(pageSize); sqlite3pager_set_pagesize(pBt->pPager, pageSize); } pBt->usableSize = pBt->pageSize - nReserve; return SQLITE_OK; } @@ -1197,10 +1205,11 @@ pBt->pageSize = get2byte(&page1[16]); pBt->usableSize = pBt->pageSize - page1[20]; if( pBt->usableSize<500 ){ goto page1_init_failed; } + pBt->psAligned = FORCE_ALIGNMENT(pBt->pageSize); pBt->maxEmbedFrac = page1[21]; pBt->minEmbedFrac = page1[22]; pBt->minLeafFrac = page1[23]; } @@ -1246,11 +1255,11 @@ */ static void unlockBtreeIfUnused(Btree *pBt){ if( pBt->inTrans==TRANS_NONE && pBt->pCursor==0 && pBt->pPage1!=0 ){ if( pBt->pPage1->aData==0 ){ MemPage *pPage = pBt->pPage1; - pPage->aData = &((char*)pPage)[-pBt->pageSize]; + pPage->aData = &((char*)pPage)[-pBt->psAligned]; pPage->pBt = pBt; pPage->pgno = 1; } releasePage(pBt->pPage1); pBt->pPage1 = 0; @@ -2671,11 +2680,11 @@ if( pgno==0 ) return; assert( pBt->pPager!=0 ); aData = sqlite3pager_lookup(pBt->pPager, pgno); if( aData ){ - pThis = (MemPage*)&aData[pBt->pageSize]; + pThis = (MemPage*)&aData[pBt->psAligned]; assert( pThis->aData==aData ); if( pThis->isInit ){ if( pThis->pParent!=pNewParent ){ if( pThis->pParent ) sqlite3pager_unref(pThis->pParent->aData); pThis->pParent = pNewParent; @@ -2958,21 +2967,21 @@ */ mxCellPerPage = MX_CELL(pBt); apCell = sqliteMallocRaw( (mxCellPerPage+2)*NB*(sizeof(u8*)+sizeof(int)) + sizeof(MemPage)*NB - + pBt->pageSize*(5+NB) + + pBt->psAligned*(5+NB) ); if( apCell==0 ){ return SQLITE_NOMEM; } szCell = (int*)&apCell[(mxCellPerPage+2)*NB]; aCopy[0] = (u8*)&szCell[(mxCellPerPage+2)*NB]; for(i=1; ipageSize+sizeof(MemPage)]; + aCopy[i] = &aCopy[i-1][pBt->psAligned+sizeof(MemPage)]; } - aSpace = &aCopy[NB-1][pBt->pageSize+sizeof(MemPage)]; + aSpace = &aCopy[NB-1][pBt->psAligned+sizeof(MemPage)]; /* ** Find the cell in the parent page whose left child points back ** to pPage. The "idx" variable is the index of that cell. If pPage ** is the rightmost child of pParent then set idx to pParent->nCell @@ -3039,14 +3048,14 @@ ** The rest of this function will use data from the copies rather ** that the original pages since the original pages will be in the ** process of being overwritten. */ for(i=0; ipageSize]; - p->aData = &((u8*)p)[-pBt->pageSize]; - memcpy(p->aData, apOld[i]->aData, pBt->pageSize + sizeof(MemPage)); - p->aData = &((u8*)p)[-pBt->pageSize]; + MemPage *p = apCopy[i] = (MemPage*)&aCopy[i][pBt->psAligned]; + p->aData = &((u8*)p)[-pBt->psAligned]; + memcpy(p->aData, apOld[i]->aData, pBt->psAligned + sizeof(MemPage)); + p->aData = &((u8*)p)[-pBt->psAligned]; } /* ** Load pointers to all cells on sibling pages and the divider cells ** into the local apCell[] array. Make copies of the divider cells @@ -3086,11 +3095,11 @@ }else{ u8 *pTemp; szCell[nCell] = sz; pTemp = &aSpace[iSpace]; iSpace += sz; - assert( iSpace<=pBt->pageSize*5 ); + assert( iSpace<=pBt->psAligned*5 ); memcpy(pTemp, apDiv[i], sz); apCell[nCell] = pTemp+leafCorrection; dropCell(pParent, nxDiv, sz); szCell[nCell] -= leafCorrection; assert( get4byte(pTemp)==pgnoOld[i] ); @@ -3270,17 +3279,17 @@ j--; parseCellPtr(pNew, apCell[j], &info); pCell = &aSpace[iSpace]; fillInCell(pParent, pCell, 0, info.nKey, 0, 0, &sz); iSpace += sz; - assert( iSpace<=pBt->pageSize*5 ); + assert( iSpace<=pBt->psAligned*5 ); pTemp = 0; }else{ pCell -= 4; pTemp = &aSpace[iSpace]; iSpace += sz; - assert( iSpace<=pBt->pageSize*5 ); + assert( iSpace<=pBt->psAligned*5 ); } insertCell(pParent, nxDiv, pCell, sz, pTemp); put4byte(findOverflowCell(pParent,nxDiv), pNew->pgno); j++; nxDiv++; Index: src/pager.c ================================================================== --- src/pager.c +++ src/pager.c @@ -16,11 +16,11 @@ ** is separate from the database file. The pager also implements file ** locking to prevent two processes from writing the same database ** file simultaneously, or one process from reading the database while ** another is writing. ** -** @(#) $Id: pager.c,v 1.167 2004/10/05 02:41:43 drh Exp $ +** @(#) $Id: pager.c,v 1.168 2004/10/22 16:22:59 drh Exp $ */ #include "sqliteInt.h" #include "os.h" #include "pager.h" #include @@ -108,10 +108,16 @@ */ #ifndef SQLITE_BUSY_RESERVED_LOCK # define SQLITE_BUSY_RESERVED_LOCK 0 #endif +/* +** This macro rounds values up so that if the value is an address it +** is guaranteed to be an address that is aligned to an 8-byte boundary. +*/ +#define FORCE_ALIGNMENT(X) (((X)+7)&~7) + /* ** Each in-memory image of a page begins with the following header. ** This header is only visible to this pager module. The client ** code that calls pager sees only the data that follows the header. ** @@ -141,11 +147,11 @@ u8 dirty; /* TRUE if we need to write back changes */ u8 needSync; /* Sync journal before writing this page */ u8 alwaysRollback; /* Disable dont_rollback() for this page */ short int nRef; /* Number of users of this page */ PgHdr *pDirty; /* Dirty pages sorted by PgHdr.pgno */ - /* pPager->pageSize bytes of page data follow this header */ + /* pPager->psAligned bytes of page data follow this header */ /* Pager.nExtra bytes of local data follow the page data */ }; /* ** For an in-memory only database, some extra information is recorded about @@ -177,13 +183,13 @@ ** Convert a pointer to a PgHdr into a pointer to its data ** and back again. */ #define PGHDR_TO_DATA(P) ((void*)(&(P)[1])) #define DATA_TO_PGHDR(D) (&((PgHdr*)(D))[-1]) -#define PGHDR_TO_EXTRA(G,P) ((void*)&((char*)(&(G)[1]))[(P)->pageSize]) +#define PGHDR_TO_EXTRA(G,P) ((void*)&((char*)(&(G)[1]))[(P)->psAligned]) #define PGHDR_TO_HIST(P,PGR) \ - ((PgHistory*)&((char*)(&(P)[1]))[(PGR)->pageSize+(PGR)->nExtra]) + ((PgHistory*)&((char*)(&(P)[1]))[(PGR)->psAligned+(PGR)->nExtra]) /* ** How big to make the hash table used for locating in-memory pages ** by page number. */ @@ -212,10 +218,11 @@ int stmtNRec; /* Number of records in stmt subjournal */ int nExtra; /* Add this many bytes to each in-memory page */ void (*xDestructor)(void*,int); /* Call this routine when freeing pages */ void (*xReiniter)(void*,int); /* Call this routine when reloading pages */ int pageSize; /* Number of bytes in a page */ + int psAligned; /* pageSize rounded up to a multiple of 8 */ 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 */ @@ -1501,10 +1508,11 @@ pPager->stmtOpen = 0; pPager->stmtInUse = 0; pPager->nRef = 0; pPager->dbSize = memDb-1; pPager->pageSize = SQLITE_DEFAULT_PAGE_SIZE; + pPager->psAligned = FORCE_ALIGNMENT(pPager->pageSize); pPager->stmtSize = 0; pPager->stmtJSize = 0; pPager->nPage = 0; pPager->mxPage = 100; pPager->state = PAGER_UNLOCK; @@ -1516,11 +1524,11 @@ pPager->noSync = pPager->tempFile || !useJournal; pPager->fullSync = (pPager->noSync?0:1); pPager->pFirst = 0; pPager->pFirstSynced = 0; pPager->pLast = 0; - pPager->nExtra = nExtra; + pPager->nExtra = FORCE_ALIGNMENT(nExtra); pPager->sectorSize = PAGER_SECTOR_SIZE; pPager->pBusyHandler = 0; memset(pPager->aHash, 0, sizeof(pPager->aHash)); *ppPager = pPager; return SQLITE_OK; @@ -1562,10 +1570,11 @@ ** The page size must only be changed when the cache is empty. */ void sqlite3pager_set_pagesize(Pager *pPager, int pageSize){ assert( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE ); pPager->pageSize = pageSize; + pPager->psAligned = FORCE_ALIGNMENT(pageSize); } /* ** Read the first N bytes from the beginning of the file into memory ** that pDest points to. No error checking is done. @@ -2131,11 +2140,11 @@ /* The requested page is not in the page cache. */ int h; pPager->nMiss++; if( pPager->nPagemxPage || pPager->pFirst==0 || pPager->memDb ){ /* Create a new page */ - pPg = sqliteMallocRaw( sizeof(*pPg) + pPager->pageSize + pPg = sqliteMallocRaw( sizeof(*pPg) + pPager->psAligned + sizeof(u32) + pPager->nExtra + pPager->memDb*sizeof(PgHistory) ); if( pPg==0 ){ if( !pPager->memDb ){ pager_unwritelock(pPager); Index: test/pagesize.test ================================================================== --- test/pagesize.test +++ test/pagesize.test @@ -9,11 +9,11 @@ # #*********************************************************************** # This file implements regression tests for SQLite library. # This file implements tests for the page_size PRAGMA. # -# $Id: pagesize.test,v 1.3 2004/09/05 00:33:44 drh Exp $ +# $Id: pagesize.test,v 1.4 2004/10/22 16:22:59 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -59,11 +59,11 @@ } } 8192 -foreach PGSZ {512 2000 2048 3000 4096} { +foreach PGSZ {512 515 516 751 2000 2001 2002 2003 2004 2048 3000 4096} { do_test pagesize-2.$PGSZ.1 { db close file delete -force test.db sqlite3 db test.db execsql "PRAGMA page_size=$PGSZ"