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.422 2007/09/03 22:00:39 drh Exp $ +** $Id: btree.c,v 1.423 2007/09/06 22:19:15 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** See the header comment on "btreeInt.h" for additional information. ** Including a description of file format and an overview of operation. */ @@ -900,11 +900,12 @@ pBt = pPage->pBt; assert( pBt!=0 ); assert( pParent==0 || pParent->pBt==pBt ); assert( sqlite3_mutex_held(pBt->mutex) ); assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) ); - assert( pPage->aData == &((unsigned char*)pPage)[-pBt->pageSize] ); + assert( pPage == sqlite3PagerGetExtra(pPage->pDbPage) ); + assert( pPage->aData == sqlite3PagerGetData(pPage->pDbPage) ); if( pPage->pParent!=pParent && (pPage->pParent!=0 || pPage->isInit) ){ /* The parent page should never change unless the file is corrupt */ return SQLITE_CORRUPT_BKPT; } if( pPage->isInit ) return SQLITE_OK; @@ -967,11 +968,12 @@ BtShared *pBt = pPage->pBt; int hdr = pPage->hdrOffset; int first; assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno ); - assert( &data[pBt->pageSize] == (unsigned char*)pPage ); + assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); + assert( sqlite3PagerGetData(pPage->pDbPage) == data ); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( sqlite3_mutex_held(pBt->mutex) ); memset(&data[hdr], 0, pBt->usableSize - hdr); data[hdr] = flags; first = hdr + 8 + 4*((flags&PTF_LEAF)==0); @@ -1051,11 +1053,12 @@ */ static void releasePage(MemPage *pPage){ if( pPage ){ assert( pPage->aData ); assert( pPage->pBt ); - assert( &pPage->aData[pPage->pBt->pageSize]==(unsigned char*)pPage ); + assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); + assert( sqlite3PagerGetData(pPage->pDbPage)==pPage->aData ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); sqlite3PagerUnref(pPage->pDbPage); } } @@ -1729,11 +1732,11 @@ assert( sqlite3_mutex_held(pBt->mutex) ); if( pBt->inTransaction==TRANS_NONE && pBt->pCursor==0 && pBt->pPage1!=0 ){ if( sqlite3PagerRefcount(pBt->pPager)>=1 ){ if( pBt->pPage1->aData==0 ){ MemPage *pPage = pBt->pPage1; - pPage->aData = &((u8*)pPage)[-pBt->pageSize]; + pPage->aData = sqlite3PagerGetData(pPage->pDbPage); pPage->pBt = pBt; pPage->pgno = 1; } releasePage(pBt->pPage1); } @@ -4346,11 +4349,11 @@ assert( pBt->pPager!=0 ); pDbPage = sqlite3PagerLookup(pBt->pPager, pgno); if( pDbPage ){ pThis = (MemPage *)sqlite3PagerGetExtra(pDbPage); if( pThis->isInit ){ - assert( pThis->aData==(sqlite3PagerGetData(pDbPage)) ); + assert( pThis->aData==sqlite3PagerGetData(pDbPage) ); if( pThis->pParent!=pNewParent ){ if( pThis->pParent ) sqlite3PagerUnref(pThis->pParent->pDbPage); pThis->pParent = pNewParent; sqlite3PagerRef(pNewParent->pDbPage); } @@ -4897,16 +4900,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)); - /* The memcpy() above changes the value of p->aData so we have to - ** set it again. */ - p->aData = &((u8*)p)[-pBt->pageSize]; + MemPage *p = apCopy[i] = (MemPage*)aCopy[i]; + memcpy(p, apOld[i], sizeof(MemPage)); + p->aData = (void*)&p[1]; + memcpy(p->aData, apOld[i]->aData, pBt->pageSize); } /* ** Load pointers to all cells on sibling pages and the divider cells ** into the local apCell[] array. Make copies of the divider cells 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.385 2007/09/03 15:19:35 drh Exp $ +** @(#) $Id: pager.c,v 1.386 2007/09/06 22:19:15 drh Exp $ */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" #include #include @@ -269,16 +269,15 @@ short int nRef; /* Number of users of this page */ PgHdr *pDirty, *pPrevDirty; /* Dirty pages */ #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT PagerLruLink gfree; /* Global list of nRef==0 pages */ #endif - u32 notUsed; /* Buffer space */ #ifdef SQLITE_CHECK_PAGES u32 pageHash; #endif - /* pPager->pageSize bytes of page data follow this header */ - /* Pager.nExtra bytes of local data follow the page data */ + void *pData; /* Page data */ + /* Pager.nExtra bytes of local data appended to this header */ }; /* ** For an in-memory only database, some extra information is recorded about ** each page so that changes can be rolled back. (Journal files are not @@ -311,15 +310,14 @@ /* ** 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_DATA(P) ((P)->pData) +#define PGHDR_TO_EXTRA(G,P) ((void*)&((G)[1])) #define PGHDR_TO_HIST(P,PGR) \ - ((PgHistory*)&((char*)(&(P)[1]))[(PGR)->pageSize+(PGR)->nExtra]) + ((PgHistory*)&((char*)(&(P)[1]))[(PGR)->nExtra]) /* ** A open page cache is an instance of the following structure. ** ** Pager.errCode may be set to SQLITE_IOERR, SQLITE_CORRUPT, or @@ -1243,10 +1241,11 @@ for(pPg=pPager->pAll; pPg; pPg=pNext){ IOTRACE(("PGFREE %p %d\n", pPager, pPg->pgno)); PAGER_INCR(sqlite3_pager_pgfree_count); pNext = pPg->pNextAll; lruListRemove(pPg); + sqlite3_free(pPg->pData); sqlite3_free(pPg); } assert(pPager->lru.pFirst==0); assert(pPager->lru.pFirstSynced==0); assert(pPager->lru.pLast==0); @@ -2493,10 +2492,11 @@ *ppPg = pPg->pNextAll; IOTRACE(("PGFREE %p %d\n", pPager, pPg->pgno)); PAGER_INCR(sqlite3_pager_pgfree_count); unlinkPage(pPg); makeClean(pPg); + sqlite3_free(pPg->pData); sqlite3_free(pPg); pPager->nPage--; } } } @@ -3156,10 +3156,11 @@ + sizeof(u32) + pPager->nExtra + MEMDB*sizeof(PgHistory) ); IOTRACE(("PGFREE %p %d *\n", pPager, pPg->pgno)); PAGER_INCR(sqlite3_pager_pgfree_count); + sqlite3_free(pPg->pData); sqlite3_free(pPg); pPager->nPage--; }else{ /* An error occured whilst writing to the database file or ** journal in pager_recycle(). The error is not returned to the @@ -3392,10 +3393,11 @@ ** allowed. */ static int pagerAllocatePage(Pager *pPager, PgHdr **ppPg){ int rc = SQLITE_OK; PgHdr *pPg; + void *pData; /* Create a new PgHdr if any of the four conditions defined ** above are met: */ if( pPager->nPagemxPage || pPager->lru.pFirst==0 @@ -3409,22 +3411,29 @@ rc = SQLITE_NOMEM; goto pager_allocate_out; } } pagerLeave(pPager); - pPg = sqlite3_malloc( sizeof(*pPg) + pPager->pageSize - + sizeof(u32) + pPager->nExtra + pPg = sqlite3_malloc( sizeof(*pPg) + sizeof(u32) + pPager->nExtra + MEMDB*sizeof(PgHistory) ); + if( pPg ){ + pData = sqlite3_malloc( pPager->pageSize ); + if( pData==0 ){ + sqlite3_free(pPg); + pPg = 0; + } + } pagerEnter(pPager); if( pPg==0 ){ rc = SQLITE_NOMEM; goto pager_allocate_out; } memset(pPg, 0, sizeof(*pPg)); if( MEMDB ){ memset(PGHDR_TO_HIST(pPg, pPager), 0, sizeof(PgHistory)); } + pPg->pData = pData; pPg->pPager = pPager; pPg->pNextAll = pPager->pAll; pPager->pAll = pPg; pPager->nPage++; }else{ @@ -3985,43 +3994,43 @@ ** EXCLUSIVE lock on the main database file. Write the current page to ** the transaction journal if it is not there already. */ if( !pPg->inJournal && (pPager->useJournal || MEMDB) ){ if( (int)pPg->pgno <= pPager->origDbSize ){ - int szPg; if( MEMDB ){ PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager); PAGERTRACE3("JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno); assert( pHist->pOrig==0 ); pHist->pOrig = sqlite3_malloc( pPager->pageSize ); if( pHist->pOrig ){ memcpy(pHist->pOrig, PGHDR_TO_DATA(pPg), pPager->pageSize); } }else{ - u32 cksum, saved; - char *pData2, *pEnd; + u32 cksum; + char *pData2; /* We should never write to the journal file the page that ** contains the database locks. The following assert verifies ** that we do not. */ assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) ); pData2 = CODEC2(pPager, pData, pPg->pgno, 7); cksum = pager_cksum(pPager, (u8*)pData2); - pEnd = pData2 + pPager->pageSize; - pData2 -= 4; - saved = *(u32*)pEnd; - put32bits(pEnd, cksum); - szPg = pPager->pageSize+8; - put32bits(pData2, pPg->pgno); - rc = sqlite3OsWrite(pPager->jfd, pData2, szPg, pPager->journalOff); - IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno, + rc = write32bits(pPager->jfd, pPager->journalOff, pPg->pgno); + if( rc==SQLITE_OK ){ + rc = sqlite3OsWrite(pPager->jfd, pData2, pPager->pageSize, + pPager->journalOff + 4); + pPager->journalOff += pPager->pageSize+4; + } + if( rc==SQLITE_OK ){ + rc = write32bits(pPager->jfd, pPager->journalOff, cksum); + pPager->journalOff += 4; + } + IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno, pPager->journalOff, szPg)); PAGER_INCR(sqlite3_pager_writej_count); - pPager->journalOff += szPg; PAGERTRACE5("JOURNAL %d page %d needSync=%d hash(%08x)\n", PAGERID(pPager), pPg->pgno, pPg->needSync, pager_pagehash(pPg)); - *(u32*)pEnd = saved; /* An error has occured writing to the journal file. The ** transaction will be rolled back by the layer above. */ if( rc!=SQLITE_OK ){ @@ -4066,13 +4075,15 @@ } PAGERTRACE3("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno); page_add_to_stmt_list(pPg); }else{ i64 offset = pPager->stmtNRec*(4+pPager->pageSize); - char *pData2 = CODEC2(pPager, pData, pPg->pgno, 7)-4; - put32bits(pData2, pPg->pgno); - rc = sqlite3OsWrite(pPager->stfd, pData2, pPager->pageSize+4, offset); + char *pData2 = CODEC2(pPager, pData, pPg->pgno, 7); + rc = write32bits(pPager->stfd, offset, pPg->pgno); + if( rc==SQLITE_OK ){ + rc = sqlite3OsWrite(pPager->stfd, pData2, pPager->pageSize, offset+4); + } PAGERTRACE3("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno); if( rc!=SQLITE_OK ){ return rc; } pPager->stmtNRec++; Index: test/btree.test ================================================================== --- test/btree.test +++ test/btree.test @@ -9,11 +9,11 @@ # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this script is btree database backend # -# $Id: btree.test,v 1.40 2007/06/25 08:16:58 danielk1977 Exp $ +# $Id: btree.test,v 1.41 2007/09/06 22:19:15 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -780,10 +780,11 @@ do_test btree-10.2 { set ::c1 [btree_cursor $::b1 2 1] lindex [btree_pager_stats $::b1] 1 } {2} do_test btree-10.3 { +btree_breakpoint for {set i 1} {$i<=30} {incr i} { set key [format %03d $i] set data "*** $key *** $key *** $key *** $key ***" btree_insert $::c1 $key $data }