diff options
author | danielk1977 <danielk1977@noemail.net> | 2009-01-16 16:23:38 +0000 |
---|---|---|
committer | danielk1977 <danielk1977@noemail.net> | 2009-01-16 16:23:38 +0000 |
commit | 45d6882fd9e6b964362bcf815c1e39652e8279e1 (patch) | |
tree | c06e2b230edfd06291622b138ce64abbd1761edd /src | |
parent | 443c0597fe11768a01aacbe99d0acd0255a0d3f8 (diff) | |
download | sqlite-45d6882fd9e6b964362bcf815c1e39652e8279e1.tar.gz sqlite-45d6882fd9e6b964362bcf815c1e39652e8279e1.zip |
Revert (6187). (CVS 6188)
FossilOrigin-Name: a353c1ab376b159c4d12532412365318cdbdcc60
Diffstat (limited to 'src')
-rw-r--r-- | src/bitvec.c | 10 | ||||
-rw-r--r-- | src/btree.c | 344 | ||||
-rw-r--r-- | src/btreeInt.h | 21 | ||||
-rw-r--r-- | src/pager.c | 2279 | ||||
-rw-r--r-- | src/pager.h | 80 | ||||
-rw-r--r-- | src/pcache.c | 5 | ||||
-rw-r--r-- | src/pcache.h | 4 | ||||
-rw-r--r-- | src/sqliteInt.h | 3 | ||||
-rw-r--r-- | src/test2.c | 9 | ||||
-rw-r--r-- | src/vdbeaux.c | 10 |
10 files changed, 1115 insertions, 1650 deletions
diff --git a/src/bitvec.c b/src/bitvec.c index 0297d286b..f631359cd 100644 --- a/src/bitvec.c +++ b/src/bitvec.c @@ -34,7 +34,7 @@ ** start of a transaction, and is thus usually less than a few thousand, ** but can be as large as 2 billion for a really big database. ** -** @(#) $Id: bitvec.c,v 1.11 2009/01/16 15:21:05 danielk1977 Exp $ +** @(#) $Id: bitvec.c,v 1.12 2009/01/16 16:23:38 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -275,14 +275,6 @@ void sqlite3BitvecDestroy(Bitvec *p){ sqlite3_free(p); } -/* -** Return the value of the iSize parameter specified when Bitvec *p -** was created. -*/ -u32 sqlite3BitvecSize(Bitvec *p){ - return p->iSize; -} - #ifndef SQLITE_OMIT_BUILTIN_TEST /* ** Let V[] be an array of unsigned characters sufficient to hold diff --git a/src/btree.c b/src/btree.c index 560578221..3732dc6b6 100644 --- a/src/btree.c +++ b/src/btree.c @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btree.c,v 1.559 2009/01/16 15:21:06 danielk1977 Exp $ +** $Id: btree.c,v 1.560 2009/01/16 16:23:38 danielk1977 Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** See the header comment on "btreeInt.h" for additional information. @@ -283,80 +283,6 @@ static void invalidateAllOverflowCache(BtShared *pBt){ #endif /* -** Set bit pgno of the BtShared.pHasContent bitvec. This is called -** when a page that previously contained data becomes a free-list leaf -** page. -** -** The BtShared.pHasContent bitvec exists to work around an obscure -** bug caused by the interaction of two useful IO optimizations surrounding -** free-list leaf pages: -** -** 1) When all data is deleted from a page and the page becomes -** a free-list leaf page, the page is not written to the database -** (as free-list leaf pages contain no meaningful data). Sometimes -** such a page is not even journalled (as it will not be modified, -** why bother journalling it?). -** -** 2) When a free-list leaf page is reused, its content is not read -** from the database or written to the journal file (why should it -** be, if it is not at all meaningful?). -** -** By themselves, these optimizations work fine and provide a handy -** performance boost to bulk delete or insert operations. However, if -** a page is moved to the free-list and then reused within the same -** transaction, a problem comes up. If the page is not journalled when -** it is moved to the free-list and it is also not journalled when it -** is extracted from the free-list and reused, then the original data -** may be lost. In the event of a rollback, it may not be possible -** to restore the database to its original configuration. -** -** The solution is the BtShared.pHasContent bitvec. Whenever a page is -** moved to become a free-list leaf page, the corresponding bit is -** set in the bitvec. Whenever a leaf page is extracted from the free-list, -** optimization 2 above is ommitted if the corresponding bit is already -** set in BtShared.pHasContent. The contents of the bitvec are cleared -** at the end of every transaction. -*/ -static int btreeSetHasContent(BtShared *pBt, Pgno pgno){ - int rc = SQLITE_OK; - if( !pBt->pHasContent ){ - int nPage; - rc = sqlite3PagerPagecount(pBt->pPager, &nPage); - if( rc==SQLITE_OK ){ - pBt->pHasContent = sqlite3BitvecCreate((u32)nPage); - if( !pBt->pHasContent ){ - rc = SQLITE_NOMEM; - } - } - } - if( rc==SQLITE_OK && pgno<=sqlite3BitvecSize(pBt->pHasContent) ){ - rc = sqlite3BitvecSet(pBt->pHasContent, pgno); - } - return rc; -} - -/* -** Query the BtShared.pHasContent vector. -** -** This function is called when a free-list leaf page is removed from the -** free-list for reuse. It returns false if it is safe to retrieve the -** page from the pager layer with the 'no-content' flag set. True otherwise. -*/ -static int btreeGetHasContent(BtShared *pBt, Pgno pgno){ - Bitvec *p = pBt->pHasContent; - return (p && (pgno>sqlite3BitvecSize(p) || sqlite3BitvecTest(p, pgno))); -} - -/* -** Clear (destroy) the BtShared.pHasContent bitvec. This should be -** invoked at the conclusion of each write-transaction. -*/ -static void btreeClearHasContent(BtShared *pBt){ - sqlite3BitvecDestroy(pBt->pHasContent); - pBt->pHasContent = 0; -} - -/* ** Save the current cursor position in the variables BtCursor.nKey ** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK. */ @@ -1175,21 +1101,6 @@ int sqlite3BtreeGetPage( } /* -** Retrieve a page from the pager cache. If the requested page is not -** already in the pager cache return NULL. Initialize the MemPage.pBt and -** MemPage.aData elements if needed. -*/ -static MemPage *btreePageLookup(BtShared *pBt, Pgno pgno){ - DbPage *pDbPage; - assert( sqlite3_mutex_held(pBt->mutex) ); - pDbPage = sqlite3PagerLookup(pBt->pPager, pgno); - if( pDbPage ){ - return btreePageFromDbPage(pDbPage, pgno, pBt); - } - return 0; -} - -/* ** Return the size of the database file in pages. If there is any kind of ** error, return ((unsigned int)-1). */ @@ -1213,6 +1124,7 @@ static int getAndInitPage( MemPage **ppPage /* Write the page pointer here */ ){ int rc; + DbPage *pDbPage; MemPage *pPage; assert( sqlite3_mutex_held(pBt->mutex) ); @@ -1225,9 +1137,10 @@ static int getAndInitPage( ** pagerPagecount() to make sure pgno is within limits, which results ** in a measureable performance improvements. */ - *ppPage = pPage = btreePageLookup(pBt, pgno); - if( pPage ){ + pDbPage = sqlite3PagerLookup(pBt->pPager, pgno); + if( pDbPage ){ /* Page is already in cache */ + *ppPage = pPage = btreePageFromDbPage(pDbPage, pgno, pBt); rc = SQLITE_OK; }else{ /* Page not in cache. Acquire it. */ @@ -2459,7 +2372,7 @@ int sqlite3BtreeIncrVacuum(Btree *p){ rc = SQLITE_DONE; }else{ invalidateAllOverflowCache(pBt); - rc = incrVacuumStep(pBt, 0, pagerPagecount(pBt)); + rc = incrVacuumStep(pBt, 0, sqlite3PagerImageSize(pBt->pPager)); } sqlite3BtreeLeave(p); return rc; @@ -2627,7 +2540,6 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){ /* Set the handles current transaction state to TRANS_NONE and unlock ** the pager if this call closed the only read or write transaction. */ - btreeClearHasContent(pBt); p->inTrans = TRANS_NONE; unlockBtreeIfUnused(pBt); @@ -2763,7 +2675,6 @@ int sqlite3BtreeRollback(Btree *p){ } } - btreeClearHasContent(pBt); p->inTrans = TRANS_NONE; pBt->inStmt = 0; unlockBtreeIfUnused(pBt); @@ -3171,29 +3082,34 @@ int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){ ** ** If an error occurs an SQLite error code is returned. Otherwise: ** -** The page number of the next overflow page in the linked list is -** written to *pPgnoNext. If page ovfl is the last page in its linked -** list, *pPgnoNext is set to zero. +** Unless pPgnoNext is NULL, the page number of the next overflow +** page in the linked list is written to *pPgnoNext. If page ovfl +** is the last page in its linked list, *pPgnoNext is set to zero. ** -** If ppPage is not NULL, and a reference to the MemPage object corresponding -** to page number pOvfl was obtained, then *ppPage is set to point to that -** reference. It is the responsibility of the caller to call releasePage() -** on *ppPage to free the reference. In no reference was obtained (because -** the pointer-map was used to obtain the value for *pPgnoNext), then -** *ppPage is set to zero. +** If ppPage is not NULL, *ppPage is set to the MemPage* handle +** for page ovfl. The underlying pager page may have been requested +** with the noContent flag set, so the page data accessable via +** this handle may not be trusted. */ static int getOverflowPage( BtShared *pBt, Pgno ovfl, /* Overflow page */ - MemPage **ppPage, /* OUT: MemPage handle (may be NULL) */ + MemPage **ppPage, /* OUT: MemPage handle */ Pgno *pPgnoNext /* OUT: Next overflow page number */ ){ Pgno next = 0; - MemPage *pPage = 0; int rc = SQLITE_OK; assert( sqlite3_mutex_held(pBt->mutex) ); - assert(pPgnoNext); + /* One of these must not be NULL. Otherwise, why call this function? */ + assert(ppPage || pPgnoNext); + + /* If pPgnoNext is NULL, then this function is being called to obtain + ** a MemPage* reference only. No page-data is required in this case. + */ + if( !pPgnoNext ){ + return sqlite3BtreeGetPage(pBt, ovfl, ppPage, 1); + } #ifndef SQLITE_OMIT_AUTOVACUUM /* Try to find the next page in the overflow list using the @@ -3213,29 +3129,34 @@ static int getOverflowPage( if( iGuess<=pagerPagecount(pBt) ){ rc = ptrmapGet(pBt, iGuess, &eType, &pgno); - if( rc==SQLITE_OK && eType==PTRMAP_OVERFLOW2 && pgno==ovfl ){ + if( rc!=SQLITE_OK ){ + return rc; + } + if( eType==PTRMAP_OVERFLOW2 && pgno==ovfl ){ next = iGuess; - rc = SQLITE_DONE; } } } #endif - if( rc==SQLITE_OK ){ - rc = sqlite3BtreeGetPage(pBt, ovfl, &pPage, 0); + if( next==0 || ppPage ){ + MemPage *pPage = 0; + + rc = sqlite3BtreeGetPage(pBt, ovfl, &pPage, next!=0); assert(rc==SQLITE_OK || pPage==0); if( next==0 && rc==SQLITE_OK ){ next = get4byte(pPage->aData); } - } - *pPgnoNext = next; - if( ppPage ){ - *ppPage = pPage; - }else{ - releasePage(pPage); + if( ppPage ){ + *ppPage = pPage; + }else{ + releasePage(pPage); + } } - return (rc==SQLITE_DONE ? SQLITE_OK : rc); + *pPgnoNext = next; + + return rc; } /* @@ -4344,7 +4265,6 @@ static int allocateBtreePage( iPage = get4byte(&aData[8+closest*4]); if( !searchList || iPage==nearby ){ - int noContent; Pgno nPage; *pPgno = iPage; nPage = pagerPagecount(pBt); @@ -4361,9 +4281,9 @@ static int allocateBtreePage( } put4byte(&aData[4], k-1); assert( sqlite3PagerIswriteable(pTrunk->pDbPage) ); - noContent = !btreeGetHasContent(pBt, *pPgno); - rc = sqlite3BtreeGetPage(pBt, *pPgno, ppPage, noContent); + rc = sqlite3BtreeGetPage(pBt, *pPgno, ppPage, 1); if( rc==SQLITE_OK ){ + sqlite3PagerDontRollback((*ppPage)->pDbPage); rc = sqlite3PagerWrite((*ppPage)->pDbPage); if( rc!=SQLITE_OK ){ releasePage(*ppPage); @@ -4381,10 +4301,6 @@ static int allocateBtreePage( int nPage = pagerPagecount(pBt); *pPgno = nPage + 1; - if( *pPgno==PENDING_BYTE_PAGE(pBt) ){ - (*pPgno)++; - } - #ifndef SQLITE_OMIT_AUTOVACUUM if( pBt->autoVacuum && PTRMAP_ISPAGE(pBt, *pPgno) ){ /* If *pPgno refers to a pointer-map page, allocate two new pages @@ -4424,51 +4340,32 @@ end_allocate_page: } /* -** This function is used to add page iPage to the database file free-list. -** It is assumed that the page is not already a part of the free-list. -** -** The value passed as the second argument to this function is optional. -** If the caller happens to have a pointer to the MemPage object -** corresponding to page iPage handy, it may pass it as the second value. -** Otherwise, it may pass NULL. +** Add a page of the database file to the freelist. ** -** If a pointer to a MemPage object is passed as the second argument, -** its reference count is not altered by this function. +** sqlite3PagerUnref() is NOT called for pPage. */ -static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ - MemPage *pTrunk = 0; /* Free-list trunk page */ - Pgno iTrunk = 0; /* Page number of free-list trunk page */ - MemPage *pPage1 = pBt->pPage1; /* Local reference to page 1 */ - MemPage *pPage; /* Page being freed. May be NULL. */ - int rc; /* Return Code */ - int nFree; /* Initial number of pages on free-list */ - - assert( sqlite3_mutex_held(pBt->mutex) ); - assert( iPage>1 ); - assert( !pMemPage || pMemPage->pgno==iPage ); +static int freePage(MemPage *pPage){ + BtShared *pBt = pPage->pBt; + MemPage *pPage1 = pBt->pPage1; + int rc, n, k; - if( pMemPage ){ - pPage = pMemPage; - sqlite3PagerRef(pPage->pDbPage); - }else{ - pPage = btreePageLookup(pBt, iPage); - } + /* Prepare the page for freeing */ + assert( sqlite3_mutex_held(pPage->pBt->mutex) ); + assert( pPage->pgno>1 ); + pPage->isInit = 0; /* Increment the free page count on pPage1 */ rc = sqlite3PagerWrite(pPage1->pDbPage); - if( rc ) goto freepage_out; - nFree = get4byte(&pPage1->aData[36]); - put4byte(&pPage1->aData[36], nFree+1); + if( rc ) return rc; + n = get4byte(&pPage1->aData[36]); + put4byte(&pPage1->aData[36], n+1); #ifdef SQLITE_SECURE_DELETE /* If the SQLITE_SECURE_DELETE compile-time option is enabled, then ** always fully overwrite deleted information with zeros. */ - if( (!pPage && (rc = sqlite3BtreeGetPage(pBt, iPage, &pPage, 0))) - || (rc = sqlite3PagerWrite(pPage->pDbPage)) - ){ - goto freepage_out; - } + rc = sqlite3PagerWrite(pPage->pDbPage); + if( rc ) return rc; memset(pPage->aData, 0, pPage->pBt->pageSize); #endif @@ -4476,34 +4373,27 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ ** to indicate that the page is free. */ if( ISAUTOVACUUM ){ - rc = ptrmapPut(pBt, iPage, PTRMAP_FREEPAGE, 0); - if( rc ) goto freepage_out; + rc = ptrmapPut(pBt, pPage->pgno, PTRMAP_FREEPAGE, 0); + if( rc ) return rc; } - /* Now manipulate the actual database free-list structure. There are two - ** possibilities. If the free-list is currently empty, or if the first - ** trunk page in the free-list is full, then this page will become a - ** new free-list trunk page. Otherwise, it will become a leaf of the - ** first trunk page in the current free-list. This block tests if it - ** is possible to add the page as a new free-list leaf. - */ - if( nFree!=0 ){ - int nLeaf; /* Initial number of leaf cells on trunk page */ - - iTrunk = get4byte(&pPage1->aData[32]); - rc = sqlite3BtreeGetPage(pBt, iTrunk, &pTrunk, 0); - if( rc!=SQLITE_OK ){ - goto freepage_out; - } - - nLeaf = get4byte(&pTrunk->aData[4]); - if( nLeaf<0 ){ - rc = SQLITE_CORRUPT_BKPT; - goto freepage_out; - } - if( nLeaf<pBt->usableSize/4 - 8 ){ - /* In this case there is room on the trunk page to insert the page - ** being freed as a new leaf. + if( n==0 ){ + /* This is the first free page */ + rc = sqlite3PagerWrite(pPage->pDbPage); + if( rc ) return rc; + memset(pPage->aData, 0, 8); + put4byte(&pPage1->aData[32], pPage->pgno); + TRACE(("FREE-PAGE: %d first\n", pPage->pgno)); + }else{ + /* Other free pages already exist. Retrive the first trunk page + ** of the freelist and find out how many leaves it has. */ + MemPage *pTrunk; + rc = sqlite3BtreeGetPage(pBt, get4byte(&pPage1->aData[32]), &pTrunk, 0); + if( rc ) return rc; + k = get4byte(&pTrunk->aData[4]); + if( k>=pBt->usableSize/4 - 8 ){ + /* The trunk is full. Turn the page being freed into a new + ** trunk page with no leaves. ** ** Note that the trunk page is not really full until it contains ** usableSize/4 - 2 entries, not usableSize/4 - 8 entries as we have @@ -4516,49 +4406,32 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ ** to 3.6.0 or later) we should consider fixing the conditional above ** to read "usableSize/4-2" instead of "usableSize/4-8". */ + rc = sqlite3PagerWrite(pPage->pDbPage); + if( rc==SQLITE_OK ){ + put4byte(pPage->aData, pTrunk->pgno); + put4byte(&pPage->aData[4], 0); + put4byte(&pPage1->aData[32], pPage->pgno); + TRACE(("FREE-PAGE: %d new trunk page replacing %d\n", + pPage->pgno, pTrunk->pgno)); + } + }else if( k<0 ){ + rc = SQLITE_CORRUPT; + }else{ + /* Add the newly freed page as a leaf on the current trunk */ rc = sqlite3PagerWrite(pTrunk->pDbPage); if( rc==SQLITE_OK ){ - put4byte(&pTrunk->aData[4], nLeaf+1); - put4byte(&pTrunk->aData[8+nLeaf*4], iPage); + put4byte(&pTrunk->aData[4], k+1); + put4byte(&pTrunk->aData[8+k*4], pPage->pgno); #ifndef SQLITE_SECURE_DELETE - if( pPage ){ - sqlite3PagerDontWrite(pPage->pDbPage); - } + rc = sqlite3PagerDontWrite(pPage->pDbPage); #endif - rc = btreeSetHasContent(pBt, iPage); } TRACE(("FREE-PAGE: %d leaf on trunk page %d\n",pPage->pgno,pTrunk->pgno)); - goto freepage_out; } + releasePage(pTrunk); } - - /* If control flows to this point, then it was not possible to add the - ** the page being freed as a leaf page of the first trunk in the free-list. - ** Possibly because the free-list is empty, or possibly because the - ** first trunk in the free-list is full. Either way, the page being freed - ** will become the new first trunk page in the free-list. - */ - if( (!pPage && (rc = sqlite3BtreeGetPage(pBt, iPage, &pPage, 0))) - || (rc = sqlite3PagerWrite(pPage->pDbPage)) - ){ - goto freepage_out; - } - put4byte(pPage->aData, iTrunk); - put4byte(&pPage->aData[4], 0); - put4byte(&pPage1->aData[32], iPage); - TRACE(("FREE-PAGE: %d new trunk page replacing %d\n", pPage->pgno, iTrunk)); - -freepage_out: - if( pPage ){ - pPage->isInit = 0; - } - releasePage(pPage); - releasePage(pTrunk); return rc; } -static int freePage(MemPage *pPage){ - return freePage2(pPage->pBt, pPage, pPage->pgno); -} /* ** Free any overflow pages associated with the given Cell. @@ -4581,21 +4454,16 @@ static int clearCell(MemPage *pPage, unsigned char *pCell){ nOvfl = (info.nPayload - info.nLocal + ovflPageSize - 1)/ovflPageSize; assert( ovflPgno==0 || nOvfl>0 ); while( nOvfl-- ){ - Pgno iNext; - MemPage *pOvfl = 0; + MemPage *pOvfl; if( ovflPgno==0 || ovflPgno>pagerPagecount(pBt) ){ return SQLITE_CORRUPT_BKPT; } - if( nOvfl ){ - rc = getOverflowPage(pBt, ovflPgno, &pOvfl, &iNext); - if( rc ) return rc; - } - rc = freePage2(pBt, pOvfl, ovflPgno); - if( pOvfl ){ - sqlite3PagerUnref(pOvfl->pDbPage); - } + + rc = getOverflowPage(pBt, ovflPgno, &pOvfl, (nOvfl==0)?0:&ovflPgno); + if( rc ) return rc; + rc = freePage(pOvfl); + sqlite3PagerUnref(pOvfl->pDbPage); if( rc ) return rc; - ovflPgno = iNext; } return SQLITE_OK; } @@ -6361,6 +6229,11 @@ static int btreeCreateTable(Btree *p, int *piTable, int flags){ } assert( eType!=PTRMAP_ROOTPAGE ); assert( eType!=PTRMAP_FREEPAGE ); + rc = sqlite3PagerWrite(pRoot->pDbPage); + if( rc!=SQLITE_OK ){ + releasePage(pRoot); + return rc; + } rc = relocatePage(pBt, pRoot, eType, iPtrPage, pgnoMove, 0); releasePage(pRoot); @@ -7222,6 +7095,17 @@ const char *sqlite3BtreeGetFilename(Btree *p){ } /* +** Return the pathname of the directory that contains the database file. +** +** The pager directory name is invariant as long as the pager is +** open so it is safe to access without the BtShared mutex. +*/ +const char *sqlite3BtreeGetDirname(Btree *p){ + assert( p->pBt->pPager!=0 ); + return sqlite3PagerDirname(p->pBt->pPager); +} + +/* ** Return the pathname of the journal file for this database. The return ** value of this routine is the same regardless of whether the journal file ** has been created or not. @@ -7306,7 +7190,7 @@ static int btreeCopyFile(Btree *pTo, Btree *pFrom){ ** page is still on the rollback journal, though. And that is the ** whole point of this block: to put pages on the rollback journal. */ - sqlite3PagerDontWrite(pDbPage); + rc = sqlite3PagerDontWrite(pDbPage); } sqlite3PagerUnref(pDbPage); } diff --git a/src/btreeInt.h b/src/btreeInt.h index 89df4b7b8..b605453c5 100644 --- a/src/btreeInt.h +++ b/src/btreeInt.h @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btreeInt.h,v 1.39 2009/01/16 15:21:06 danielk1977 Exp $ +** $Id: btreeInt.h,v 1.40 2009/01/16 16:23:38 danielk1977 Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -204,6 +204,10 @@ ** * zero or more pages numbers of leaves */ #include "sqliteInt.h" +#include "pager.h" +#include "btree.h" +#include "os.h" +#include <assert.h> /* Round up a number to the next larger multiple of 8. This is used ** to force 8-byte alignment on 64-bit architectures. @@ -379,7 +383,6 @@ struct BtShared { void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */ void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */ sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */ - Bitvec *pHasContent; /* Set of pages moved to free-list this transaction */ #ifndef SQLITE_OMIT_SHARED_CACHE int nRef; /* Number of references to this structure */ BtShared *pNext; /* Next on a list of sharable BtShared structs */ @@ -487,10 +490,18 @@ struct BtCursor { #define CURSOR_REQUIRESEEK 2 #define CURSOR_FAULT 3 -/* -** The database page the PENDING_BYTE occupies. This page is never used. +/* The database page the PENDING_BYTE occupies. This page is never used. +** TODO: This macro is very similary to PAGER_MJ_PGNO() in pager.c. They +** should possibly be consolidated (presumably in pager.h). +** +** If disk I/O is omitted (meaning that the database is stored purely +** in memory) then there is no pending byte. */ -# define PENDING_BYTE_PAGE(pBt) PAGER_MJ_PGNO(pBt) +#ifdef SQLITE_OMIT_DISKIO +# define PENDING_BYTE_PAGE(pBt) 0x7fffffff +#else +# define PENDING_BYTE_PAGE(pBt) ((Pgno)((PENDING_BYTE/(pBt)->pageSize)+1)) +#endif /* ** A linked list of the following structures is stored at BtShared.pLock. diff --git a/src/pager.c b/src/pager.c index 82d2a6ead..0df2b594f 100644 --- a/src/pager.c +++ b/src/pager.c @@ -18,7 +18,7 @@ ** file simultaneously, or one process from reading the database while ** another is writing. ** -** @(#) $Id: pager.c,v 1.552 2009/01/16 15:21:06 danielk1977 Exp $ +** @(#) $Id: pager.c,v 1.553 2009/01/16 16:23:38 danielk1977 Exp $ */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" @@ -149,153 +149,73 @@ struct PagerSavepoint { /* ** A open page cache is an instance of the following structure. ** -** errCode -** -** Pager.errCode may be set to SQLITE_IOERR, SQLITE_CORRUPT, or -** or SQLITE_FULL. Once one of the first three errors occurs, it persists -** and is returned as the result of every major pager API call. The -** SQLITE_FULL return code is slightly different. It persists only until the -** next successful rollback is performed on the pager cache. Also, -** SQLITE_FULL does not affect the sqlite3PagerGet() and sqlite3PagerLookup() -** APIs, they may still be used successfully. -** -** dbSizeValid, dbSize, dbOrigSize, dbFileSize -** -** Managing the size of the database file in pages is a little complicated. -** The variable Pager.dbSize contains the number of pages that the database -** image currently contains. As the database image grows or shrinks this -** variable is updated. The variable Pager.dbFileSize contains the number -** of pages in the database file. This may be different from Pager.dbSize -** if some pages have been appended to the database image but not yet written -** out from the cache to the actual file on disk. Or if the image has been -** truncated by an incremental-vacuum operation. The Pager.dbOrigSize variable -** contains the number of pages in the database image when the current -** transaction was opened. The contents of all three of these variables is -** only guaranteed to be correct if the boolean Pager.dbSizeValid is true. -** -** TODO: Under what conditions is dbSizeValid set? Cleared? -** -** changeCountDone -** -** This boolean variable is used to make sure that the change-counter -** (the 4-byte header field at byte offset 24 of the database file) is -** not updated more often than necessary. -** -** It is set to true when the change-counter field is updated, which -** can only happen if an exclusive lock is held on the database file. -** It is cleared (set to false) whenever an exclusive lock is -** relinquished on the database file. Each time a transaction is committed, -** The changeCountDone flag is inspected. If it is true, the work of -** updating the change-counter is omitted for the current transaction. -** -** This mechanism means that when running in exclusive mode, a connection -** need only update the change-counter once, for the first transaction -** committed. -** -** dbModified -** -** The dbModified flag is set whenever a database page is dirtied. -** It is cleared at the end of each transaction. -** -** It is used when committing or otherwise ending a transaction. If -** the dbModified flag is clear then less work has to be done. -** -** TODO: Check some of the logic surrounding this optimization. -** -** journalStarted -** -** This flag is set whenever the the main journal is synced. -** -** The point of this flag is that it must be set after the -** first journal header in a journal file has been synced to disk. -** After this has happened, new pages appended to the database -** do not need the PGHDR_NEED_SYNC flag set, as they do not need -** to wait for a journal sync before they can be written out to -** the database file (see function pager_write()). -** -** setMaster -** -** This variable is used to ensure that the master journal file name -** (if any) is only written into the journal file once. -** -** When committing a transaction, the master journal file name (if any) -** may be written into the journal file while the pager is still in -** PAGER_RESERVED state (see CommitPhaseOne() for the action). It -** then attempts to upgrade to an exclusive lock. If this attempt -** fails, then SQLITE_BUSY may be returned to the user and the user -** may attempt to commit the transaction again later (calling -** CommitPhaseOne() again). This flag is used to ensure that the -** master journal name is only written to the journal file the first -** time CommitPhaseOne() is called. -** -** doNotSync -** -** This variable is set and cleared by sqlite3PagerWrite(). -** -** needSync -** -** TODO: It might be easier to set this variable in writeJournalHdr() -** and writeMasterJournal() only. Change its meaning to "unsynced data -** has been written to the journal". +** Pager.errCode may be set to SQLITE_IOERR, SQLITE_CORRUPT, or +** or SQLITE_FULL. Once one of the first three errors occurs, it persists +** and is returned as the result of every major pager API call. The +** SQLITE_FULL return code is slightly different. It persists only until the +** next successful rollback is performed on the pager cache. Also, +** SQLITE_FULL does not affect the sqlite3PagerGet() and sqlite3PagerLookup() +** APIs, they may still be used successfully. +** +** Managing the size of the database file in pages is a little complicated. +** The variable Pager.dbSize contains the number of pages that the database +** image currently contains. As the database image grows or shrinks this +** variable is updated. The variable Pager.dbFileSize contains the number +** of pages in the database file. This may be different from Pager.dbSize +** if some pages have been appended to the database image but not yet written +** out from the cache to the actual file on disk. Or if the image has been +** truncated by an incremental-vacuum operation. The Pager.dbOrigSize variable +** contains the number of pages in the database image when the current +** transaction was opened. The contents of all three of these variables is +** only guaranteed to be correct if the boolean Pager.dbSizeValid is true. */ struct Pager { sqlite3_vfs *pVfs; /* OS functions to use for IO */ - u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */ - u8 journalMode; /* On of the PAGER_JOURNALMODE_* values */ + 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 */ u8 noReadlock; /* Do not bother to obtain readlocks */ u8 noSync; /* Do not sync the journal if true */ u8 fullSync; /* Do extra syncs of the journal for robustness */ u8 sync_flags; /* One of SYNC_NORMAL or SYNC_FULL */ + u8 state; /* PAGER_UNLOCK, _SHARED, _RESERVED, etc. */ u8 tempFile; /* zFilename is a temporary file */ u8 readOnly; /* True for a read-only database */ - u8 memDb; /* True to inhibit all file I/O */ - - /* The following block contains those class members that are dynamically - ** modified during normal operations. The other variables in this structure - ** are either constant throughout the lifetime of the pager, or else - ** used to store configuration parameters that affect the way the pager - ** operates. - ** - ** The 'state' variable is described in more detail along with the - ** descriptions of the values it may take - PAGER_UNLOCK etc. Many of the - ** other variables in this block are described in the comment directly - ** above this class definition. - */ - u8 state; /* PAGER_UNLOCK, _SHARED, _RESERVED, etc. */ - u8 dbModified; /* True if there are any changes to the Db */ u8 needSync; /* True if an fsync() is needed on the journal */ - u8 journalStarted; /* True if header of journal is synced */ - u8 changeCountDone; /* Set after incrementing the change-counter */ + u8 dirtyCache; /* True if cached pages have changed */ + u8 memDb; /* True to inhibit all file I/O */ u8 setMaster; /* True if a m-j name has been written to jrnl */ u8 doNotSync; /* Boolean. While true, do not spill the cache */ + u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */ + u8 journalMode; /* On of the PAGER_JOURNALMODE_* values */ + u8 dbModified; /* True if there are any changes to the Db */ + u8 changeCountDone; /* Set after incrementing the change-counter */ u8 dbSizeValid; /* Set when dbSize is correct */ Pgno dbSize; /* Number of pages in the database */ Pgno dbOrigSize; /* dbSize before the current transaction */ Pgno dbFileSize; /* Number of pages in the database file */ + u32 vfsFlags; /* Flags for sqlite3_vfs.xOpen() */ int errCode; /* One of several kinds of errors */ - int nRec; /* Pages journalled since last j-header written */ + int nRec; /* Number of pages written to the journal */ u32 cksumInit; /* Quasi-random value added to every checksum */ - u32 nSubRec; /* Number of records written to sub-journal */ - Bitvec *pInJournal; /* One bit for each page in the database file */ - sqlite3_file *fd; /* File descriptor for database */ - sqlite3_file *jfd; /* File descriptor for main journal */ - sqlite3_file *sjfd; /* File descriptor for sub-journal */ - i64 journalOff; /* Current write offset in the journal file */ - i64 journalHdr; /* Byte offset to previous journal header */ - PagerSavepoint *aSavepoint; /* Array of active savepoints */ - int nSavepoint; /* Number of elements in aSavepoint[] */ - char dbFileVers[16]; /* Changes whenever database file changes */ - u32 sectorSize; /* Assumed sector size during rollback */ - + int stmtNRec; /* Number of records in stmt subjournal */ int nExtra; /* Add this many bytes to each in-memory page */ - u32 vfsFlags; /* Flags for sqlite3_vfs.xOpen() */ int pageSize; /* Number of bytes in a page */ + int nPage; /* Total number of in-memory pages */ + int mxPage; /* Maximum number of pages to hold in cache */ Pgno mxPgno; /* Maximum allowed size of the database */ + Bitvec *pInJournal; /* One bit for each page in the database file */ + Bitvec *pAlwaysRollback; /* One bit for each page marked always-rollback */ char *zFilename; /* Name of the database file */ char *zJournal; /* Name of the journal file */ + char *zDirectory; /* Directory hold database and journal files */ + sqlite3_file *fd, *jfd; /* File descriptors for database and journal */ + sqlite3_file *sjfd; /* File descriptor for the sub-journal*/ int (*xBusyHandler)(void*); /* Function to call when busy */ void *pBusyHandlerArg; /* Context argument for xBusyHandler */ + i64 journalOff; /* Current byte offset in the journal file */ + i64 journalHdr; /* Byte offset to previous journal header */ + u32 sectorSize; /* Assumed sector size during rollback */ #ifdef SQLITE_TEST int nHit, nMiss; /* Cache hits and missing */ int nRead, nWrite; /* Database pages read/written */ @@ -306,8 +226,11 @@ struct Pager { void *pCodecArg; /* First argument to xCodec() */ #endif char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ + char dbFileVers[16]; /* Changes whenever database file changes */ i64 journalSizeLimit; /* Size limit for persistent journal files */ PCache *pPCache; /* Pointer to page cache object */ + PagerSavepoint *aSavepoint; /* Array of active savepoints */ + int nSavepoint; /* Number of elements in aSavepoint[] */ }; /* @@ -354,14 +277,15 @@ static const unsigned char aJournalMagic[] = { }; /* -** The size of the of each page record in the journal is given by -** the following macro. +** The size of the header and of each page in the journal is determined +** by the following macros. */ #define JOURNAL_PG_SZ(pPager) ((pPager->pageSize) + 8) /* -** The journal header size for this pager. This is usually the same -** size as a single disk sector. See also setSectorSize(). +** The journal header size for this pager. In the future, this could be +** set to some value read from the disk controller. The important +** characteristic is that it is the same size as a disk sector. */ #define JOURNAL_HDR_SZ(pPager) (pPager->sectorSize) @@ -378,6 +302,16 @@ static const unsigned char aJournalMagic[] = { #endif /* +** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is +** reserved for working around a windows/posix incompatibility). It is +** used in the journal to signify that the remainder of the journal file +** is devoted to storing a master journal name - there are no more pages to +** roll back. See comments for function writeMasterJournal() for details. +*/ +/* #define PAGER_MJ_PGNO(x) (PENDING_BYTE/((x)->pageSize)) */ +#define PAGER_MJ_PGNO(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) + +/* ** The maximum legal page number is (2^31 - 1). */ #define PAGER_MAX_PGNO 2147483647 @@ -443,24 +377,10 @@ static int write32bits(sqlite3_file *fd, i64 offset, u32 val){ } /* -** The argument to this macro is a file descriptor (type sqlite3_file*). -** Return 0 if it is not open, or non-zero (but not 1) if it is. -** -** This is so that expressions can be written as: -** -** if( isOpen(pPager->jfd) ){ ... -** -** instead of -** -** if( pPager->jfd->pMethods ){ ... -*/ -#define isOpen(pFd) ((pFd)->pMethods) - -/* ** If file pFd is open, call sqlite3OsUnlock() on it. */ static int osUnlock(sqlite3_file *pFd, int eLock){ - if( !isOpen(pFd) ){ + if( !pFd->pMethods ){ return SQLITE_OK; } return sqlite3OsUnlock(pFd, eLock); @@ -475,44 +395,39 @@ static int osUnlock(sqlite3_file *pFd, int eLock){ ** (b) the value returned by OsSectorSize() is less than or equal ** to the page size. ** -** The optimization is also always enabled for temporary files. It is -** an error to call this function if pPager is opened on an in-memory -** database. -** ** If the optimization cannot be used, 0 is returned. If it can be used, ** then the value returned is the size of the journal file when it ** contains rollback data for exactly one page. */ #ifdef SQLITE_ENABLE_ATOMIC_WRITE static int jrnlBufferSize(Pager *pPager){ - assert( !MEMDB ); - if( !pPager->tempFile ){ - int dc; /* Device characteristics */ - int nSector; /* Sector size */ - int szPage; /* Page size */ + int dc; /* Device characteristics */ + int nSector; /* Sector size */ + int szPage; /* Page size */ + sqlite3_file *fd = pPager->fd; - assert( isOpen(pPager->fd) ); - dc = sqlite3OsDeviceCharacteristics(pPager->fd); + if( fd->pMethods ){ + dc = sqlite3OsDeviceCharacteristics(fd); nSector = pPager->sectorSize; szPage = pPager->pageSize; - - assert(SQLITE_IOCAP_ATOMIC512==(512>>8)); - assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8)); - if( 0==(dc&(SQLITE_IOCAP_ATOMIC|(szPage>>8)) || nSector>szPage) ){ - return 0; - } } - return JOURNAL_HDR_SZ(pPager) + JOURNAL_PG_SZ(pPager); + assert(SQLITE_IOCAP_ATOMIC512==(512>>8)); + assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8)); + + if( !fd->pMethods || + (dc & (SQLITE_IOCAP_ATOMIC|(szPage>>8)) && nSector<=szPage) ){ + return JOURNAL_HDR_SZ(pPager) + JOURNAL_PG_SZ(pPager); + } + return 0; } #endif /* -** This function should be called when an IOERR, CORRUPT or FULL error -** may have occured. The first argument is a pointer to the pager -** structure, the second the error-code about to be returned by a pager -** API function. The value returned is a copy of the second argument -** to this function. +** This function should be called when an error occurs within the pager +** code. The first argument is a pointer to the pager structure, the +** second the error-code about to be returned by a pager API function. +** The value returned is a copy of the second argument to this function. ** ** If the second argument is SQLITE_IOERR, SQLITE_CORRUPT, or SQLITE_FULL ** the error becomes persistent. Until the persisten error is cleared, @@ -523,8 +438,7 @@ static int jrnlBufferSize(Pager *pPager){ ** cannot be trusted. This state can be cleared by completely discarding ** the contents of the pager-cache. If a transaction was active when ** the persistent error occured, then the rollback journal may need -** to be replayed to restore the contents of the database file (as if -** it were a hot-journal). +** to be replayed. */ static void pager_unlock(Pager *pPager); static int pager_error(Pager *pPager, int rc){ @@ -597,10 +511,8 @@ static void checkPage(PgHdr *pPg){ /* ** When this is called the journal file for pager pPager must be open. -** This function attempts to read a master journal file name from the -** end of the file and, if successful, copies it into memory supplied -** by the caller. See comments above writeMasterJournal() for the format -** used to store a master journal file name at the end of a journal file. +** The master journal file name is read from the end of the file and +** written into memory supplied by the caller. ** ** zMaster must point to a buffer of at least nMaster bytes allocated by ** the caller. This should be sqlite3_vfs.mxPathname+1 (to ensure there is @@ -609,68 +521,70 @@ static void checkPage(PgHdr *pPg){ ** nul-terminator), then this is handled as if no master journal name ** were present in the journal. ** -** If a master journal file name is present at the end of the journal -** file, then it is copied into the buffer pointed to by zMaster. A -** nul-terminator byte is appended to the buffer following the master -** journal file name. -** -** If it is determined that no master journal file name is present -** zMaster[0] is set to 0 and SQLITE_OK returned. -** -** If an error occurs while reading from the journal file, an SQLite -** error code is returned. +** If no master journal file name is present zMaster[0] is set to 0 and +** SQLITE_OK returned. */ static int readMasterJournal(sqlite3_file *pJrnl, char *zMaster, u32 nMaster){ - int rc; /* Return code */ - u32 len; /* Length in bytes of master journal name */ - i64 szJ; /* Total size in bytes of journal file pJrnl */ - u32 cksum; /* MJ checksum value read from journal */ - u32 u; /* Unsigned loop counter */ - unsigned char aMagic[8]; /* A buffer to hold the magic header */ + int rc; + u32 len; + i64 szJ; + u32 cksum; + u32 u; /* Unsigned loop counter */ + unsigned char aMagic[8]; /* A buffer to hold the magic header */ + zMaster[0] = '\0'; - if( SQLITE_OK!=(rc = sqlite3OsFileSize(pJrnl, &szJ)) - || szJ<16 - || SQLITE_OK!=(rc = read32bits(pJrnl, szJ-16, &len)) - || len>=nMaster - || SQLITE_OK!=(rc = read32bits(pJrnl, szJ-12, &cksum)) - || SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8)) - || memcmp(aMagic, aJournalMagic, 8) - || SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, zMaster, len, szJ-16-len)) - ){ + rc = sqlite3OsFileSize(pJrnl, &szJ); + if( rc!=SQLITE_OK || szJ<16 ) return rc; + + rc = read32bits(pJrnl, szJ-16, &len); + if( rc!=SQLITE_OK ) return rc; + + if( len>=nMaster ){ + return SQLITE_OK; + } + + rc = read32bits(pJrnl, szJ-12, &cksum); + if( rc!=SQLITE_OK ) return rc; + + rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8); + if( rc!=SQLITE_OK || memcmp(aMagic, aJournalMagic, 8) ) return rc; + + rc = sqlite3OsRead(pJrnl, zMaster, len, szJ-16-len); + if( rc!=SQLITE_OK ){ return rc; } + zMaster[len] = '\0'; /* See if the checksum matches the master journal name */ for(u=0; u<len; u++){ cksum -= zMaster[u]; - } + } if( cksum ){ /* If the checksum doesn't add up, then one or more of the disk sectors ** containing the master journal filename is corrupted. This means ** definitely roll back, so just return SQLITE_OK and report a (nul) ** master-journal filename. */ - len = 0; + zMaster[0] = '\0'; } - zMaster[len] = '\0'; return SQLITE_OK; } /* -** Return the offset of the sector boundary at or immediately -** following the value in pPager->journalOff, assuming a sector -** size of pPager->sectorSize bytes. +** Seek the journal file descriptor to the next sector boundary where a +** journal header may be read or written. Pager.journalOff is updated with +** the new seek offset. ** ** i.e for a sector size of 512: ** -** Pager.journalOff Return value -** --------------------------------------- -** 0 0 -** 512 512 -** 100 512 -** 2000 2048 +** Input Offset Output Offset +** --------------------------------------- +** 0 0 +** 512 512 +** 100 512 +** 2000 2048 ** */ static i64 journalHdrOffset(Pager *pPager){ @@ -684,39 +598,26 @@ static i64 journalHdrOffset(Pager *pPager){ assert( (offset-c)<JOURNAL_HDR_SZ(pPager) ); return offset; } +static void seekJournalHdr(Pager *pPager){ + pPager->journalOff = journalHdrOffset(pPager); +} /* -** The journal file must be open when this function is called. -** -** This function is a no-op if the journal file has not been written to -** within the current transaction (i.e. if Pager.journalOff==0). -** -** If doTruncate is non-zero or the Pager.journalSizeLimit variable is -** set to 0, then truncate the journal file to zero bytes in size. Otherwise, -** zero the 28-byte header at the start of the journal file. In either case, -** if the pager is not in no-sync mode, sync the journal file immediately -** after writing or truncating it. -** -** If Pager.journalSizeLimit is set to a positive, non-zero value, and -** following the truncation or zeroing described above the size of the -** journal file in bytes is larger than this value, then truncate the -** journal file to Pager.journalSizeLimit bytes. The journal file does -** not need to be synced following this operation. -** -** If an IO error occurs, abandon processing and return the IO error code. -** Otherwise, return SQLITE_OK. +** Write zeros over the header of the journal file. This has the +** effect of invalidating the journal file and committing the +** transaction. */ static int zeroJournalHdr(Pager *pPager, int doTruncate){ - int rc = SQLITE_OK; /* Return code */ - assert( isOpen(pPager->jfd) ); + int rc = SQLITE_OK; + static const char zeroHdr[28] = {0}; + if( pPager->journalOff ){ - const i64 iLimit = pPager->journalSizeLimit; /* Local cache of jsl */ + i64 iLimit = pPager->journalSizeLimit; IOTRACE(("JZEROHDR %p\n", pPager)) if( doTruncate || iLimit==0 ){ rc = sqlite3OsTruncate(pPager->jfd, 0); }else{ - static const char zeroHdr[28] = {0}; rc = sqlite3OsWrite(pPager->jfd, zeroHdr, sizeof(zeroHdr), 0); } if( rc==SQLITE_OK && !pPager->noSync ){ @@ -756,21 +657,19 @@ static int zeroJournalHdr(Pager *pPager, int doTruncate){ ** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space. */ static int writeJournalHdr(Pager *pPager){ - int rc = SQLITE_OK; /* Return code */ - char *zHeader = pPager->pTmpSpace; /* Temporary space used to build header */ - u32 nHeader = pPager->pageSize; /* Size of buffer pointed to by zHeader */ - u32 nWrite; /* Bytes of header sector written */ - int ii; /* Loop counter */ - - assert( isOpen(pPager->jfd) ); /* Journal file must be open. */ + int rc = SQLITE_OK; + char *zHeader = pPager->pTmpSpace; + u32 nHeader = pPager->pageSize; + u32 nWrite; + int ii; if( nHeader>JOURNAL_HDR_SZ(pPager) ){ nHeader = JOURNAL_HDR_SZ(pPager); } - /* If there are active savepoints and any of them were created - ** since the most recent journal header was written, update the - ** PagerSavepoint.iHdrOffset fields now. + /* If there are active savepoints and any of them were created since the + ** most recent journal header was written, update the PagerSavepoint.iHdrOff + ** fields now. */ for(ii=0; ii<pPager->nSavepoint; ii++){ if( pPager->aSavepoint[ii].iHdrOffset==0 ){ @@ -778,7 +677,9 @@ static int writeJournalHdr(Pager *pPager){ } } - pPager->journalHdr = pPager->journalOff = journalHdrOffset(pPager); + seekJournalHdr(pPager); + pPager->journalHdr = pPager->journalOff; + memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic)); /* @@ -801,7 +702,7 @@ static int writeJournalHdr(Pager *pPager){ ** * When the SQLITE_IOCAP_SAFE_APPEND flag is set. This guarantees ** that garbage data is never appended to the journal file. */ - assert( isOpen(pPager->fd) || pPager->noSync ); + assert(pPager->fd->pMethods||pPager->noSync); if( (pPager->noSync) || (pPager->journalMode==PAGER_JOURNALMODE_MEMORY) || (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND) ){ @@ -818,34 +719,19 @@ static int writeJournalHdr(Pager *pPager){ /* The assumed sector size for this process */ put32bits(&zHeader[sizeof(aJournalMagic)+12], pPager->sectorSize); - /* The page size */ - put32bits(&zHeader[sizeof(aJournalMagic)+16], pPager->pageSize); - /* Initializing the tail of the buffer is not necessary. Everything ** works find if the following memset() is omitted. But initializing ** the memory prevents valgrind from complaining, so we are willing to ** take the performance hit. */ - memset(&zHeader[sizeof(aJournalMagic)+20], 0, - nHeader-(sizeof(aJournalMagic)+20)); - - /* In theory, it is only necessary to write the 28 bytes that the - ** journal header consumes to the journal file here. Then increment the - ** Pager.journalOff variable by JOURNAL_HDR_SZ so that the next - ** record is written to the following sector (leaving a gap in the file - ** that will be implicitly filled in by the OS). - ** - ** However it has been discovered that on some systems this pattern can - ** be significantly slower than contiguously writing data to the file, - ** even if that means explicitly writing data to the block of - ** (JOURNAL_HDR_SZ - 28) bytes that will not be used. So that is what - ** is done. - ** - ** The loop is required here in case the sector-size is larger than the - ** database page size. Since the zHeader buffer is only Pager.pageSize - ** bytes in size, more than one call to sqlite3OsWrite() may be required - ** to populate the entire journal header sector. - */ + memset(&zHeader[sizeof(aJournalMagic)+16], 0, + nHeader-(sizeof(aJournalMagic)+16)); + + if( pPager->journalHdr==0 ){ + /* The page size */ + put32bits(&zHeader[sizeof(aJournalMagic)+16], pPager->pageSize); + } + for(nWrite=0; rc==SQLITE_OK&&nWrite<JOURNAL_HDR_SZ(pPager); nWrite+=nHeader){ IOTRACE(("JHDR %p %lld %d\n", pPager, pPager->journalHdr, nHeader)) rc = sqlite3OsWrite(pPager->jfd, zHeader, nHeader, pPager->journalOff); @@ -859,114 +745,93 @@ static int writeJournalHdr(Pager *pPager){ ** The journal file must be open when this is called. A journal header file ** (JOURNAL_HDR_SZ bytes) is read from the current location in the journal ** file. The current location in the journal file is given by -** pPager->journalOff. See comments above function writeJournalHdr() for +** pPager->journalOff. See comments above function writeJournalHdr() for ** a description of the journal header format. ** -** If the header is read successfully, *pNRec is set to the number of -** page records following this header and *pDbSize is set to the size of the +** If the header is read successfully, *nRec is set to the number of +** page records following this header and *dbSize is set to the size of the ** database before the transaction began, in pages. Also, pPager->cksumInit ** is set to the value read from the journal header. SQLITE_OK is returned ** in this case. ** ** If the journal header file appears to be corrupted, SQLITE_DONE is -** returned and *pNRec and *PDbSize are undefined. If JOURNAL_HDR_SZ bytes +** returned and *nRec and *dbSize are undefined. If JOURNAL_HDR_SZ bytes ** cannot be read from the journal file an error code is returned. */ static int readJournalHdr( - Pager *pPager, /* Pager object */ - i64 journalSize, /* Size of the open journal file in bytes */ - u32 *pNRec, /* OUT: Value read from the nRec field */ - u32 *pDbSize /* OUT: Value of original database size field */ + Pager *pPager, + i64 journalSize, + u32 *pNRec, + u32 *pDbSize ){ - int rc; /* Return code */ - unsigned char aMagic[8]; /* A buffer to hold the magic header */ - i64 iHdrOff; /* Offset of journal header being read */ - - assert( isOpen(pPager->jfd) ); /* Journal file must be open. */ + int rc; + unsigned char aMagic[8]; /* A buffer to hold the magic header */ + i64 jrnlOff; + u32 iPageSize; + u32 iSectorSize; - /* Advance Pager.journalOff to the start of the next sector. If the - ** journal file is too small for there to be a header stored at this - ** point, return SQLITE_DONE. - */ - pPager->journalOff = journalHdrOffset(pPager); + seekJournalHdr(pPager); if( pPager->journalOff+JOURNAL_HDR_SZ(pPager) > journalSize ){ return SQLITE_DONE; } - iHdrOff = pPager->journalOff; + jrnlOff = pPager->journalOff; + + rc = sqlite3OsRead(pPager->jfd, aMagic, sizeof(aMagic), jrnlOff); + if( rc ) return rc; + jrnlOff += sizeof(aMagic); - /* Read in the first 8 bytes of the journal header. If they do not match - ** the magic string found at the start of each journal header, return - ** SQLITE_DONE. If an IO error occurs, return an error code. Otherwise, - ** proceed. - */ - rc = sqlite3OsRead(pPager->jfd, aMagic, sizeof(aMagic), iHdrOff); - if( rc ){ - return rc; - } if( memcmp(aMagic, aJournalMagic, sizeof(aMagic))!=0 ){ return SQLITE_DONE; } - /* Read the first three 32-bit fields of the journal header: The nRec - ** field, the checksum-initializer and the database size at the start - ** of the transaction. Return an error code if anything goes wrong. - */ - if( SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+8, pNRec)) - || SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+12, &pPager->cksumInit)) - || SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+16, pDbSize)) - ){ - return rc; - } + rc = read32bits(pPager->jfd, jrnlOff, pNRec); + if( rc ) return rc; - if( pPager->journalOff==0 ){ - u32 iPageSize; /* Page-size field of journal header */ - u32 iSectorSize; /* Sector-size field of journal header */ - u16 iPageSize16; /* Copy of iPageSize in 16-bit variable */ + rc = read32bits(pPager->jfd, jrnlOff+4, &pPager->cksumInit); + if( rc ) return rc; - /* Read the page-size and sector-size journal header fields. */ - if( SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+20, &iSectorSize)) - || SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+24, &iPageSize)) - ){ - return rc; - } + rc = read32bits(pPager->jfd, jrnlOff+8, pDbSize); + if( rc ) return rc; - /* Check that the values read from the page-size and sector-size fields - ** are within range. To be 'in range', both values need to be a power - ** of two greater than or equal to 512, and not greater than their - ** respective compile time maximum limits. - */ - if( iPageSize<512 || iSectorSize<512 - || iPageSize>SQLITE_MAX_PAGE_SIZE || iSectorSize>MAX_SECTOR_SIZE - || ((iPageSize-1)&iPageSize)!=0 || ((iSectorSize-1)&iSectorSize)!=0 + if( pPager->journalOff==0 ){ + rc = read32bits(pPager->jfd, jrnlOff+16, &iPageSize); + if( rc ) return rc; + + if( iPageSize<512 + || iPageSize>SQLITE_MAX_PAGE_SIZE + || ((iPageSize-1)&iPageSize)!=0 ){ - /* If the either the page-size or sector-size in the journal-header is - ** invalid, then the process that wrote the journal-header must have - ** crashed before the header was synced. In this case stop reading - ** the journal file here. + /* If the page-size in the journal-header is invalid, then the process + ** that wrote the journal-header must have crashed before the header + ** was synced. In this case stop reading the journal file here. */ - return SQLITE_DONE; + rc = SQLITE_DONE; + }else{ + u16 pagesize = (u16)iPageSize; + rc = sqlite3PagerSetPagesize(pPager, &pagesize); + assert( rc!=SQLITE_OK || pagesize==(u16)iPageSize ); } - - /* Update the page-size to match the value read from the journal. - ** Use a testcase() macro to make sure that malloc failure within - ** PagerSetPagesize() is tested. - */ - iPageSize16 = (u16)iPageSize; - rc = sqlite3PagerSetPagesize(pPager, &iPageSize16); - testcase( rc!=SQLITE_OK ); - assert( rc!=SQLITE_OK || iPageSize16==(u16)iPageSize ); - + if( rc ) return rc; + /* Update the assumed sector-size to match the value used by ** the process that created this journal. If this journal was ** created by a process other than this one, then this routine ** is being called from within pager_playback(). The local value ** of Pager.sectorSize is restored at the end of that routine. */ + rc = read32bits(pPager->jfd, jrnlOff+12, &iSectorSize); + if( rc ) return rc; + if( (iSectorSize&(iSectorSize-1)) + || iSectorSize<512 + || iSectorSize>MAX_SECTOR_SIZE + ){ + return SQLITE_DONE; + } pPager->sectorSize = iSectorSize; } pPager->journalOff += JOURNAL_HDR_SZ(pPager); - return rc; + return SQLITE_OK; } @@ -977,35 +842,34 @@ static int readJournalHdr( ** journal file descriptor is advanced to the next sector boundary before ** anything is written. The format is: ** -** + 4 bytes: PAGER_MJ_PGNO. -** + N bytes: Master journal filename in utf-8. -** + 4 bytes: N (length of master journal name in bytes, no nul-terminator). -** + 4 bytes: Master journal name checksum. -** + 8 bytes: aJournalMagic[]. +** + 4 bytes: PAGER_MJ_PGNO. +** + N bytes: length of master journal name. +** + 4 bytes: N +** + 4 bytes: Master journal name checksum. +** + 8 bytes: aJournalMagic[]. ** ** The master journal page checksum is the sum of the bytes in the master -** journal name, where each byte is interpreted as a signed 8-bit integer. +** journal name. ** ** If zMaster is a NULL pointer (occurs for a single database transaction), ** this call is a no-op. */ static int writeMasterJournal(Pager *pPager, const char *zMaster){ - int rc; /* Return code */ - int nMaster; /* Length of string zMaster */ - i64 iHdrOff; /* Offset of header in journal file */ - i64 jrnlSize; /* Size of journal file on disk */ - u32 cksum = 0; /* Checksum of string zMaster */ - - assert( isOpen(pPager->jfd) ); - assert( !pPager->setMaster ); - - if( !zMaster ) return SQLITE_OK; + int rc; + int len; + int i; + i64 jrnlOff; + i64 jrnlSize; + u32 cksum = 0; + char zBuf[sizeof(aJournalMagic)+2*4]; + + if( !zMaster || pPager->setMaster ) return SQLITE_OK; if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ) return SQLITE_OK; pPager->setMaster = 1; - /* Calculate the length in bytes and the checksum of zMaster */ - for(nMaster=0; zMaster[nMaster]; nMaster++){ - cksum += zMaster[nMaster]; + len = sqlite3Strlen30(zMaster); + for(i=0; i<len; i++){ + cksum += zMaster[i]; } /* If in full-sync mode, advance to the next disk sector before writing @@ -1013,22 +877,24 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){ ** the journal has already been synced. */ if( pPager->fullSync ){ - pPager->journalOff = journalHdrOffset(pPager); + seekJournalHdr(pPager); } - iHdrOff = pPager->journalOff; + jrnlOff = pPager->journalOff; + pPager->journalOff += (len+20); - /* Write the master journal data to the end of the journal file. If - ** an error occurs, return the error code to the caller. - */ - if( (rc = write32bits(pPager->jfd, iHdrOff, PAGER_MJ_PGNO(pPager))) - || (rc = sqlite3OsWrite(pPager->jfd, zMaster, nMaster, iHdrOff+4)) - || (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster, nMaster)) - || (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster+4, cksum)) - || (rc = sqlite3OsWrite(pPager->jfd, aJournalMagic, 8, iHdrOff+4+nMaster+8)) - ){ - return rc; - } - pPager->journalOff += (nMaster+20); + rc = write32bits(pPager->jfd, jrnlOff, PAGER_MJ_PGNO(pPager)); + if( rc!=SQLITE_OK ) return rc; + jrnlOff += 4; + + rc = sqlite3OsWrite(pPager->jfd, zMaster, len, jrnlOff); + if( rc!=SQLITE_OK ) return rc; + jrnlOff += len; + + put32bits(zBuf, len); + put32bits(&zBuf[4], cksum); + memcpy(&zBuf[8], aJournalMagic, sizeof(aJournalMagic)); + rc = sqlite3OsWrite(pPager->jfd, zBuf, 8+sizeof(aJournalMagic), jrnlOff); + jrnlOff += 8+sizeof(aJournalMagic); pPager->needSync = !pPager->noSync; /* If the pager is in peristent-journal mode, then the physical @@ -1041,37 +907,34 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){ ** Easiest thing to do in this scenario is to truncate the journal ** file to the required size. */ - if( SQLITE_OK==(rc = sqlite3OsFileSize(pPager->jfd, &jrnlSize)) - && jrnlSize>pPager->journalOff + if( (rc==SQLITE_OK) + && (rc = sqlite3OsFileSize(pPager->jfd, &jrnlSize))==SQLITE_OK + && jrnlSize>jrnlOff ){ - rc = sqlite3OsTruncate(pPager->jfd, pPager->journalOff); + rc = sqlite3OsTruncate(pPager->jfd, jrnlOff); } return rc; } /* -** Find a page in the hash table given its page number. Return -** a pointer to the page or NULL if the requested page is not -** already in memory. +** Find a page in the hash table given its page number. Return +** a pointer to the page or NULL if not found. */ static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){ - PgHdr *p; /* Return value */ - - /* It is not possible for a call to PcacheFetch() with createFlag==0 to - ** fail, since no attempt to allocate dynamic memory will be made. - */ - (void)sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &p); + PgHdr *p; + sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &p); return p; } /* -** Unless the pager is in error-state, discard all in-memory pages. If -** the pager is in error-state, then this call is a no-op. +** Clear the in-memory cache. This routine +** sets the state of the pager back to what it was when it was first +** opened. Any outstanding pages are invalidated and subsequent attempts +** to access those pages will likely result in a coredump. */ static void pager_reset(Pager *pPager){ - if( SQLITE_OK==pPager->errCode ){ - sqlite3PcacheClear(pPager->pPCache); - } + if( pPager->errCode ) return; + sqlite3PcacheClear(pPager->pPCache); } /* @@ -1079,8 +942,8 @@ static void pager_reset(Pager *pPager){ ** Pager.aSavepoint and Pager.nSavepoint to zero. Close the sub-journal ** if it is open and the pager is not in exclusive mode. */ -static void releaseAllSavepoints(Pager *pPager){ - int ii; /* Iterator for looping through Pager.aSavepoint */ +static void releaseAllSavepoint(Pager *pPager){ + int ii; for(ii=0; ii<pPager->nSavepoint; ii++){ sqlite3BitvecDestroy(pPager->aSavepoint[ii].pInSavepoint); } @@ -1090,13 +953,12 @@ static void releaseAllSavepoints(Pager *pPager){ sqlite3_free(pPager->aSavepoint); pPager->aSavepoint = 0; pPager->nSavepoint = 0; - pPager->nSubRec = 0; + pPager->stmtNRec = 0; } /* -** Set the bit number pgno in the PagerSavepoint.pInSavepoint -** bitvecs of all open savepoints. Return SQLITE_OK if successful -** or SQLITE_NOMEM if a malloc failure occurs. +** Set the bit number pgno in the PagerSavepoint.pInSavepoint bitvecs of +** all open savepoints. */ static int addToSavepointBitvecs(Pager *pPager, Pgno pgno){ int ii; /* Loop counter */ @@ -1106,7 +968,6 @@ static int addToSavepointBitvecs(Pager *pPager, Pgno pgno){ PagerSavepoint *p = &pPager->aSavepoint[ii]; if( pgno<=p->nOrig ){ rc |= sqlite3BitvecSet(p->pInSavepoint, pgno); - testcase( rc==SQLITE_NOMEM ); assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); } } @@ -1114,8 +975,7 @@ static int addToSavepointBitvecs(Pager *pPager, Pgno pgno){ } /* -** Unlock the database file. This function is a no-op if the pager -** is in exclusive mode. +** Unlock the database file. ** ** If the pager is currently in error state, discard the contents of ** the cache and reset the Pager structure internal state. If there is @@ -1125,28 +985,24 @@ static int addToSavepointBitvecs(Pager *pPager, Pgno pgno){ */ static void pager_unlock(Pager *pPager){ if( !pPager->exclusiveMode ){ - int rc; /* Return code */ + int rc; /* Always close the journal file when dropping the database lock. ** Otherwise, another connection with journal_mode=delete might ** delete the file out from under us. */ - sqlite3OsClose(pPager->jfd); - sqlite3BitvecDestroy(pPager->pInJournal); - pPager->pInJournal = 0; - releaseAllSavepoints(pPager); - - /* If the file is unlocked, somebody else might change it. The - ** values stored in Pager.dbSize etc. might become invalid if - ** this happens. TODO: Really, this doesn't need to be cleared - ** until the change-counter check fails in pagerSharedLock(). - */ - pPager->dbSizeValid = 0; + if( pPager->journalOpen ){ + sqlite3OsClose(pPager->jfd); + pPager->journalOpen = 0; + sqlite3BitvecDestroy(pPager->pInJournal); + pPager->pInJournal = 0; + sqlite3BitvecDestroy(pPager->pAlwaysRollback); + pPager->pAlwaysRollback = 0; + } rc = osUnlock(pPager->fd, NO_LOCK); - if( rc ){ - pPager->errCode = rc; - } + if( rc ) pPager->errCode = rc; + pPager->dbSizeValid = 0; IOTRACE(("UNLOCK %p\n", pPager)) /* If Pager.errCode is set, the contents of the pager cache cannot be @@ -1154,123 +1010,62 @@ static void pager_unlock(Pager *pPager){ ** cache can be discarded and the error code safely cleared. */ if( pPager->errCode ){ - if( rc==SQLITE_OK ){ - pPager->errCode = SQLITE_OK; - } + if( rc==SQLITE_OK ) pPager->errCode = SQLITE_OK; pager_reset(pPager); + releaseAllSavepoint(pPager); + pPager->journalOff = 0; + pPager->journalStarted = 0; + pPager->dbOrigSize = 0; } - pPager->changeCountDone = 0; pPager->state = PAGER_UNLOCK; + pPager->changeCountDone = 0; } } /* ** Execute a rollback if a transaction is active and unlock the -** database file. -** -** If the pager has already entered the error state, do not attempt -** the rollback at this time. Instead, pager_unlock() is called. The -** call to pager_unlock() will discard all in-memory pages, unlock -** the database file and clear the error state. If this means that -** there is a hot-journal left in the file-system, the next connection -** to obtain a shared lock on the pager (which may be this one) will -** roll it back. -** -** If the pager has not already entered the error state, but an IO or -** malloc error occurs during a rollback, then this will itself cause -** the pager to enter the error state. Which will be cleared by the -** call to pager_unlock(), as described above. +** database file. If the pager has already entered the error state, +** do not attempt the rollback. */ -static void pagerUnlockAndRollback(Pager *pPager){ - if( pPager->errCode==SQLITE_OK && pPager->state>=PAGER_RESERVED ){ +static void pagerUnlockAndRollback(Pager *p){ + if( p->errCode==SQLITE_OK && p->state>=PAGER_RESERVED ){ sqlite3BeginBenignMalloc(); - sqlite3PagerRollback(pPager); + sqlite3PagerRollback(p); sqlite3EndBenignMalloc(); } - pager_unlock(pPager); + pager_unlock(p); } /* -** This routine ends a transaction. A transaction is usually ended by -** either a COMMIT or a ROLLBACK operation. This routine may be called -** after rollback of a hot-journal, or if an error occurs while opening -** the journal file or writing the very first journal-header of a -** database transaction. -** -** If the pager is in PAGER_SHARED or PAGER_UNLOCK state when this -** routine is called, it is a no-op (returns SQLITE_OK). -** -** Otherwise, any active savepoints are released. -** -** If the journal file is open, then it is "finalized". Once a journal -** file has been finalized it is not possible to use it to roll back a -** transaction. Nor will it be considered to be a hot-journal by this -** or any other database connection. Exactly how a journal is finalized -** depends on whether or not the pager is running in exclusive mode and -** the current journal-mode (Pager.journalMode value), as follows: -** -** journalMode==MEMORY -** Journal file descriptor is simply closed. This destroys an -** in-memory journal. +** This routine ends a transaction. A transaction is ended by either +** a COMMIT or a ROLLBACK. ** -** journalMode==TRUNCATE -** Journal file is truncated to zero bytes in size. +** When this routine is called, the pager has the journal file open and +** a RESERVED or EXCLUSIVE lock on the database. This routine will release +** the database lock and acquires a SHARED lock in its place if that is +** the appropriate thing to do. Release locks usually is appropriate, +** unless we are in exclusive access mode or unless this is a +** COMMIT AND BEGIN or ROLLBACK AND BEGIN operation. ** -** journalMode==PERSIST -** The first 28 bytes of the journal file are zeroed. This invalidates -** the first journal header in the file, and hence the entire journal -** file. An invalid journal file cannot be rolled back. +** The journal file is either deleted or truncated. ** -** journalMode==DELETE -** The journal file is closed and deleted using sqlite3OsDelete(). -** -** If the pager is running in exclusive mode, this method of finalizing -** the journal file is never used. Instead, if the journalMode is -** DELETE and the pager is in exclusive mode, the method described under -** journalMode==PERSIST is used instead. -** -** After the journal is finalized, if running in non-exclusive mode, the -** pager moves to PAGER_SHARED state (and downgrades the lock on the -** database file accordingly). -** -** If the pager is running in exclusive mode and is in PAGER_SYNCED state, -** it moves to PAGER_EXCLUSIVE. No locks are downgraded when running in -** exclusive mode. -** -** SQLITE_OK is returned if no error occurs. If an error occurs during -** any of the IO operations to finalize the journal file or unlock the -** database then the IO error code is returned to the user. If the -** operation to finalize the journal file fails, then the code still -** tries to unlock the database file if not in exclusive mode. If the -** unlock operation fails as well, then the first error code related -** to the first error encountered (the journal finalization one) is -** returned. +** TODO: Consider keeping the journal file open for temporary databases. +** This might give a performance improvement on windows where opening +** a file is an expensive operation. */ static int pager_end_transaction(Pager *pPager, int hasMaster){ - int rc = SQLITE_OK; /* Error code from journal finalization operation */ - int rc2 = SQLITE_OK; /* Error code from db file unlock operation */ - + int rc = SQLITE_OK; + int rc2 = SQLITE_OK; if( pPager->state<PAGER_RESERVED ){ return SQLITE_OK; } - releaseAllSavepoints(pPager); - - assert( isOpen(pPager->jfd) || pPager->pInJournal==0 ); - if( isOpen(pPager->jfd) ){ - - /* TODO: There's a problem here if a journal-file was opened in MEMORY - ** mode and then the journal-mode is changed to TRUNCATE or PERSIST - ** during the transaction. This code should be changed to assume - ** that the journal mode has not changed since the transaction was - ** started. And the sqlite3PagerJournalMode() function should be - ** changed to make sure that this is the case too. - */ - - /* Finalize the journal file. */ + releaseAllSavepoint(pPager); + if( pPager->journalOpen ){ if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){ int isMemoryJournal = sqlite3IsMemJournal(pPager->jfd); sqlite3OsClose(pPager->jfd); + pPager->journalOpen = 0; if( !isMemoryJournal ){ rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); } @@ -1288,19 +1083,23 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){ }else{ assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE || rc ); sqlite3OsClose(pPager->jfd); + pPager->journalOpen = 0; if( rc==SQLITE_OK && !pPager->tempFile ){ rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); } } - + sqlite3BitvecDestroy(pPager->pInJournal); + pPager->pInJournal = 0; + sqlite3BitvecDestroy(pPager->pAlwaysRollback); + pPager->pAlwaysRollback = 0; #ifdef SQLITE_CHECK_PAGES sqlite3PcacheIterateDirty(pPager->pPCache, pager_set_pagehash); #endif - sqlite3PcacheCleanAll(pPager->pPCache); - sqlite3BitvecDestroy(pPager->pInJournal); - pPager->pInJournal = 0; + pPager->dirtyCache = 0; pPager->nRec = 0; + }else{ + assert( pPager->pInJournal==0 ); } if( !pPager->exclusiveMode ){ @@ -1310,43 +1109,42 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){ }else if( pPager->state==PAGER_SYNCED ){ pPager->state = PAGER_EXCLUSIVE; } + pPager->dbOrigSize = 0; pPager->setMaster = 0; pPager->needSync = 0; - pPager->dbModified = 0; - - /* TODO: Is this optimal? Why is the db size invalidated here - ** when the database file is not unlocked? */ - pPager->dbOrigSize = 0; + /* lruListSetFirstSynced(pPager); */ sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize); if( !MEMDB ){ pPager->dbSizeValid = 0; } + pPager->dbModified = 0; return (rc==SQLITE_OK?rc2:rc); } /* -** Parameter aData must point to a buffer of pPager->pageSize bytes -** of data. Compute and return a checksum based ont the contents of the -** page of data and the current value of pPager->cksumInit. -** -** This is not a real checksum. It is really just the sum of the -** random initial value (pPager->cksumInit) and every 200th byte -** of the page data, starting with byte offset (pPager->pageSize%200). -** Each byte is interpreted as an 8-bit unsigned integer. +** Compute and return a checksum for the page of data. ** -** Changing the formula used to compute this checksum results in an -** incompatible journal file format. +** This is not a real checksum. It is really just the sum of the +** random initial value and the page number. We experimented with +** a checksum of the entire data, but that was found to be too slow. ** -** If journal corruption occurs due to a power failure, the most likely -** scenario is that one end or the other of the record will be changed. -** It is much less likely that the two ends of the journal record will be +** Note that the page number is stored at the beginning of data and +** the checksum is stored at the end. This is important. If journal +** corruption occurs due to a power failure, the most likely scenario +** is that one end or the other of the record will be changed. It is +** much less likely that the two ends of the journal record will be ** correct and the middle be corrupt. Thus, this "checksum" scheme, ** though fast and simple, catches the mostly likely kind of corruption. +** +** FIX ME: Consider adding every 200th (or so) byte of the data to the +** checksum. That way if a single page spans 3 or more disk sectors and +** only the middle sector is corrupt, we will still have a reasonable +** chance of failing the checksum and thus detecting the problem. */ static u32 pager_cksum(Pager *pPager, const u8 *aData){ - u32 cksum = pPager->cksumInit; /* Checksum value to return */ - int i = pPager->pageSize-200; /* Loop counter */ + u32 cksum = pPager->cksumInit; + int i = pPager->pageSize-200; while( i>0 ){ cksum += aData[i]; i -= 200; @@ -1357,40 +1155,18 @@ static u32 pager_cksum(Pager *pPager, const u8 *aData){ /* ** Read a single page from either the journal file (if isMainJrnl==1) or ** from the sub-journal (if isMainJrnl==0) and playback that page. -** The page begins at offset *pOffset into the file. The *pOffset +** The page begins at offset *pOffset into the file. The *pOffset ** value is increased to the start of the next page in the journal. ** ** The isMainJrnl flag is true if this is the main rollback journal and ** false for the statement journal. The main rollback journal uses ** checksums - the statement journal does not. ** -** If the page number of the page record read from the (sub-)journal file -** is greater than the current value of Pager.dbSize, then playback is -** skipped and SQLITE_OK is returned. -** ** If pDone is not NULL, then it is a record of pages that have already ** been played back. If the page at *pOffset has already been played back ** (if the corresponding pDone bit is set) then skip the playback. ** Make sure the pDone bit corresponding to the *pOffset page is set ** prior to returning. -** -** If the page record is successfully read from the (sub-)journal file -** and played back, then SQLITE_OK is returned. If an IO error occurs -** while reading the record from the (sub-)journal file or while writing -** to the database file, then the IO error code is returned. If data -** is successfully read from the (sub-)journal file but appears to be -** corrupted, SQLITE_DONE is returned. Data is considered corrupted in -** two circumstances: -** -** * If the record page-number is illegal (0 or PAGER_MJ_PGNO), or -** * If the record is being rolled back from the main journal file -** and the checksum field does not match the record content. -** -** Neither of these two scenarios are possible during a savepoint rollback. -** -** If this is a savepoint rollback, then memory may have to be dynamically -** allocated by this function. If this is the case and an allocation fails, -** SQLITE_NOMEM is returned. */ static int pager_playback_one_page( Pager *pPager, /* The pager being played back */ @@ -1414,10 +1190,8 @@ static int pager_playback_one_page( aData = (u8*)pPager->pTmpSpace; assert( aData ); /* Temp storage must have already been allocated */ - /* Read the page number and page data from the journal or sub-journal - ** file. Return an error code to the caller if an IO error occurs. - */ jfd = isMainJrnl ? pPager->jfd : pPager->sjfd; + rc = read32bits(jfd, *pOffset, &pgno); if( rc!=SQLITE_OK ) return rc; rc = sqlite3OsRead(jfd, aData, pPager->pageSize, (*pOffset)+4); @@ -1430,7 +1204,6 @@ static int pager_playback_one_page( ** detect this invalid data (with high probability) and ignore it. */ if( pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){ - assert( !isSavepnt ); return SQLITE_DONE; } if( pgno>(Pgno)pPager->dbSize || sqlite3BitvecTest(pDone, pgno) ){ @@ -1443,7 +1216,6 @@ static int pager_playback_one_page( return SQLITE_DONE; } } - if( pDone && (rc = sqlite3BitvecSet(pDone, pgno)) ){ return rc; } @@ -1489,7 +1261,7 @@ static int pager_playback_one_page( )); if( (pPager->state>=PAGER_EXCLUSIVE) && (pPg==0 || 0==(pPg->flags&PGHDR_NEED_SYNC)) - && isOpen(pPager->fd) + && (pPager->fd->pMethods) ){ i64 ofst = (pgno-1)*(i64)pPager->pageSize; rc = sqlite3OsWrite(pPager->fd, aData, pPager->pageSize, ofst); @@ -1533,7 +1305,7 @@ static int pager_playback_one_page( if( pPager->xReiniter ){ pPager->xReiniter(pPg); } - if( isMainJrnl && (!isSavepnt || *pOffset<=pPager->journalHdr) ){ + if( isMainJrnl && (!isSavepnt || pPager->journalOff<=pPager->journalHdr) ){ /* If the contents of this page were just restored from the main ** journal file, then its content must be as they were when the ** transaction was first opened. In this case we can mark the page @@ -1619,45 +1391,18 @@ static int pagerNextJournalPageIsValid(Pager *pPager){ ** Argument zMaster may point to Pager.pTmpSpace. So that buffer is not ** available for use within this function. ** -** When a master journal file is created, it is populated with the names -** of all of its child journals, one after another, formatted as utf-8 -** encoded text. The end of each child journal file is marked with a -** nul-terminator byte (0x00). i.e. the entire contents of a master journal -** file for a transaction involving two databases might be: -** -** "/home/bill/a.db-journal\x00/home/bill/b.db-journal\x00" -** -** A master journal file may only be deleted once all of its child -** journals have been rolled back. -** -** This function reads the contents of the master-journal file into -** memory and loops through each of the child journal names. For -** each child journal, it checks if: ** -** * if the child journal exists, and if so -** * if the child journal contains a reference to master journal -** file zMaster -** -** If a child journal can be found that matches both of the criteria -** above, this function returns without doing anything. Otherwise, if -** no such child journal can be found, file zMaster is deleted from -** the file-system using sqlite3OsDelete(). -** -** If an IO error within this function, an error code is returned. This -** function allocates memory by calling sqlite3Malloc(). If an allocation -** fails, SQLITE_NOMEM is returned. Otherwise, if no IO or malloc errors -** occur, SQLITE_OK is returned. -** -** TODO: This function allocates a single block of memory to load -** the entire contents of the master journal file. This could be -** a couple of kilobytes or so - potentially larger than the page -** size. +** The master journal file contains the names of all child journals. +** To tell if a master journal can be deleted, check to each of the +** children. If all children are either missing or do not refer to +** a different master journal, then this master journal can be deleted. */ static int pager_delmaster(Pager *pPager, const char *zMaster){ sqlite3_vfs *pVfs = pPager->pVfs; - int rc; /* Return code */ - sqlite3_file *pMaster; /* Malloc'd master-journal file descriptor */ - sqlite3_file *pJournal; /* Malloc'd child-journal file descriptor */ + int rc; + int master_open = 0; + sqlite3_file *pMaster; + sqlite3_file *pJournal; char *zMasterJournal = 0; /* Contents of master journal file */ i64 nMasterJournal; /* Size of master journal file */ @@ -1673,6 +1418,7 @@ static int pager_delmaster(Pager *pPager, const char *zMaster){ rc = sqlite3OsOpen(pVfs, zMaster, pMaster, flags, 0); } if( rc!=SQLITE_OK ) goto delmaster_out; + master_open = 1; rc = sqlite3OsFileSize(pMaster, &nMasterJournal); if( rc!=SQLITE_OK ) goto delmaster_out; @@ -1680,7 +1426,7 @@ static int pager_delmaster(Pager *pPager, const char *zMaster){ if( nMasterJournal>0 ){ char *zJournal; char *zMasterPtr = 0; - int nMasterPtr = pVfs->mxPathname+1; + int nMasterPtr = pPager->pVfs->mxPathname+1; /* Load the entire master journal file into space obtained from ** sqlite3_malloc() and pointed to by zMasterJournal. @@ -1735,9 +1481,8 @@ delmaster_out: if( zMasterJournal ){ sqlite3_free(zMasterJournal); } - if( pMaster ){ + if( master_open ){ sqlite3OsClose(pMaster); - assert( !isOpen(pJournal) ); } sqlite3_free(pMaster); return rc; @@ -1745,30 +1490,23 @@ delmaster_out: /* -** This function is used to change the actual size of the database -** file in the file-system. This only happens when committing a transaction, -** or rolling back a transaction (including rolling back a hot-journal). -** -** If the main database file is not open, or an exclusive lock is not -** held, this function is a no-op. Otherwise, the size of the file is -** changed to nPage pages (nPage*pPager->pageSize bytes). If the file -** on disk is currently larger than nPage pages, then use the VFS -** xTruncate() method to truncate it. -** -** Or, it might might be the case that the file on disk is smaller than -** nPage pages. Some operating system implementations can get confused if -** you try to truncate a file to some size that is larger than it -** currently is, so detect this case and write a single zero byte to -** the end of the new file instead. -** -** If successful, return SQLITE_OK. If an IO error occurs while modifying -** the database file, return the error code to the caller. +** If the main database file is open and an exclusive lock is held, +** truncate the main file of the given pager to the specified number +** of pages. +** +** It might might be the case that the file on disk is smaller than nPage. +** This can happen, for example, if we are in the middle of a transaction +** which has extended the file size and the new pages are still all held +** in cache, then an INSERT or UPDATE does a statement rollback. Some +** operating system implementations can get confused if you try to +** truncate a file to some size that is larger than it currently is, +** so detect this case and write a single zero byte to the end of the new +** file instead. */ static int pager_truncate(Pager *pPager, Pgno nPage){ int rc = SQLITE_OK; - if( pPager->state>=PAGER_EXCLUSIVE && isOpen(pPager->fd) ){ + if( pPager->state>=PAGER_EXCLUSIVE && pPager->fd->pMethods ){ i64 currentSize, newSize; - /* TODO: Is it safe to use Pager.dbFileSize here? */ rc = sqlite3OsFileSize(pPager->fd, ¤tSize); newSize = pPager->pageSize*(i64)nPage; if( rc==SQLITE_OK && currentSize!=newSize ){ @@ -1786,25 +1524,16 @@ static int pager_truncate(Pager *pPager, Pgno nPage){ } /* -** Set the value of the Pager.sectorSize variable for the given -** pager based on the value returned by the xSectorSize method -** of the open database file. The sector size will be used used -** to determine the size and alignment of journal header and -** master journal pointers within created journal files. -** -** For temporary files the effective sector size is always 512 bytes. +** Set the sectorSize for the given pager. ** -** Otherwise, for non-temporary files, the effective sector size is -** the value returned by the xSectorSize() method rounded up to 512 if -** it is less than 512, or rounded down to MAX_SECTOR_SIZE if it -** is greater than MAX_SECTOR_SIZE. +** The sector size is at least as big as the sector size reported +** by sqlite3OsSectorSize(). The minimum sector size is 512. */ static void setSectorSize(Pager *pPager){ - assert( isOpen(pPager->fd) || pPager->tempFile ); - + assert(pPager->fd->pMethods||pPager->tempFile); if( !pPager->tempFile ){ /* Sector size doesn't matter for temporary files. Also, the file - ** may not have been opened yet, in which case the OsSectorSize() + ** may not have been opened yet, in whcih case the OsSectorSize() ** call will segfault. */ pPager->sectorSize = sqlite3OsSectorSize(pPager->fd); @@ -1813,7 +1542,6 @@ static void setSectorSize(Pager *pPager){ pPager->sectorSize = 512; } if( pPager->sectorSize>MAX_SECTOR_SIZE ){ - assert( MAX_SECTOR_SIZE>=512 ); pPager->sectorSize = MAX_SECTOR_SIZE; } } @@ -1887,7 +1615,7 @@ static int pager_playback(Pager *pPager, int isHot){ /* Figure out how many records are in the journal. Abort early if ** the journal is empty. */ - assert( isOpen(pPager->jfd) ); + assert( pPager->journalOpen ); rc = sqlite3OsFileSize(pPager->jfd, &szJ); if( rc!=SQLITE_OK || szJ==0 ){ goto end_playback; @@ -1897,12 +1625,6 @@ static int pager_playback(Pager *pPager, int isHot){ ** If a master journal file name is specified, but the file is not ** present on disk, then the journal is not hot and does not need to be ** played back. - ** - ** TODO: Technically the following is an error because it assumes that - ** buffer Pager.pTmpSpace is (mxPathname+1) bytes or larger. i.e. that - ** (pPager->pageSize >= pPager->pVfs->mxPathname+1). Using os_unix.c, - ** mxPathname is 512, which is the same as the minimum allowable value - ** for pageSize. */ zMaster = pPager->pTmpSpace; rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1); @@ -1915,10 +1637,8 @@ static int pager_playback(Pager *pPager, int isHot){ } pPager->journalOff = 0; - /* This loop terminates either when a readJournalHdr() or - ** pager_playback_one_page() call returns SQLITE_DONE or an IO error - ** occurs. - */ + /* This loop terminates either when the readJournalHdr() call returns + ** SQLITE_DONE or an IO error occurs. */ while( 1 ){ /* Read the next journal header from the journal file. If there are @@ -1979,8 +1699,7 @@ static int pager_playback(Pager *pPager, int isHot){ pPager->dbSize = mxPg; } - /* Copy original pages out of the journal and back into the - ** database file and/or page cache. + /* Copy original pages out of the journal and back into the database file. */ for(u=0; u<nRec; u++){ rc = pager_playback_one_page(pPager, 1, &pPager->journalOff, 0, 0); @@ -2017,18 +1736,15 @@ end_playback: if( rc==SQLITE_OK ){ zMaster = pPager->pTmpSpace; rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1); - testcase( rc!=SQLITE_OK ); } if( rc==SQLITE_OK ){ rc = pager_end_transaction(pPager, zMaster[0]!='\0'); - testcase( rc!=SQLITE_OK ); } if( rc==SQLITE_OK && zMaster[0] && res ){ /* If there was a master journal and this routine will return success, ** see if it is possible to delete the master journal. */ rc = pager_delmaster(pPager, zMaster); - testcase( rc!=SQLITE_OK ); } /* The Pager.sectorSize variable may have been updated while rolling @@ -2040,49 +1756,19 @@ end_playback: } /* -** Playback savepoint pSavepoint. Or, if pSavepoint==NULL, then playback -** the entire master journal file. The case pSavepoint==NULL occurs when -** a ROLLBACK TO command is invoked on a SAVEPOINT that is a transaction -** savepoint. -** -** When pSavepoint is not NULL (meaning a non-transaction savepoint is -** being rolled back), then the rollback consists of up to three stages, -** performed in the order specified: +** Playback savepoint pSavepoint. Or, if pSavepoint==NULL, then playback +** the entire master journal file. ** -** * Pages are played back from the main journal starting at byte -** offset PagerSavepoint.iOffset and continuing to -** PagerSavepoint.iHdrOffset, or to the end of the main journal -** file if PagerSavepoint.iHdrOffset is zero. -** -** * If PagerSavepoint.iHdrOffset is not zero, then pages are played -** back starting from the journal header immediately following -** PagerSavepoint.iHdrOffset to the end of the main journal file. -** -** * Pages are then played back from the sub-journal file, starting -** with the PagerSavepoint.iSubRec and continuing to the end of -** the journal file. -** -** Throughout the rollback process, each time a page is rolled back, the -** corresponding bit is set in a bitvec structure (variable pDone in the -** implementation below). This is used to ensure that a page is only -** rolled back the first time it is encountered in either journal. -** -** If pSavepoint is NULL, then pages are only played back from the main -** journal file. There is no need for a bitvec in this case. -** -** In either case, before playback commences the Pager.dbSize variable -** is reset to the value that it held at the start of the savepoint -** (or transaction). No page with a page-number greater than this value -** is played back. If one is encountered it is simply skipped. +** The case pSavepoint==NULL occurs when a ROLLBACK TO command is invoked +** on a SAVEPOINT that is a transaction savepoint. */ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){ i64 szJ; /* Effective size of the main journal */ i64 iHdrOff; /* End of first segment of main-journal records */ + Pgno ii; /* Loop counter */ int rc = SQLITE_OK; /* Return code */ Bitvec *pDone = 0; /* Bitvec to ensure pages played back only once */ - assert( pPager->state>=PAGER_SHARED ); - /* Allocate a bitvec to use to store the set of pages rolled back */ if( pSavepoint ){ pDone = sqlite3BitvecCreate(pSavepoint->nOrig); @@ -2091,10 +1777,11 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){ } } - /* Set the database size back to the value it was before the savepoint - ** being reverted was opened. + /* Truncate the database back to the size it was before the + ** savepoint being reverted was opened. */ pPager->dbSize = pSavepoint ? pSavepoint->nOrig : pPager->dbOrigSize; + assert( pPager->state>=PAGER_SHARED ); /* Use pPager->journalOff as the effective size of the main rollback ** journal. The actual file might be larger than this in @@ -2115,8 +1802,8 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){ pPager->journalOff = pSavepoint->iOffset; while( rc==SQLITE_OK && pPager->journalOff<iHdrOff ){ rc = pager_playback_one_page(pPager, 1, &pPager->journalOff, 1, pDone); + assert( rc!=SQLITE_DONE ); } - assert( rc!=SQLITE_DONE ); }else{ pPager->journalOff = 0; } @@ -2127,7 +1814,6 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){ ** continue adding pages rolled back to pDone. */ while( rc==SQLITE_OK && pPager->journalOff<szJ ){ - u32 ii; /* Loop counter */ u32 nJRec = 0; /* Number of Journal Records */ u32 dummy; rc = readJournalHdr(pPager, szJ, &nJRec, &dummy); @@ -2150,8 +1836,8 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){ } for(ii=0; rc==SQLITE_OK && ii<nJRec && pPager->journalOff<szJ; ii++){ rc = pager_playback_one_page(pPager, 1, &pPager->journalOff, 1, pDone); + assert( rc!=SQLITE_DONE ); } - assert( rc!=SQLITE_DONE ); } assert( rc!=SQLITE_OK || pPager->journalOff==szJ ); @@ -2160,13 +1846,12 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){ ** will be skipped. Out-of-range pages are also skipped. */ if( pSavepoint ){ - u32 ii; /* Loop counter */ i64 offset = pSavepoint->iSubRec*(4+pPager->pageSize); - for(ii=pSavepoint->iSubRec; rc==SQLITE_OK && ii<pPager->nSubRec; ii++){ - assert( offset==ii*(4+pPager->pageSize) ); + for(ii=pSavepoint->iSubRec; rc==SQLITE_OK&&ii<(u32)pPager->stmtNRec; ii++){ + assert( offset == ii*(4+pPager->pageSize) ); rc = pager_playback_one_page(pPager, 0, &offset, 1, pDone); + assert( rc!=SQLITE_DONE ); } - assert( rc!=SQLITE_DONE ); } sqlite3BitvecDestroy(pDone); @@ -2228,26 +1913,18 @@ int sqlite3_opentemp_count = 0; #endif /* -** Open a temporary file. -** -** Write the file descriptor into *pFile. Return SQLITE_OK on success -** or some other error code if we fail. The OS will automatically -** delete the temporary file when it is closed. +** Open a temporary file. ** -** The flags passed to the VFS layer xOpen() call are those specified -** by parameter vfsFlags ORed with the following: -** -** SQLITE_OPEN_READWRITE -** SQLITE_OPEN_CREATE -** SQLITE_OPEN_EXCLUSIVE -** SQLITE_OPEN_DELETEONCLOSE +** Write the file descriptor into *fd. Return SQLITE_OK on success or some +** other error code if we fail. The OS will automatically delete the temporary +** file when it is closed. */ -static int pagerOpentemp( +static int sqlite3PagerOpentemp( Pager *pPager, /* The pager object */ sqlite3_file *pFile, /* Write the file descriptor here */ int vfsFlags /* Flags passed through to the VFS */ ){ - int rc; /* Return code */ + int rc; #ifdef SQLITE_TEST sqlite3_opentemp_count++; /* Used for testing and analysis only */ @@ -2256,79 +1933,56 @@ static int pagerOpentemp( vfsFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE; rc = sqlite3OsOpen(pPager->pVfs, 0, pFile, vfsFlags, 0); - assert( rc!=SQLITE_OK || isOpen(pFile) ); + assert( rc!=SQLITE_OK || pFile->pMethods ); return rc; } static int pagerStress(void *,PgHdr *); /* -** Allocate and initialize a new Pager object and put a pointer to it -** in *ppPager. The pager should eventually be freed by passing it -** to sqlite3PagerClose(). +** Create a new page cache and put a pointer to the page cache in *ppPager. +** The file to be cached need not exist. The file is not locked until +** the first call to sqlite3PagerGet() and is only held open until the +** last page is released using sqlite3PagerUnref(). ** -** The zFilename argument is the path to the database file to open. ** If zFilename is NULL then a randomly-named temporary file is created -** and used as the file to be cached. Temporary files are be deleted -** automatically when they are closed. If zFilename is ":memory:" then -** all information is held in cache. It is never written to disk. -** This can be used to implement an in-memory database. -** -** The nExtra parameter specifies the number of bytes of space allocated -** along with each page reference. This space is available to the user -** via the sqlite3PagerGetExtra() API. -** -** The flags argument is used to specify properties that affect the -** operation of the pager. It should be passed some bitwise combination -** of the PAGER_OMIT_JOURNAL and PAGER_NO_READLOCK flags. +** and used as the file to be cached. The file will be deleted +** automatically when it is closed. ** -** The vfsFlags parameter is a bitmask to pass to the flags parameter -** of the xOpen() method of the supplied VFS when opening files. -** -** If the pager object is allocated and the specified file opened -** successfully, SQLITE_OK is returned and *ppPager set to point to -** the new pager object. If an error occurs, *ppPager is set to NULL -** and error code returned. This function may return SQLITE_NOMEM -** (sqlite3Malloc() is used to allocate memory), SQLITE_CANTOPEN or -** various SQLITE_IO_XXX errors. +** If zFilename is ":memory:" then all information is held in cache. +** It is never written to disk. This can be used to implement an +** in-memory database. */ int sqlite3PagerOpen( sqlite3_vfs *pVfs, /* The virtual file system to use */ - Pager **ppPager, /* OUT: Return the Pager structure here */ + Pager **ppPager, /* Return the Pager structure here */ const char *zFilename, /* Name of the database file to open */ int nExtra, /* Extra bytes append to each in-memory page */ int flags, /* flags controlling this file */ int vfsFlags /* flags passed through to sqlite3_vfs.xOpen() */ ){ u8 *pPtr; - Pager *pPager = 0; /* Pager object to allocate and return */ - int rc = SQLITE_OK; /* Return code */ - int tempFile = 0; /* True for temp files (incl. in-memory files) */ - int memDb = 0; /* True if this is an in-memory file */ - int readOnly = 0; /* True if this is a read-only file */ - int journalFileSize; /* Bytes to allocate for each journal fd */ - char *zPathname = 0; /* Full path to database file */ - int nPathname = 0; /* Number of bytes in zPathname */ - int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; /* False to omit journal */ - int noReadlock = (flags & PAGER_NO_READLOCK)!=0; /* True to omit read-lock */ - int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */ - u16 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */ - - /* Figure out how much space is required for each journal file-handle - ** (there are two of them, the main journal and the sub-journal). This - ** is the maximum space required for an in-memory journal file handle - ** and a regular journal file-handle. Note that a "regular journal-handle" - ** may be a wrapper capable of caching the first portion of the journal - ** file in memory to implement the atomic-write optimization (see - ** source file journal.c). - */ + Pager *pPager = 0; + int rc = SQLITE_OK; + int i; + int tempFile = 0; + int memDb = 0; + int readOnly = 0; + int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; + int noReadlock = (flags & PAGER_NO_READLOCK)!=0; + int journalFileSize; + int pcacheSize = sqlite3PcacheSize(); + int szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; + char *zPathname = 0; + int nPathname = 0; + if( sqlite3JournalSize(pVfs)>sqlite3MemJournalSize() ){ journalFileSize = sqlite3JournalSize(pVfs); }else{ journalFileSize = sqlite3MemJournalSize(); } - /* Set the output variable to NULL in case an error occurs. */ + /* The default return is a NULL pointer */ *ppPager = 0; /* Compute and store the full pathname in an allocated buffer pointed @@ -2350,101 +2004,80 @@ int sqlite3PagerOpen( { rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname); } - - nPathname = sqlite3Strlen30(zPathname); - if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){ - /* This branch is taken when the journal path required by - ** the database being opened will be more than pVfs->mxPathname - ** bytes in length. This means the database cannot be opened, - ** as it will not be possible to open the journal file or even - ** check for a hot-journal before reading. - */ - rc = SQLITE_CANTOPEN; - } if( rc!=SQLITE_OK ){ sqlite3_free(zPathname); return rc; } + nPathname = sqlite3Strlen30(zPathname); } - /* Allocate memory for the Pager structure, PCache object, the - ** three file descriptors, the database file name and the journal - ** file name. The layout in memory is as follows: - ** - ** Pager object (sizeof(Pager) bytes) - ** PCache object (sqlite3PcacheSize() bytes) - ** Database file handle (pVfs->szOsFile bytes) - ** Sub-journal file handle (journalFileSize bytes) - ** Main journal file handle (journalFileSize bytes) - ** Database file name (nPathname+1 bytes) - ** Journal file name (nPathname+8+1 bytes) - */ - pPtr = (u8 *)sqlite3MallocZero( + /* Allocate memory for the pager structure */ + pPager = sqlite3MallocZero( sizeof(*pPager) + /* Pager structure */ pcacheSize + /* PCache object */ + journalFileSize + /* The journal file structure */ pVfs->szOsFile + /* The main db file */ journalFileSize * 2 + /* The two journal files */ - nPathname + 1 + /* zFilename */ - nPathname + 8 + 1 /* zJournal */ + 3*nPathname + 40 /* zFilename, zDirectory, zJournal */ ); - if( !pPtr ){ + if( !pPager ){ sqlite3_free(zPathname); return SQLITE_NOMEM; } - pPager = (Pager*)(pPtr); - pPager->pPCache = (PCache*)(pPtr += sizeof(*pPager)); - pPager->fd = (sqlite3_file*)(pPtr += pcacheSize); - pPager->sjfd = (sqlite3_file*)(pPtr += pVfs->szOsFile); - pPager->jfd = (sqlite3_file*)(pPtr += journalFileSize); - pPager->zFilename = (char*)(pPtr += journalFileSize); - - /* Fill in the Pager.zFilename and Pager.zJournal buffers, if required. */ + pPager->pPCache = (PCache *)&pPager[1]; + pPtr = ((u8 *)&pPager[1]) + pcacheSize; + pPager->vfsFlags = vfsFlags; + pPager->fd = (sqlite3_file*)&pPtr[pVfs->szOsFile*0]; + pPager->sjfd = (sqlite3_file*)&pPtr[pVfs->szOsFile]; + pPager->jfd = (sqlite3_file*)&pPtr[pVfs->szOsFile+journalFileSize]; + pPager->zFilename = (char*)&pPtr[pVfs->szOsFile+2*journalFileSize]; + pPager->zDirectory = &pPager->zFilename[nPathname+1]; + pPager->zJournal = &pPager->zDirectory[nPathname+1]; + pPager->pVfs = pVfs; if( zPathname ){ - pPager->zJournal = (char*)(pPtr += nPathname + 1); - memcpy(pPager->zFilename, zPathname, nPathname); - memcpy(pPager->zJournal, zPathname, nPathname); - memcpy(&pPager->zJournal[nPathname], "-journal", 8); + memcpy(pPager->zFilename, zPathname, nPathname+1); sqlite3_free(zPathname); } - pPager->pVfs = pVfs; - pPager->vfsFlags = vfsFlags; /* Open the pager file. */ if( zFilename && zFilename[0] && !memDb ){ - int fout = 0; /* VFS flags returned by xOpen() */ - rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, vfsFlags, &fout); - readOnly = (fout&SQLITE_OPEN_READONLY); - - /* If the file was successfully opened for read/write access, - ** choose a default page size in case we have to create the - ** database file. The default page size is the maximum of: - ** - ** + SQLITE_DEFAULT_PAGE_SIZE, - ** + The value returned by sqlite3OsSectorSize() - ** + The largest page size that can be written atomically. - */ - if( rc==SQLITE_OK && !readOnly ){ - setSectorSize(pPager); - if( szPageDflt<pPager->sectorSize ){ - szPageDflt = pPager->sectorSize; - } + if( nPathname>(pVfs->mxPathname - (int)sizeof("-journal")) ){ + rc = SQLITE_CANTOPEN; + }else{ + int fout = 0; + rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, + pPager->vfsFlags, &fout); + readOnly = (fout&SQLITE_OPEN_READONLY); + + /* If the file was successfully opened for read/write access, + ** choose a default page size in case we have to create the + ** database file. The default page size is the maximum of: + ** + ** + SQLITE_DEFAULT_PAGE_SIZE, + ** + The value returned by sqlite3OsSectorSize() + ** + The largest page size that can be written atomically. + */ + if( rc==SQLITE_OK && !readOnly ){ + setSectorSize(pPager); + if( szPageDflt<pPager->sectorSize ){ + szPageDflt = pPager->sectorSize; + } #ifdef SQLITE_ENABLE_ATOMIC_WRITE - { - int iDc = sqlite3OsDeviceCharacteristics(pPager->fd); - int ii; - assert(SQLITE_IOCAP_ATOMIC512==(512>>8)); - assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8)); - assert(SQLITE_MAX_DEFAULT_PAGE_SIZE<=65536); - for(ii=szPageDflt; ii<=SQLITE_MAX_DEFAULT_PAGE_SIZE; ii=ii*2){ - if( iDc&(SQLITE_IOCAP_ATOMIC|(ii>>8)) ){ - szPageDflt = ii; + { + int iDc = sqlite3OsDeviceCharacteristics(pPager->fd); + int ii; + assert(SQLITE_IOCAP_ATOMIC512==(512>>8)); + assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8)); + assert(SQLITE_MAX_DEFAULT_PAGE_SIZE<=65536); + for(ii=szPageDflt; ii<=SQLITE_MAX_DEFAULT_PAGE_SIZE; ii=ii*2){ + if( iDc&(SQLITE_IOCAP_ATOMIC|(ii>>8)) ) szPageDflt = ii; } } - } #endif - if( szPageDflt>SQLITE_MAX_DEFAULT_PAGE_SIZE ){ - szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE; + if( szPageDflt>SQLITE_MAX_DEFAULT_PAGE_SIZE ){ + szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE; + } } } }else{ @@ -2460,26 +2093,20 @@ int sqlite3PagerOpen( pPager->state = PAGER_EXCLUSIVE; } - /* The following call to PagerSetPagesize() serves to set the value of - ** Pager.pageSize and to allocate the Pager.pTmpSpace buffer. - */ - if( rc==SQLITE_OK ){ - assert( pPager->memDb==0 ); - rc = sqlite3PagerSetPagesize(pPager, &szPageDflt); - testcase( rc!=SQLITE_OK ); + if( pPager && rc==SQLITE_OK ){ + pPager->pTmpSpace = sqlite3PageMalloc(szPageDflt); } - /* If an error occured in either of the blocks above, free the - ** Pager structure and close the file. + /* If an error occured in either of the blocks above. + ** Free the Pager structure and close the file. + ** Since the pager is not allocated there is no need to set + ** any Pager.errMask variables. */ - if( rc!=SQLITE_OK ){ - assert( !pPager->pTmpSpace ); + if( !pPager || !pPager->pTmpSpace ){ sqlite3OsClose(pPager->fd); sqlite3_free(pPager); - return rc; + return ((rc==SQLITE_OK)?SQLITE_NOMEM:rc); } - - /* Initialize the PCache object. */ nExtra = FORCE_ALIGNMENT(nExtra); sqlite3PcacheOpen(szPageDflt, nExtra, !memDb, !memDb?pagerStress:0, (void *)pPager, pPager->pPCache); @@ -2487,15 +2114,32 @@ int sqlite3PagerOpen( PAGERTRACE(("OPEN %d %s\n", FILEHANDLEID(pPager->fd), pPager->zFilename)); IOTRACE(("OPEN %p %s\n", pPager, pPager->zFilename)) + /* Fill in Pager.zDirectory[] */ + memcpy(pPager->zDirectory, pPager->zFilename, nPathname+1); + for(i=sqlite3Strlen30(pPager->zDirectory); + i>0 && pPager->zDirectory[i-1]!='/'; i--){} + if( i>0 ) pPager->zDirectory[i-1] = 0; + + /* Fill in Pager.zJournal[] */ + if( zPathname ){ + memcpy(pPager->zJournal, pPager->zFilename, nPathname); + memcpy(&pPager->zJournal[nPathname], "-journal", 9); + }else{ + pPager->zJournal = 0; + } + + /* pPager->journalOpen = 0; */ pPager->useJournal = (u8)useJournal; pPager->noReadlock = (noReadlock && readOnly) ?1:0; /* pPager->stmtOpen = 0; */ /* pPager->stmtInUse = 0; */ /* pPager->nRef = 0; */ pPager->dbSizeValid = (u8)memDb; + pPager->pageSize = szPageDflt; /* pPager->stmtSize = 0; */ /* pPager->stmtJSize = 0; */ /* pPager->nPage = 0; */ + pPager->mxPage = 100; pPager->mxPgno = SQLITE_MAX_PAGE_COUNT; /* pPager->state = PAGER_UNLOCK; */ assert( pPager->state == (tempFile ? PAGER_EXCLUSIVE : PAGER_UNLOCK) ); @@ -2516,7 +2160,7 @@ int sqlite3PagerOpen( /* pPager->pLast = 0; */ pPager->nExtra = nExtra; pPager->journalSizeLimit = SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT; - assert( isOpen(pPager->fd) || tempFile ); + assert(pPager->fd->pMethods||tempFile); setSectorSize(pPager); if( memDb ){ pPager->journalMode = PAGER_JOURNALMODE_MEMORY; @@ -2530,74 +2174,31 @@ int sqlite3PagerOpen( /* ** Set the busy handler function. -** -** The pager invokes the busy-handler if sqlite3OsLock() returns -** SQLITE_BUSY when trying to upgrade from no-lock to a SHARED lock, -** or when trying to upgrade from a RESERVED lock to an EXCLUSIVE -** lock. It does *not* invoke the busy handler when upgrading from -** SHARED to RESERVED, or when upgrading from SHARED to EXCLUSIVE -** (which occurs during hot-journal rollback). Summary: -** -** Transition | Invokes xBusyHandler -** -------------------------------------------------------- -** NO_LOCK -> SHARED_LOCK | Yes -** SHARED_LOCK -> RESERVED_LOCK | No -** SHARED_LOCK -> EXCLUSIVE_LOCK | No -** RESERVED_LOCK -> EXCLUSIVE_LOCK | Yes -** -** If the busy-handler callback returns non-zero, the lock is -** retried. If it returns zero, then the SQLITE_BUSY error is -** returned to the caller of the pager API function. */ void sqlite3PagerSetBusyhandler( - Pager *pPager, /* Pager object */ - int (*xBusyHandler)(void *), /* Pointer to busy-handler function */ - void *pBusyHandlerArg /* Argument to pass to xBusyHandler */ + Pager *pPager, + int (*xBusyHandler)(void *), + void *pBusyHandlerArg ){ pPager->xBusyHandler = xBusyHandler; pPager->pBusyHandlerArg = pBusyHandlerArg; } /* -** Set the reinitializer for this pager. If not NULL, the reinitializer -** is called when the content of a page in cache is modified (restored) -** as part of a transaction or savepoint rollback. The callback gives -** higher-level code an opportunity to restore the EXTRA section to -** agree with the restored page data. +** Set the reinitializer for this pager. If not NULL, the reinitializer +** is called when the content of a page in cache is restored to its original +** value as a result of a rollback. The callback gives higher-level code +** an opportunity to restore the EXTRA section to agree with the restored +** page data. */ void sqlite3PagerSetReiniter(Pager *pPager, void (*xReinit)(DbPage*)){ pPager->xReiniter = xReinit; } /* -** Change the page size used by the Pager object. The new page size -** is passed in *pPageSize. -** -** If the pager is in the error state when this function is called, it -** is a no-op. The value returned is the error state error code (i.e. -** one of SQLITE_IOERR, SQLITE_CORRUPT or SQLITE_FULL). -** -** Otherwise, if all of the following are true: -** -** * the new page size (value of *pPageSize) is valid (a power -** of two between 512 and SQLITE_MAX_PAGE_SIZE, inclusive), and -** -** * there are no outstanding page references, and -** -** * the database is either not an in-memory database or it is -** an in-memory database that currently consists of zero pages. -** -** then the pager object page size is set to *pPageSize. -** -** If the page size is changed, then this function uses sqlite3PagerMalloc() -** to obtain a new Pager.pTmpSpace buffer. If this allocation attempt -** fails, SQLITE_NOMEM is returned and the page size remains unchanged. -** In all other cases, SQLITE_OK is returned. -** -** If the page size is not changed, either because one of the enumerated -** conditions above is not true, the pager was in error state when this -** function was called, or because the memory allocation attempt failed, -** then *pPageSize is set to the old, retained page size before returning. +** Set the page size to *pPageSize. If the suggest new page size is +** inappropriate, then an alternative page size is set to that +** value before returning. */ int sqlite3PagerSetPagesize(Pager *pPager, u16 *pPageSize){ int rc = pPager->errCode; @@ -2614,6 +2215,7 @@ int sqlite3PagerSetPagesize(Pager *pPager, u16 *pPageSize){ }else{ pager_reset(pPager); pPager->pageSize = pageSize; + if( !pPager->memDb ) setSectorSize(pPager); sqlite3PageFree(pPager->pTmpSpace); pPager->pTmpSpace = pNew; sqlite3PcacheSetPageSize(pPager->pPCache, pageSize); @@ -2679,21 +2281,17 @@ void enable_simulated_io_errors(void){ ** Read the first N bytes from the beginning of the file into memory ** that pDest points to. ** -** If the pager was opened on a transient file (zFilename==""), or -** opened on a file less than N bytes in size, the output buffer is -** zeroed and SQLITE_OK returned. The rationale for this is that this -** function is used to read database headers, and a new transient or -** zero sized database has a header than consists entirely of zeroes. -** -** If any IO error apart from SQLITE_IOERR_SHORT_READ is encountered, -** the error code is returned to the caller and the contents of the -** output buffer undefined. +** No error checking is done. The rational for this is that this function +** may be called even if the file does not exist or contain a header. In +** these cases sqlite3OsRead() will return an error, to which the correct +** response is to zero the memory at pDest and continue. A real IO error +** will presumably recur and be picked up later (Todo: Think about this). */ int sqlite3PagerReadFileheader(Pager *pPager, int N, unsigned char *pDest){ int rc = SQLITE_OK; memset(pDest, 0, N); - assert( isOpen(pPager->fd) || pPager->tempFile ); - if( isOpen(pPager->fd) ){ + assert(pPager->fd->pMethods||pPager->tempFile); + if( pPager->fd->pMethods ){ IOTRACE(("DBHDR %p 0 %d\n", pPager, N)) rc = sqlite3OsRead(pPager->fd, pDest, N, 0); if( rc==SQLITE_IOERR_SHORT_READ ){ @@ -2704,88 +2302,69 @@ int sqlite3PagerReadFileheader(Pager *pPager, int N, unsigned char *pDest){ } /* -** Return the total number of pages in the database file associated -** with pPager. Normally, this is calculated as (<db file size>/<page-size>). -** However, if the file is between 1 and <page-size> bytes in size, then -** this is considered a 1 page file. +** Return the total number of pages in the disk file associated with +** pPager. ** -** If the pager is in error state when this function is called, then the -** error state error code is returned and *pnPage left unchanged. Or, -** if the file system has to be queried for the size of the file and -** the query attempt returns an IO error, the IO error code is returned -** and *pnPage is left unchanged. -** -** Otherwise, if everything is successful, then SQLITE_OK is returned -** and *pnPage is set to the number of pages in the database. +** If the PENDING_BYTE lies on the page directly after the end of the +** file, then consider this page part of the file too. For example, if +** PENDING_BYTE is byte 4096 (the first byte of page 5) and the size of the +** file is 4096 bytes, 5 is returned instead of 4. */ int sqlite3PagerPagecount(Pager *pPager, int *pnPage){ - Pgno nPage; /* Value to return via *pnPage */ - - /* If the pager is already in the error state, return the error code. */ + i64 n = 0; + int rc; + assert( pPager!=0 ); if( pPager->errCode ){ - return pPager->errCode; + rc = pPager->errCode; + return rc; } - - /* Determine the number of pages in the file. Store this in nPage. */ if( pPager->dbSizeValid ){ - nPage = pPager->dbSize; - }else{ - int rc; /* Error returned by OsFileSize() */ - i64 n = 0; /* File size in bytes returned by OsFileSize() */ - - assert( isOpen(pPager->fd) || pPager->tempFile ); - if( isOpen(pPager->fd) && (rc = sqlite3OsFileSize(pPager->fd, &n)) ){ + n = pPager->dbSize; + } else { + assert(pPager->fd->pMethods||pPager->tempFile); + if( (pPager->fd->pMethods) + && (rc = sqlite3OsFileSize(pPager->fd, &n))!=SQLITE_OK ){ pager_error(pPager, rc); return rc; } if( n>0 && n<pPager->pageSize ){ - nPage = 1; + n = 1; }else{ - nPage = n / pPager->pageSize; + n /= pPager->pageSize; } if( pPager->state!=PAGER_UNLOCK ){ - pPager->dbSize = (Pgno)nPage; - pPager->dbFileSize = (Pgno)nPage; + pPager->dbSize = (Pgno)n; + pPager->dbFileSize = (Pgno)n; pPager->dbSizeValid = 1; } } - - /* If the current number of pages in the file is greater than the - ** configured maximum pager number, increase the allowed limit so - ** that the file can be read. - */ - if( nPage>pPager->mxPgno ){ - pPager->mxPgno = (Pgno)nPage; + if( n==(PENDING_BYTE/pPager->pageSize) ){ + n++; + } + if( n>pPager->mxPgno ){ + pPager->mxPgno = (Pgno)n; } - - /* Set the output variable and return SQLITE_OK */ if( pnPage ){ - *pnPage = nPage; + *pnPage = (int)n; } return SQLITE_OK; } /* -** Forward declaration. +** Forward declaration */ static int syncJournal(Pager*); /* -** Try to obtain a lock of type locktype on the database file. If -** a similar or greater lock is already held, this function is a no-op -** (returning SQLITE_OK immediately). -** -** Otherwise, attempt to obtain the lock using sqlite3OsLock(). Invoke -** the busy callback if the lock is currently not available. Repeat -** until the busy callback returns false or until the attempt to -** obtain the lock succeeds. +** Try to obtain a lock on a file. Invoke the busy callback if the lock +** is currently not available. Repeat until the busy callback returns +** false or until the lock succeeds. ** ** Return SQLITE_OK on success and an error code if we cannot obtain -** the lock. If the lock is obtained successfully, set the Pager.state -** variable to locktype before returning. +** the lock. */ static int pager_wait_on_lock(Pager *pPager, int locktype){ - int rc; /* Return code */ + int rc; /* The OS lock values must be the same as the Pager lock values */ assert( PAGER_SHARED==SHARED_LOCK ); @@ -2795,16 +2374,6 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){ /* If the file is currently unlocked then the size must be unknown */ assert( pPager->state>=PAGER_SHARED || pPager->dbSizeValid==0 ); - /* Check that this is either a no-op (because the requested lock is - ** already held, or one of the transistions that the busy-handler - ** may be invoked during, according to the comment above - ** sqlite3PagerSetBusyhandler(). - */ - assert( (pPager->state>=locktype) - || (pPager->state==PAGER_UNLOCK && locktype==PAGER_SHARED) - || (pPager->state==PAGER_RESERVED && locktype==PAGER_EXCLUSIVE) - ); - if( pPager->state>=locktype ){ rc = SQLITE_OK; }else{ @@ -2829,9 +2398,23 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){ void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){ assert( pPager->dbSizeValid ); assert( pPager->dbSize>=nPage ); - assert( pPager->state>=PAGER_RESERVED ); pPager->dbSize = nPage; } + +/* +** Return the current size of the database file image in pages. This +** function differs from sqlite3PagerPagecount() in two ways: +** +** a) It may only be called when at least one reference to a database +** page is held. This guarantees that the database size is already +** known and a call to sqlite3OsFileSize() is not required. +** +** b) The return value is not adjusted for the locking page. +*/ +Pgno sqlite3PagerImageSize(Pager *pPager){ + assert( pPager->dbSizeValid ); + return pPager->dbSize; +} #endif /* ifndef SQLITE_OMIT_AUTOVACUUM */ /* @@ -2849,14 +2432,13 @@ void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){ ** to the caller. */ int sqlite3PagerClose(Pager *pPager){ + disable_simulated_io_errors(); sqlite3BeginBenignMalloc(); pPager->errCode = 0; pPager->exclusiveMode = 0; pager_reset(pPager); - if( MEMDB ){ - pager_unlock(pPager); - }else{ + if( !MEMDB ){ /* Set Pager.journalHdr to -1 for the benefit of the pager_playback() ** call which may be made from within pagerUnlockAndRollback(). If it ** is not -1, then the unsynced portion of an open journal file may @@ -2866,89 +2448,87 @@ int sqlite3PagerClose(Pager *pPager){ pPager->journalHdr = -1; pagerUnlockAndRollback(pPager); } - sqlite3EndBenignMalloc(); enable_simulated_io_errors(); + sqlite3EndBenignMalloc(); PAGERTRACE(("CLOSE %d\n", PAGERID(pPager))); IOTRACE(("CLOSE %p\n", pPager)) + if( pPager->journalOpen ){ + sqlite3OsClose(pPager->jfd); + } + sqlite3BitvecDestroy(pPager->pInJournal); + sqlite3BitvecDestroy(pPager->pAlwaysRollback); + releaseAllSavepoint(pPager); sqlite3OsClose(pPager->fd); + /* Temp files are automatically deleted by the OS + ** if( pPager->tempFile ){ + ** sqlite3OsDelete(pPager->zFilename); + ** } + */ + sqlite3PageFree(pPager->pTmpSpace); sqlite3PcacheClose(pPager->pPCache); - - assert( !pPager->aSavepoint && !pPager->pInJournal ); - assert( !isOpen(pPager->jfd) && !isOpen(pPager->sjfd) ); - sqlite3_free(pPager); return SQLITE_OK; } #if !defined(NDEBUG) || defined(SQLITE_TEST) /* -** Return the page number for page pPg. +** Return the page number for the given page data. */ -Pgno sqlite3PagerPagenumber(DbPage *pPg){ - return pPg->pgno; +Pgno sqlite3PagerPagenumber(DbPage *p){ + return p->pgno; } #endif /* -** Increment the reference count for page pPg. +** Increment the reference count for a page. The input pointer is +** a reference to the page data. */ -void sqlite3PagerRef(DbPage *pPg){ +int sqlite3PagerRef(DbPage *pPg){ sqlite3PcacheRef(pPg); + return SQLITE_OK; } /* -** Sync the journal. In other words, make sure all the pages that have +** Sync the journal. In other words, make sure all the pages that have ** been written to the journal have actually reached the surface of the -** disk and can be restored in the event of a hot-journal rollback. -** -** If the Pager.needSync flag is not set, then this function is a -** no-op. Otherwise, the actions required depend on the journal-mode -** and the device characteristics of the the file-system, as follows: -** -** * If the journal file is an in-memory journal file, no action need -** be taken. -** -** * Otherwise, if the device does not support the SAFE_APPEND property, -** then the nRec field of the most recently written journal header -** is updated to contain the number of journal records that have -** been written following it. If the pager is operating in full-sync -** mode, then the journal file is synced before this field is updated. -** -** * If the device does not support the SEQUENTIAL property, then -** journal file is synced. -** -** Or, in pseudo-code: +** disk. It is not safe to modify the original database file until after +** the journal has been synced. If the original database is modified before +** the journal is synced and a power failure occurs, the unsynced journal +** data would be lost and we would be unable to completely rollback the +** database changes. Database corruption would occur. +** +** This routine also updates the nRec field in the header of the journal. +** (See comments on the pager_playback() routine for additional information.) +** If the sync mode is FULL, two syncs will occur. First the whole journal +** is synced, then the nRec field is updated, then a second sync occurs. ** -** if( NOT <in-memory journal> ){ -** if( NOT SAFE_APPEND ){ -** if( <full-sync mode> ) xSync(<journal file>); -** <update nRec field> -** } -** if( NOT SEQUENTIAL ) xSync(<journal file>); -** } +** For temporary databases, we do not care if we are able to rollback +** after a power failure, so no sync occurs. ** -** The Pager.needSync flag is never be set for temporary files, or any -** file operating in no-sync mode (Pager.noSync set to non-zero). +** If the IOCAP_SEQUENTIAL flag is set for the persistent media on which +** the database is stored, then OsSync() is never called on the journal +** file. In this case all that is required is to update the nRec field in +** the journal header. ** -** If successful, this routine clears the PGHDR_NEED_SYNC flag of every -** page currently held in memory before returning SQLITE_OK. If an IO -** error is encountered, then the IO error code is returned to the caller. +** This routine clears the needSync field of every page current held in +** memory. */ static int syncJournal(Pager *pPager){ + int rc = SQLITE_OK; + + /* Sync the journal before modifying the main database + ** (assuming there is a journal and it needs to be synced.) + */ if( pPager->needSync ){ assert( !pPager->tempFile ); if( pPager->journalMode!=PAGER_JOURNALMODE_MEMORY ){ - int rc; /* Return code */ - const int iDc = sqlite3OsDeviceCharacteristics(pPager->fd); - assert( isOpen(pPager->jfd) ); + int iDc = sqlite3OsDeviceCharacteristics(pPager->fd); + assert( pPager->journalOpen ); if( 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){ - /* Variable iNRecOffset is set to the offset in the journal file - ** of the nRec field of the most recently written journal header. - ** This field will be updated following the xSync() operation - ** on the journal file. */ - i64 iNRecOffset = pPager->journalHdr + sizeof(aJournalMagic); + i64 jrnlOff = journalHdrOffset(pPager); + u8 zMagic[8]; /* This block deals with an obscure problem. If the last connection ** that wrote to this database was operating in persistent-journal @@ -2966,18 +2546,11 @@ static int syncJournal(Pager *pPager){ ** To work around this, if the journal file does appear to contain ** a valid header following Pager.journalOff, then write a 0x00 ** byte to the start of it to prevent it from being recognized. - ** - ** Variable iNextHdrOffset is set to the offset at which this - ** problematic header will occur, if it exists. aMagic is used - ** as a temporary buffer to inspect the first couple of bytes of - ** the potential journal header. */ - i64 iNextHdrOffset = journalHdrOffset(pPager); - u8 aMagic[8]; - rc = sqlite3OsRead(pPager->jfd, aMagic, 8, iNextHdrOffset); - if( rc==SQLITE_OK && 0==memcmp(aMagic, aJournalMagic, 8) ){ + rc = sqlite3OsRead(pPager->jfd, zMagic, 8, jrnlOff); + if( rc==SQLITE_OK && 0==memcmp(zMagic, aJournalMagic, 8) ){ static const u8 zerobyte = 0; - rc = sqlite3OsWrite(pPager->jfd, &zerobyte, 1, iNextHdrOffset); + rc = sqlite3OsWrite(pPager->jfd, &zerobyte, 1, jrnlOff); } if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){ return rc; @@ -2998,11 +2571,13 @@ static int syncJournal(Pager *pPager){ PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager))); IOTRACE(("JSYNC %p\n", pPager)) rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags); - if( rc!=SQLITE_OK ) return rc; + if( rc!=0 ) return rc; } - IOTRACE(("JHDR %p %lld %d\n", pPager, iNRecOffset, 4)); - rc = write32bits(pPager->jfd, iNRecOffset, pPager->nRec); - if( rc!=SQLITE_OK ) return rc; + + jrnlOff = pPager->journalHdr + sizeof(aJournalMagic); + IOTRACE(("JHDR %p %lld %d\n", pPager, jrnlOff, 4)); + rc = write32bits(pPager->jfd, jrnlOff, pPager->nRec); + if( rc ) return rc; } if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){ PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager))); @@ -3010,63 +2585,37 @@ static int syncJournal(Pager *pPager){ rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags| (pPager->sync_flags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0) ); - if( rc!=SQLITE_OK ) return rc; + if( rc!=0 ) return rc; } + pPager->journalStarted = 1; } + pPager->needSync = 0; - /* The journal file was just successfully synced. Set Pager.needSync - ** to zero and clear the PGHDR_NEED_SYNC flag on all pagess. + /* Erase the needSync flag from every page. */ - pPager->needSync = 0; - pPager->journalStarted = 1; sqlite3PcacheClearSyncFlags(pPager->pPCache); } - return SQLITE_OK; + return rc; } /* -** The argument is the first in a linked list of dirty pages connected -** by the PgHdr.pDirty pointer. This function writes each one of the -** in-memory pages in the list to the database file. The argument may -** be NULL, representing an empty list. In this case this function is -** a no-op. -** -** The pager must hold at least a RESERVED lock when this function -** is called. Before writing anything to the database file, this lock -** is upgraded to an EXCLUSIVE lock. If the lock cannot be obtained, -** SQLITE_BUSY is returned and no data is written to the database file. -** -** If the pager is a temp-file pager and the actual file-system file -** is not yet open, it is created and opened before any data is -** written out. -** -** Once the lock has been upgraded and, if necessary, the file opened, -** the pages are written out to the database file in list order. Writing -** a page is skipped if it meets either of the following criteria: -** -** * The page number is greater than Pager.dbSize, or -** * The PGHDR_DONT_WRITE flag is set on the page. -** -** If writing out a page causes the database file to grow, Pager.dbFileSize -** is updated accordingly. If page 1 is written out, then the value cached -** in Pager.dbFileVers[] is updated to match the new value stored in -** the database file. -** -** If everything is successful, SQLITE_OK is returned. If an IO error -** occurs, an IO error code is returned. Or, if the EXCLUSIVE lock cannot -** be obtained, SQLITE_BUSY is returned. +** Given a list of pages (connected by the PgHdr.pDirty pointer) write +** every one of those pages out to the database file. No calls are made +** to the page-cache to mark the pages as clean. It is the responsibility +** of the caller to use PcacheCleanAll() or PcacheMakeClean() to mark +** the pages as clean. */ static int pager_write_pagelist(PgHdr *pList){ - Pager *pPager; /* Pager object */ - int rc; /* Return code */ + Pager *pPager; + int rc; if( pList==0 ) return SQLITE_OK; pPager = pList->pPager; /* At this point there may be either a RESERVED or EXCLUSIVE lock on the ** database file. If there is already an EXCLUSIVE lock, the following - ** call is a no-op. + ** calls to sqlite3OsLock() are no-ops. ** ** Moving the lock from RESERVED to EXCLUSIVE actually involves going ** through an intermediate state PENDING. A PENDING lock prevents new @@ -3080,82 +2629,67 @@ static int pager_write_pagelist(PgHdr *pList){ ** EXCLUSIVE, it means the database file has been changed and any rollback ** will require a journal playback. */ - assert( pPager->state>=PAGER_RESERVED ); rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); - - /* If the file is a temp-file has not yet been opened, open it now. It - ** is not possible for rc to be other than SQLITE_OK if this branch - ** is taken, as pager_wait_on_lock() is a no-op for temp-files. - */ - if( !isOpen(pPager->fd) ){ - assert( pPager->tempFile && rc==SQLITE_OK ); - rc = pagerOpentemp(pPager, pPager->fd, pPager->vfsFlags); + if( rc!=SQLITE_OK ){ + return rc; } - while( rc==SQLITE_OK && pList ){ - Pgno pgno = pList->pgno; + while( pList ){ + + /* If the file has not yet been opened, open it now. */ + if( !pPager->fd->pMethods ){ + assert(pPager->tempFile); + rc = sqlite3PagerOpentemp(pPager, pPager->fd, pPager->vfsFlags); + if( rc ) return rc; + } /* If there are dirty pages in the page cache with page numbers greater ** than Pager.dbSize, this means sqlite3PagerTruncateImage() was called to ** make the file smaller (presumably by auto-vacuum code). Do not write ** any such pages to the file. - ** - ** Also, do not write out any page that has the PGHDR_DONT_WRITE flag - ** set (set by sqlite3PagerDontWrite()). */ - if( pgno<=pPager->dbSize && 0==(pList->flags&PGHDR_DONT_WRITE) ){ - i64 offset = (pgno-1)*(i64)pPager->pageSize; /* Offset to write */ - char *pData = CODEC2(pPager, pList->pData, pgno, 6); /* Data to write */ + if( pList->pgno<=pPager->dbSize && 0==(pList->flags&PGHDR_DONT_WRITE) ){ + i64 offset = (pList->pgno-1)*(i64)pPager->pageSize; + char *pData = CODEC2(pPager, pList->pData, pList->pgno, 6); - /* Write out the page data. */ + PAGERTRACE(("STORE %d page %d hash(%08x)\n", + PAGERID(pPager), pList->pgno, pager_pagehash(pList))); + IOTRACE(("PGOUT %p %d\n", pPager, pList->pgno)); rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize, offset); - - /* If page 1 was just written, update Pager.dbFileVers to match - ** the value now stored in the database file. If writing this - ** page caused the database file to grow, update dbFileSize. - */ - if( pgno==1 ){ + PAGER_INCR(sqlite3_pager_writedb_count); + PAGER_INCR(pPager->nWrite); + if( pList->pgno==1 ){ memcpy(&pPager->dbFileVers, &pData[24], sizeof(pPager->dbFileVers)); } - if( pgno>pPager->dbFileSize ){ - pPager->dbFileSize = pgno; + if( pList->pgno>pPager->dbFileSize ){ + pPager->dbFileSize = pList->pgno; } - - PAGERTRACE(("STORE %d page %d hash(%08x)\n", - PAGERID(pPager), pgno, pager_pagehash(pList))); - IOTRACE(("PGOUT %p %d\n", pPager, pgno)); - PAGER_INCR(sqlite3_pager_writedb_count); - PAGER_INCR(pPager->nWrite); - }else{ - PAGERTRACE(("NOSTORE %d page %d\n", PAGERID(pPager), pgno)); } +#ifndef NDEBUG + else{ + PAGERTRACE(("NOSTORE %d page %d\n", PAGERID(pPager), pList->pgno)); + } +#endif + if( rc ) return rc; #ifdef SQLITE_CHECK_PAGES pList->pageHash = pager_pagehash(pList); #endif pList = pList->pDirty; } - return rc; + return SQLITE_OK; } /* -** Append a record of the current state of page pPg to the sub-journal. -** It is the callers responsibility to use subjRequiresPage() to check -** that it is really required before calling this function. -** -** If successful, set the bit corresponding to pPg->pgno in the bitvecs -** for all open savepoints before returning. -** -** This function returns SQLITE_OK if everything is successful, an IO -** error code if the attempt to write to the sub-journal fails, or -** SQLITE_NOMEM if a malloc fails while setting a bit in a savepoint -** bitvec. +** Add the page to the sub-journal. It is the callers responsibility to +** use subjRequiresPage() to check that it is really required before +** calling this function. */ static int subjournalPage(PgHdr *pPg){ int rc; void *pData = pPg->pData; Pager *pPager = pPg->pPager; - i64 offset = pPager->nSubRec*(4+pPager->pageSize); + i64 offset = pPager->stmtNRec*(4+pPager->pageSize); char *pData2 = CODEC2(pPager, pData, pPg->pgno, 7); PAGERTRACE(("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno)); @@ -3166,10 +2700,9 @@ static int subjournalPage(PgHdr *pPg){ rc = sqlite3OsWrite(pPager->sjfd, pData2, pPager->pageSize, offset+4); } if( rc==SQLITE_OK ){ - pPager->nSubRec++; + pPager->stmtNRec++; assert( pPager->nSavepoint>0 ); rc = addToSavepointBitvecs(pPager, pPg->pgno); - testcase( rc!=SQLITE_OK ); } return rc; } @@ -3177,122 +2710,62 @@ static int subjournalPage(PgHdr *pPg){ /* ** This function is called by the pcache layer when it has reached some -** soft memory limit. The first argument is a pointer to a Pager object -** (cast as a void*). The pager is always 'purgeable' (not an in-memory -** database). The second argument is a reference to a page that is -** currently dirty but has no outstanding references. The page -** is always associated with the Pager object passed as the first -** argument. -** -** The job of this function is to make pPg clean by writing its contents -** out to the database file, if possible. This may involve syncing the -** journal file. -** -** If successful, sqlite3PcacheMakeClean() is called on the page and -** SQLITE_OK returned. If an IO error occurs while trying to make the -** page clean, the IO error code is returned. If the page cannot be -** made clean for some other reason, but no error occurs, then SQLITE_OK -** is returned by sqlite3PcacheMakeClean() is not called. +** soft memory limit. The argument is a pointer to a purgeable Pager +** object. This function attempts to make a single dirty page that has no +** outstanding references (if one exists) clean so that it can be recycled +** by the pcache layer. */ static int pagerStress(void *p, PgHdr *pPg){ Pager *pPager = (Pager *)p; int rc = SQLITE_OK; - assert( pPg->pPager==pPager ); - assert( pPg->flags&PGHDR_DIRTY ); - - /* The doNotSync flag is set by the sqlite3PagerWrite() function while it - ** is journalling a set of two or more database pages that are stored - ** on the same disk sector. Syncing the journal is not allowed while - ** this is happening as it is important that all members of such a - ** set of pages are synced to disk together. So, if the page this function - ** is trying to make clean will require a journal sync and the doNotSync - ** flag is set, return without doing anything. The pcache layer will - ** just have to go ahead and allocate a new page buffer instead of - ** reusing pPg. - ** - ** Similarly, if the pager has already entered the error state, do not - ** try to write the contents of pPg to disk. - */ - if( pPager->errCode || (pPager->doNotSync && pPg->flags&PGHDR_NEED_SYNC) ){ + if( pPager->doNotSync ){ return SQLITE_OK; } - /* Sync the journal file if required. */ - if( pPg->flags&PGHDR_NEED_SYNC ){ - rc = syncJournal(pPager); - if( rc==SQLITE_OK && pPager->fullSync && - !(pPager->journalMode==PAGER_JOURNALMODE_MEMORY) && - !(sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND) - ){ - pPager->nRec = 0; - rc = writeJournalHdr(pPager); + assert( pPg->flags&PGHDR_DIRTY ); + if( pPager->errCode==SQLITE_OK ){ + if( pPg->flags&PGHDR_NEED_SYNC ){ + rc = syncJournal(pPager); + if( rc==SQLITE_OK && pPager->fullSync && + !(pPager->journalMode==PAGER_JOURNALMODE_MEMORY) && + !(sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND) + ){ + pPager->nRec = 0; + rc = writeJournalHdr(pPager); + } + } + if( rc==SQLITE_OK ){ + pPg->pDirty = 0; + if( pPg->pgno>pPager->dbSize && subjRequiresPage(pPg) ){ + rc = subjournalPage(pPg); + } + if( rc==SQLITE_OK ){ + rc = pager_write_pagelist(pPg); + } + } + if( rc!=SQLITE_OK ){ + pager_error(pPager, rc); } } - /* If the page number of this page is larger than the current size of - ** the database image, it may need to be written to the sub-journal. - ** This is because the call to pager_write_pagelist() below will not - ** actually write data to the file in this case. - ** - ** Consider the following sequence of events: - ** - ** BEGIN; - ** <journal page X> - ** <modify page X> - ** SAVEPOINT sp; - ** <shrink database file to Y pages> - ** pagerStress(page X) - ** ROLLBACK TO sp; - ** - ** If (X>Y), then when pagerStress is called page X will not be written - ** out to the database file, but will be dropped from the cache. Then, - ** following the "ROLLBACK TO sp" statement, reading page X will read - ** data from the database file. This will be the copy of page X as it - ** was when the transaction started, not as it was when "SAVEPOINT sp" - ** was executed. - ** - ** The solution is to write the current data for page X into the - ** sub-journal file now (if it is not already there), so that it will - ** be restored to its current value when the "ROLLBACK TO sp" is - ** executed. - */ - if( rc==SQLITE_OK && pPg->pgno>pPager->dbSize && subjRequiresPage(pPg) ){ - rc = subjournalPage(pPg); - } - - /* Write the contents of the page out to the database file. */ - if( rc==SQLITE_OK ){ - pPg->pDirty = 0; - rc = pager_write_pagelist(pPg); - } - - /* Mark the page as clean. */ if( rc==SQLITE_OK ){ PAGERTRACE(("STRESS %d page %d\n", PAGERID(pPager), pPg->pgno)); sqlite3PcacheMakeClean(pPg); } - - return pager_error(pPager, rc); + return rc; } /* -** This function is called after transitioning from PAGER_UNLOCK to -** PAGER_SHARED state. It tests if there is a hot journal present in -** the file-system for the given pager. A hot journal is one that -** needs to be played back. According to this function, a hot-journal -** file exists if the following three criteria are met: -** -** * The journal file exists in the file system, and -** * No process holds a RESERVED or greater lock on the database file, and -** * The database file itself is greater than 0 bytes in size. +** Return 1 if there is a hot journal on the given pager. +** A hot journal is one that needs to be played back. ** ** If the current size of the database file is 0 but a journal file ** exists, that is probably an old journal left over from a prior -** database with the same name. In this case the journal file is -** just deleted using OsDelete, *pExists is set to 0 and SQLITE_OK -** is returned. +** database with the same name. Just delete the journal. +** +** Return negative if unable to determine the status of the journal. ** ** This routine does not open the journal file to examine its ** content. Hence, the journal might contain the name of a master @@ -3302,36 +2775,28 @@ static int pagerStress(void *p, PgHdr *pPg){ ** journal file exists and is not empty this routine assumes it ** is hot. The pager_playback() routine will discover that the ** journal file is not really hot and will no-op. -** -** If a hot-journal file is found to exist, *pExists is set to 1 and -** SQLITE_OK returned. If no hot-journal file is present, *pExists is -** set to 0 and SQLITE_OK returned. If an IO error occurs while trying -** to determine whether or not a hot-journal file exists, the IO error -** code is returned and the value of *pExists is undefined. */ static int hasHotJournal(Pager *pPager, int *pExists){ - sqlite3_vfs * const pVfs = pPager->pVfs; - int rc; /* Return code */ - int exists = 0; /* True if a journal file is present */ - int locked = 0; /* True if some process holds a RESERVED lock */ - + sqlite3_vfs *pVfs = pPager->pVfs; + int rc = SQLITE_OK; + int exists = 0; + int locked = 0; assert( pPager!=0 ); assert( pPager->useJournal ); - assert( isOpen(pPager->fd) ); - + assert( pPager->fd->pMethods ); *pExists = 0; rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists); if( rc==SQLITE_OK && exists ){ rc = sqlite3OsCheckReservedLock(pPager->fd, &locked); - if( rc==SQLITE_OK && !locked ){ - int nPage; - rc = sqlite3PagerPagecount(pPager, &nPage); - if( rc==SQLITE_OK ){ - if( nPage==0 ){ - sqlite3OsDelete(pVfs, pPager->zJournal, 0); - }else{ - *pExists = 1; - } + } + if( rc==SQLITE_OK && exists && !locked ){ + int nPage; + rc = sqlite3PagerPagecount(pPager, &nPage); + if( rc==SQLITE_OK ){ + if( nPage==0 ){ + sqlite3OsDelete(pVfs, pPager->zJournal, 0); + }else{ + *pExists = 1; } } } @@ -3339,45 +2804,32 @@ static int hasHotJournal(Pager *pPager, int *pExists){ } /* -** Read the content for page pPg out of the database file and into -** pPg->pData. A shared lock or greater must be held on the database -** file before this function is called. -** -** If page 1 is read, then the value of Pager.dbFileVers[] is set to -** the value read from the database file. -** -** If an IO error occurs, then the IO error is returned to the caller. -** Otherwise, SQLITE_OK is returned. +** Read the content of page pPg out of the database file. */ -static int readDbPage(PgHdr *pPg){ - Pager *pPager = pPg->pPager; /* Pager object associated with page pPg */ - Pgno pgno = pPg->pgno; /* Page number to read */ - int rc; /* Return code */ - i64 iOffset; /* Byte offset of file to read from */ - - assert( pPager->state>=PAGER_SHARED && !MEMDB ); - - if( !isOpen(pPager->fd) ){ - assert( pPager->tempFile ); +static int readDbPage(Pager *pPager, PgHdr *pPg, Pgno pgno){ + int rc; + i64 offset; + assert( MEMDB==0 ); + assert(pPager->fd->pMethods||pPager->tempFile); + if( !pPager->fd->pMethods ){ return SQLITE_IOERR_SHORT_READ; } - iOffset = (pgno-1)*(i64)pPager->pageSize; - rc = sqlite3OsRead(pPager->fd, pPg->pData, pPager->pageSize, iOffset); - if( pgno==1 ){ - u8 *dbFileVers = &((u8*)pPg->pData)[24]; - memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers)); - } - CODEC1(pPager, pPg->pData, pgno, 3); - + offset = (pgno-1)*(i64)pPager->pageSize; + rc = sqlite3OsRead(pPager->fd, pPg->pData, pPager->pageSize, offset); PAGER_INCR(sqlite3_pager_readdb_count); PAGER_INCR(pPager->nRead); IOTRACE(("PGIN %p %d\n", pPager, pgno)); + if( pgno==1 ){ + memcpy(&pPager->dbFileVers, &((u8*)pPg->pData)[24], + sizeof(pPager->dbFileVers)); + } + CODEC1(pPager, pPg->pData, pPg->pgno, 3); PAGERTRACE(("FETCH %d page %d hash(%08x)\n", - PAGERID(pPager), pgno, pager_pagehash(pPg))); - + PAGERID(pPager), pPg->pgno, pager_pagehash(pPg))); return rc; } + /* ** This function is called to obtain the shared lock required before ** data may be read from the pager cache. If the shared lock has already @@ -3388,18 +2840,18 @@ static int readDbPage(PgHdr *pPg){ ** is performed immediately. */ static int pagerSharedLock(Pager *pPager){ - int rc = SQLITE_OK; /* Return code */ - int isErrorReset = 0; /* True if recovering from error state */ + int rc = SQLITE_OK; + int isErrorReset = 0; /* If this database is opened for exclusive access, has no outstanding - ** page references and is in an error-state, this is a chance to clear + ** page references and is in an error-state, now is the chance to clear ** the error. Discard the contents of the pager-cache and treat any ** open journal file as a hot-journal. */ if( !MEMDB && pPager->exclusiveMode && sqlite3PcacheRefCount(pPager->pPCache)==0 && pPager->errCode ){ - if( isOpen(pPager->jfd) ){ + if( pPager->journalOpen ){ isErrorReset = 1; } pPager->errCode = SQLITE_OK; @@ -3415,7 +2867,7 @@ static int pagerSharedLock(Pager *pPager){ } if( pPager->state==PAGER_UNLOCK || isErrorReset ){ - sqlite3_vfs * const pVfs = pPager->pVfs; + sqlite3_vfs *pVfs = pPager->pVfs; int isHotJournal = 0; assert( !MEMDB ); assert( sqlite3PcacheRefCount(pPager->pPCache)==0 ); @@ -3444,13 +2896,12 @@ static int pagerSharedLock(Pager *pPager){ ** important that a RESERVED lock is not obtained on the way to the ** EXCLUSIVE lock. If it were, another process might open the ** database file, detect the RESERVED lock, and conclude that the - ** database is safe to read while this process is still rolling the - ** hot-journal back. + ** database is safe to read while this process is still rolling it + ** back. ** - ** Because the intermediate RESERVED lock is not requested, any - ** other process attempting to access the database file will get to - ** this point in the code and fail to obtain its own EXCLUSIVE lock - ** on the database file. + ** Because the intermediate RESERVED lock is not requested, the + ** second process will get to this point in the code and fail to + ** obtain its own EXCLUSIVE lock on the database file. */ if( pPager->state<EXCLUSIVE_LOCK ){ rc = sqlite3OsLock(pPager->fd, EXCLUSIVE_LOCK); @@ -3467,7 +2918,7 @@ static int pagerSharedLock(Pager *pPager){ ** OsTruncate() call used in exclusive-access mode also requires ** a read/write file handle. */ - if( !isOpen(pPager->jfd) ){ + if( !isErrorReset && pPager->journalOpen==0 ){ int res; rc = sqlite3OsAccess(pVfs,pPager->zJournal,SQLITE_ACCESS_EXISTS,&res); if( rc==SQLITE_OK ){ @@ -3476,7 +2927,7 @@ static int pagerSharedLock(Pager *pPager){ int f = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL; assert( !pPager->tempFile ); rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &fout); - assert( rc!=SQLITE_OK || isOpen(pPager->jfd) ); + assert( rc!=SQLITE_OK || pPager->jfd->pMethods ); if( rc==SQLITE_OK && fout&SQLITE_OPEN_READONLY ){ rc = SQLITE_CANTOPEN; sqlite3OsClose(pPager->jfd); @@ -3491,8 +2942,7 @@ static int pagerSharedLock(Pager *pPager){ if( rc!=SQLITE_OK ){ goto failed; } - - /* TODO: Why are these cleared here? Is it necessary? */ + pPager->journalOpen = 1; pPager->journalStarted = 0; pPager->journalOff = 0; pPager->setMaster = 0; @@ -3509,8 +2959,8 @@ static int pagerSharedLock(Pager *pPager){ rc = pager_error(pPager, rc); goto failed; } - assert( (pPager->state==PAGER_SHARED) - || (pPager->exclusiveMode && pPager->state>PAGER_SHARED) + assert(pPager->state==PAGER_SHARED || + (pPager->exclusiveMode && pPager->state>PAGER_SHARED) ); } @@ -3566,6 +3016,25 @@ static int pagerSharedLock(Pager *pPager){ } /* +** Make sure we have the content for a page. If the page was +** previously acquired with noContent==1, then the content was +** just initialized to zeros instead of being read from disk. +** But now we need the real data off of disk. So make sure we +** have it. Read it in if we do not have it already. +*/ +static int pager_get_content(PgHdr *pPg){ + if( pPg->flags&PGHDR_NEED_READ ){ + int rc = readDbPage(pPg->pPager, pPg, pPg->pgno); + if( rc==SQLITE_OK ){ + pPg->flags &= ~PGHDR_NEED_READ; + }else{ + return rc; + } + } + return SQLITE_OK; +} + +/* ** If the reference count has reached zero, and the pager is not in the ** middle of a write transaction or opened in exclusive mode, unlock it. */ @@ -3614,21 +3083,12 @@ static void pagerDropPage(DbPage *pPg){ ** ** If noContent is false, the page contents are actually read from disk. ** If noContent is true, it means that we do not care about the contents -** of the page. This occurs in two seperate scenarios: -** -** a) When reading a free-list leaf page from the database, and -** -** b) When a savepoint is being rolled back and we need to load -** a new page into the cache to populate with the data read -** from the savepoint journal. -** -** If noContent is true, then the data returned is zeroed instead of -** being read from the database. Additionally, the bits corresponding -** to pgno in Pager.pInJournal (bitvec of pages already written to the -** journal file) and the PagerSavepoint.pInSavepoint bitvecs of any open -** savepoints are set. This means if the page is made writable at any -** point in the future, using a call to sqlite3PagerWrite(), its contents -** will not be journaled. This saves IO. +** of the page at this time, so do not do a disk read. Just fill in the +** page content with zeros. But mark the fact that we have not read the +** content by setting the PgHdr.needRead flag. Later on, if +** sqlite3PagerWrite() is called on this page or if this routine is +** called again with noContent==0, that means that the content is needed +** and the disk read should occur at that point. */ int sqlite3PagerAcquire( Pager *pPager, /* The pager open on the database file */ @@ -3692,24 +3152,13 @@ int sqlite3PagerAcquire( } memset(pPg->pData, 0, pPager->pageSize); if( noContent ){ - /* Failure to set the bits in the InJournal bit-vectors is benign. - ** It merely means that we might do some extra work to journal a - ** page that does not need to be journaled. Nevertheless, be sure - ** to test the case where a malloc error occurs while trying to set - ** a bit in a bit vector. - */ - sqlite3BeginBenignMalloc(); - TESTONLY( rc = ) sqlite3BitvecSet(pPager->pInJournal, pPg->pgno); - testcase( rc==SQLITE_NOMEM ); - TESTONLY( rc = ) addToSavepointBitvecs(pPager, pPg->pgno); - testcase( rc==SQLITE_NOMEM ); - sqlite3EndBenignMalloc(); + pPg->flags |= PGHDR_NEED_READ; } IOTRACE(("ZERO %p %d\n", pPager, pgno)); }else{ - assert( pPg->pPager==pPager && pPg->pgno==pgno ); - rc = readDbPage(pPg); + rc = readDbPage(pPager, pPg, pgno); if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){ + /* sqlite3PagerUnref(pPg); */ pagerDropPage(pPg); return rc; } @@ -3719,7 +3168,15 @@ int sqlite3PagerAcquire( #endif }else{ /* The requested page is in the page cache. */ + assert(sqlite3PcacheRefCount(pPager->pPCache)>0 || pgno==1); PAGER_INCR(pPager->nHit); + if( !noContent ){ + rc = pager_get_content(pPg); + if( rc ){ + sqlite3PagerUnref(pPg); + return rc; + } + } } *ppPage = pPg; @@ -3759,12 +3216,13 @@ DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){ ** are released, a rollback occurs and the lock on the database is ** removed. */ -void sqlite3PagerUnref(DbPage *pPg){ +int sqlite3PagerUnref(DbPage *pPg){ if( pPg ){ Pager *pPager = pPg->pPager; sqlite3PcacheRelease(pPg); pagerUnlockIfUnused(pPager); } + return SQLITE_OK; } /* @@ -3778,11 +3236,11 @@ void sqlite3PagerUnref(DbPage *pPg){ */ static int openSubJournal(Pager *pPager){ int rc = SQLITE_OK; - if( isOpen(pPager->jfd) && !isOpen(pPager->sjfd) ){ + if( pPager->journalOpen && !pPager->sjfd->pMethods ){ if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){ sqlite3MemJournalOpen(pPager->sjfd); }else{ - rc = pagerOpentemp(pPager, pPager->sjfd, SQLITE_OPEN_SUBJOURNAL); + rc = sqlite3PagerOpentemp(pPager, pPager->sjfd, SQLITE_OPEN_SUBJOURNAL); } } return rc; @@ -3810,7 +3268,7 @@ static int pager_open_journal(Pager *pPager){ goto failed_to_open_journal; } - if( !isOpen(pPager->jfd) ){ + if( pPager->journalOpen==0 ){ if( pPager->tempFile ){ flags |= (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL); }else{ @@ -3828,7 +3286,7 @@ static int pager_open_journal(Pager *pPager){ rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0); #endif } - assert( rc!=SQLITE_OK || isOpen(pPager->jfd) ); + assert( rc!=SQLITE_OK || pPager->jfd->pMethods ); pPager->journalOff = 0; pPager->setMaster = 0; pPager->journalHdr = 0; @@ -3839,6 +3297,7 @@ static int pager_open_journal(Pager *pPager){ goto failed_to_open_journal; } } + pPager->journalOpen = 1; pPager->journalStarted = 0; pPager->needSync = 0; pPager->nRec = 0; @@ -3912,12 +3371,13 @@ int sqlite3PagerBegin(DbPage *pPg, int exFlag){ if( rc!=SQLITE_OK ){ return rc; } + pPager->dirtyCache = 0; PAGERTRACE(("TRANSACTION %d\n", PAGERID(pPager))); if( pPager->useJournal && !pPager->tempFile && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ rc = pager_open_journal(pPager); } - }else if( isOpen(pPager->jfd) && pPager->journalOff==0 ){ + }else if( pPager->journalOpen && pPager->journalOff==0 ){ /* This happens when the pager was in exclusive-access mode the last ** time a (read or write) transaction was successfully concluded ** by this connection. Instead of deleting the journal file it was @@ -3936,7 +3396,7 @@ int sqlite3PagerBegin(DbPage *pPg, int exFlag){ rc = writeJournalHdr(pPager); } } - assert( !isOpen(pPager->jfd) || pPager->journalOff>0 || rc!=SQLITE_OK ); + assert( !pPager->journalOpen || pPager->journalOff>0 || rc!=SQLITE_OK ); return rc; } @@ -3975,11 +3435,25 @@ static int pager_write(PgHdr *pPg){ CHECK_PAGE(pPg); + /* If this page was previously acquired with noContent==1, that means + ** we didn't really read in the content of the page. This can happen + ** (for example) when the page is being moved to the freelist. But + ** now we are (perhaps) moving the page off of the freelist for + ** reuse and we need to know its original content so that content + ** can be stored in the rollback journal. So do the read at this + ** time. + */ + rc = pager_get_content(pPg); + if( rc ){ + return rc; + } + /* Mark the page as dirty. If the page has already been written ** to the journal then we can return right away. */ sqlite3PcacheMakeDirty(pPg); if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){ + pPager->dirtyCache = 1; pPager->dbModified = 1; }else{ @@ -3996,18 +3470,19 @@ static int pager_write(PgHdr *pPg){ return rc; } assert( pPager->state>=PAGER_RESERVED ); - if( !isOpen(pPager->jfd) && pPager->useJournal + if( !pPager->journalOpen && pPager->useJournal && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ rc = pager_open_journal(pPager); if( rc!=SQLITE_OK ) return rc; } + pPager->dirtyCache = 1; pPager->dbModified = 1; /* The transaction journal now exists and we have a RESERVED or an ** EXCLUSIVE lock on the main database file. Write the current page to ** the transaction journal if it is not there already. */ - if( !pageInJournal(pPg) && isOpen(pPager->jfd) ){ + if( !pageInJournal(pPg) && pPager->journalOpen ){ if( pPg->pgno<=pPager->dbOrigSize ){ u32 cksum; char *pData2; @@ -4220,19 +3695,127 @@ int sqlite3PagerIswriteable(DbPage *pPg){ ** Tests show that this optimization, together with the ** sqlite3PagerDontRollback() below, more than double the speed ** of large INSERT operations and quadruple the speed of large DELETEs. +** +** When this routine is called, set the bit corresponding to pDbPage in +** the Pager.pAlwaysRollback bitvec. Subsequent calls to +** sqlite3PagerDontRollback() for the same page will thereafter be ignored. +** This is necessary to avoid a problem where a page with data is added to +** the freelist during one part of a transaction then removed from the +** freelist during a later part of the same transaction and reused for some +** other purpose. When it is first added to the freelist, this routine is +** called. When reused, the sqlite3PagerDontRollback() routine is called. +** But because the page contains critical data, we still need to be sure it +** gets rolled back in spite of the sqlite3PagerDontRollback() call. */ -void sqlite3PagerDontWrite(PgHdr *pPg){ +int sqlite3PagerDontWrite(DbPage *pDbPage){ + PgHdr *pPg = pDbPage; Pager *pPager = pPg->pPager; - if( (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){ - PAGERTRACE(("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager))); - IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno)) - pPg->flags |= PGHDR_DONT_WRITE; + int rc; + + if( pPg->pgno>pPager->dbOrigSize ){ + return SQLITE_OK; + } + if( pPager->pAlwaysRollback==0 ){ + assert( pPager->pInJournal ); + pPager->pAlwaysRollback = sqlite3BitvecCreate(pPager->dbOrigSize); + if( !pPager->pAlwaysRollback ){ + return SQLITE_NOMEM; + } + } + rc = sqlite3BitvecSet(pPager->pAlwaysRollback, pPg->pgno); + + if( rc==SQLITE_OK && (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){ + assert( pPager->state>=PAGER_SHARED ); + if( pPager->dbSize==pPg->pgno && pPager->dbOrigSize<pPager->dbSize ){ + /* If this pages is the last page in the file and the file has grown + ** during the current transaction, then do NOT mark the page as clean. + ** When the database file grows, we must make sure that the last page + ** gets written at least once so that the disk file will be the correct + ** size. If you do not write this page and the size of the file + ** on the disk ends up being too small, that can lead to database + ** corruption during the next transaction. + */ + }else{ + PAGERTRACE(("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager))); + IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno)) + pPg->flags |= PGHDR_DONT_WRITE; #ifdef SQLITE_CHECK_PAGES - pPg->pageHash = pager_pagehash(pPg); + pPg->pageHash = pager_pagehash(pPg); #endif + } } + return rc; +} + +/* +** A call to this routine tells the pager that if a rollback occurs, +** it is not necessary to restore the data on the given page. This +** means that the pager does not have to record the given page in the +** rollback journal. +** +** If we have not yet actually read the content of this page (if +** the PgHdr.needRead flag is set) then this routine acts as a promise +** that we will never need to read the page content in the future. +** so the needRead flag can be cleared at this point. +*/ +void sqlite3PagerDontRollback(DbPage *pPg){ + Pager *pPager = pPg->pPager; + TESTONLY( int rc; ) /* Return value from sqlite3BitvecSet() */ + + assert( pPager->state>=PAGER_RESERVED ); + + /* If the journal file is not open, or DontWrite() has been called on + ** this page (DontWrite() sets the Pager.pAlwaysRollback bit), then this + ** function is a no-op. + */ + if( pPager->journalOpen==0 + || sqlite3BitvecTest(pPager->pAlwaysRollback, pPg->pgno) + || pPg->pgno>pPager->dbOrigSize + ){ + return; + } + +#ifdef SQLITE_SECURE_DELETE + if( sqlite3BitvecTest(pPager->pInJournal, pPg->pgno)!=0 + || pPg->pgno>pPager->dbOrigSize ){ + return; + } +#endif + + /* If SECURE_DELETE is disabled, then there is no way that this + ** routine can be called on a page for which sqlite3PagerDontWrite() + ** has not been previously called during the same transaction. + ** And if DontWrite() has previously been called, the following + ** conditions must be met. + ** + ** (Later:) Not true. If the database is corrupted by having duplicate + ** pages on the freelist (ex: corrupt9.test) then the following is not + ** necessarily true: + */ + /* assert( !pPg->inJournal && (int)pPg->pgno <= pPager->dbOrigSize ); */ + + assert( pPager->pInJournal!=0 ); + pPg->flags &= ~PGHDR_NEED_READ; + + /* Failure to set the bits in the InJournal bit-vectors is benign. + ** It merely means that we might do some extra work to journal a page + ** that does not need to be journaled. Nevertheless, be sure to test the + ** case where a malloc error occurs while trying to set a bit in a + ** bit vector. + */ + sqlite3BeginBenignMalloc(); + TESTONLY( rc = ) sqlite3BitvecSet(pPager->pInJournal, pPg->pgno); + testcase( rc==SQLITE_NOMEM ); + TESTONLY( rc = ) addToSavepointBitvecs(pPager, pPg->pgno); + testcase( rc==SQLITE_NOMEM ); + sqlite3EndBenignMalloc(); + + + PAGERTRACE(("DONT_ROLLBACK page %d of %d\n", pPg->pgno, PAGERID(pPager))); + IOTRACE(("GARBAGE %p %d\n", pPager, pPg->pgno)) } + /* ** This routine is called to increment the database file change-counter, ** stored at byte 24 of the pager file. @@ -4264,7 +3847,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirect){ put32bits(((char*)pPgHdr->pData)+24, change_counter); #ifdef SQLITE_ENABLE_ATOMIC_WRITE - if( isDirect && isOpen(pPager->fd) ){ + if( isDirect && pPager->fd->pMethods ){ const void *zBuf = pPgHdr->pData; assert( pPager->dbFileSize>0 ); rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0); @@ -4325,8 +3908,8 @@ int sqlite3PagerCommitPhaseOne( */ if( pPager->dbModified==0 && (pPager->journalMode!=PAGER_JOURNALMODE_DELETE || - pPager->exclusiveMode) ){ - assert( pPager->dbModified==0 || !isOpen(pPager->jfd) ); + pPager->exclusiveMode!=0) ){ + assert( pPager->dirtyCache==0 || pPager->journalOpen==0 ); return SQLITE_OK; } @@ -4336,7 +3919,7 @@ int sqlite3PagerCommitPhaseOne( /* If this is an in-memory db, or no pages have been written to, or this ** function has already been called, it is a no-op. */ - if( pPager->state!=PAGER_SYNCED && !MEMDB && pPager->dbModified ){ + if( pPager->state!=PAGER_SYNCED && !MEMDB && pPager->dirtyCache ){ PgHdr *pPg; #ifdef SQLITE_ENABLE_ATOMIC_WRITE @@ -4355,12 +3938,12 @@ int sqlite3PagerCommitPhaseOne( pPg = sqlite3PcacheDirtyList(pPager->pPCache); useAtomicWrite = ( !zMaster && - isOpen(pPager->jfd) && + pPager->journalOpen && pPager->journalOff==jrnlBufferSize(pPager) && pPager->dbSize>=pPager->dbFileSize && (pPg==0 || pPg->pDirty==0) ); - assert( isOpen(pPager->jfd) || pPager->journalMode==PAGER_JOURNALMODE_OFF ); + assert( pPager->journalOpen || pPager->journalMode==PAGER_JOURNALMODE_OFF ); if( useAtomicWrite ){ /* Update the nRec field in the journal file. */ int offset = pPager->journalHdr + sizeof(aJournalMagic); @@ -4438,10 +4021,9 @@ int sqlite3PagerCommitPhaseOne( } sqlite3PcacheCleanAll(pPager->pPCache); - if( pPager->dbSize!=pPager->dbFileSize ){ - Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager)); + if( pPager->dbSize<pPager->dbFileSize ){ assert( pPager->state>=PAGER_EXCLUSIVE ); - rc = pager_truncate(pPager, nNew); + rc = pager_truncate(pPager, pPager->dbSize); if( rc!=SQLITE_OK ) goto sync_exit; } @@ -4486,11 +4068,11 @@ int sqlite3PagerCommitPhaseTwo(Pager *pPager){ if( pPager->dbModified==0 && (pPager->journalMode!=PAGER_JOURNALMODE_DELETE || pPager->exclusiveMode!=0) ){ - assert( pPager->dbModified==0 || isOpen(pPager->jfd)==0 ); + assert( pPager->dirtyCache==0 || pPager->journalOpen==0 ); return SQLITE_OK; } PAGERTRACE(("COMMIT %d\n", PAGERID(pPager))); - assert( pPager->state==PAGER_SYNCED || MEMDB || !pPager->dbModified ); + assert( pPager->state==PAGER_SYNCED || MEMDB || !pPager->dirtyCache ); rc = pager_end_transaction(pPager, pPager->setMaster); rc = pager_error(pPager, rc); return rc; @@ -4511,7 +4093,7 @@ int sqlite3PagerCommitPhaseTwo(Pager *pPager){ int sqlite3PagerRollback(Pager *pPager){ int rc = SQLITE_OK; PAGERTRACE(("ROLLBACK %d\n", PAGERID(pPager))); - if( !pPager->dbModified || !isOpen(pPager->jfd) ){ + if( !pPager->dirtyCache || !pPager->journalOpen ){ rc = pager_end_transaction(pPager, pPager->setMaster); }else if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){ if( pPager->state>=PAGER_EXCLUSIVE ){ @@ -4601,7 +4183,7 @@ int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){ /* Either there is no active journal or the sub-journal is open or ** the journal is always stored in memory */ - assert( pPager->nSavepoint==0 || isOpen(pPager->sjfd) || + assert( pPager->nSavepoint==0 || pPager->sjfd->pMethods || pPager->journalMode==PAGER_JOURNALMODE_MEMORY ); /* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM @@ -4625,12 +4207,12 @@ int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){ for(/* no-op */; ii<nSavepoint; ii++){ assert( pPager->dbSizeValid ); aNew[ii].nOrig = pPager->dbSize; - if( isOpen(pPager->jfd) && pPager->journalOff>0 ){ + if( pPager->journalOpen && pPager->journalOff>0 ){ aNew[ii].iOffset = pPager->journalOff; }else{ aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager); } - aNew[ii].iSubRec = pPager->nSubRec; + aNew[ii].iSubRec = pPager->stmtNRec; aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize); if( !aNew[ii].pInSavepoint ){ return SQLITE_NOMEM; @@ -4669,7 +4251,7 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){ } pPager->nSavepoint = nNew; - if( op==SAVEPOINT_ROLLBACK && isOpen(pPager->jfd) ){ + if( op==SAVEPOINT_ROLLBACK && pPager->jfd->pMethods ){ PagerSavepoint *pSavepoint = (nNew==0)?0:&pPager->aSavepoint[nNew-1]; rc = pagerPlaybackSavepoint(pPager, pSavepoint); assert(rc!=SQLITE_DONE); @@ -4677,10 +4259,10 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){ /* If this is a release of the outermost savepoint, truncate ** the sub-journal. */ - if( nNew==0 && op==SAVEPOINT_RELEASE && isOpen(pPager->sjfd) ){ + if( nNew==0 && op==SAVEPOINT_RELEASE && pPager->sjfd->pMethods ){ assert( rc==SQLITE_OK ); rc = sqlite3OsTruncate(pPager->sjfd, 0); - pPager->nSubRec = 0; + pPager->stmtNRec = 0; } } return rc; @@ -4710,6 +4292,13 @@ sqlite3_file *sqlite3PagerFile(Pager *pPager){ } /* +** Return the directory of the database file. +*/ +const char *sqlite3PagerDirname(Pager *pPager){ + return pPager->zDirectory; +} + +/* ** Return the full pathname of the journal file. */ const char *sqlite3PagerJournalname(Pager *pPager){ @@ -4793,6 +4382,8 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){ PAGERID(pPager), pPg->pgno, (pPg->flags&PGHDR_NEED_SYNC)?1:0, pgno)); IOTRACE(("MOVE %p %d %d\n", pPager, pPg->pgno, pgno)) + pager_get_content(pPg); + /* If the journal needs to be sync()ed before page pPg->pgno can ** be written to, store pPg->pgno in local variable needSyncPgno. ** @@ -4825,6 +4416,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){ } sqlite3PcacheMakeDirty(pPg); + pPager->dirtyCache = 1; pPager->dbModified = 1; if( needSyncPgno ){ @@ -4916,7 +4508,8 @@ int sqlite3PagerLockingMode(Pager *pPager, int eMode){ ** If the parameter is not _QUERY, then the journal-mode is set to the ** value specified. ** -** The returned indicate the current (possibly updated) journal-mode. +** The returned indicate the current (possibly updated) +** journal-mode. */ int sqlite3PagerJournalMode(Pager *pPager, int eMode){ if( !MEMDB ){ diff --git a/src/pager.h b/src/pager.h index c5a481b9d..8aa39adb3 100644 --- a/src/pager.h +++ b/src/pager.h @@ -13,16 +13,15 @@ ** 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.94 2009/01/16 15:21:06 danielk1977 Exp $ +** @(#) $Id: pager.h,v 1.95 2009/01/16 16:23:38 danielk1977 Exp $ */ #ifndef _PAGER_H_ #define _PAGER_H_ /* -** Default maximum size for persistent journal files. A negative -** value means no limit. This value may be overridden using the -** sqlite3PagerJournalSizeLimit() API. See also "PRAGMA journal_size_limit". +** If defined as non-zero, auto-vacuum is enabled by default. Otherwise +** it must be turned on for each database using "PRAGMA auto_vacuum = 1". */ #ifndef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT #define SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT -1 @@ -45,19 +44,9 @@ typedef struct Pager Pager; typedef struct PgHdr DbPage; /* -** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is -** reserved for working around a windows/posix incompatibility). It is -** used in the journal to signify that the remainder of the journal file -** is devoted to storing a master journal name - there are no more pages to -** roll back. See comments for function writeMasterJournal() in pager.c -** for details. -*/ -#define PAGER_MJ_PGNO(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) - -/* ** Allowed values for the flags parameter to sqlite3PagerOpen(). ** -** NOTE: These values must match the corresponding BTREE_ values in btree.h. +** NOTE: This values must match the corresponding BTREE_ values in btree.h. */ #define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ #define PAGER_NO_READLOCK 0x0002 /* Omit readlocks on readonly files */ @@ -80,84 +69,75 @@ typedef struct PgHdr DbPage; #define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ /* -** The remainder of this file contains the declarations of the functions -** that make up the Pager sub-system API. See source code comments for -** a detailed description of each routine. +** See source code comments for a detailed description of the following +** routines: */ - -/* Open and close a Pager connection. */ int sqlite3PagerOpen(sqlite3_vfs *, Pager **ppPager, const char*, int,int,int); -int sqlite3PagerClose(Pager *pPager); - -/* Functions used to configure a Pager object. */ void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *); void sqlite3PagerSetReiniter(Pager*, void(*)(DbPage*)); int sqlite3PagerSetPagesize(Pager*, u16*); int sqlite3PagerMaxPageCount(Pager*, int); int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); void sqlite3PagerSetCachesize(Pager*, int); -void sqlite3PagerSetSafetyLevel(Pager*,int,int); -int sqlite3PagerLockingMode(Pager *, int); -int sqlite3PagerJournalMode(Pager *, int); -i64 sqlite3PagerJournalSizeLimit(Pager *, i64); - -/* Functions used to obtain and release page references. */ +int sqlite3PagerClose(Pager *pPager); int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag); #define sqlite3PagerGet(A,B,C) sqlite3PagerAcquire(A,B,C,0) DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno); -void sqlite3PagerRef(DbPage*); -void sqlite3PagerUnref(DbPage*); - -/* Operations on page references. */ -int sqlite3PagerWrite(DbPage*); -void sqlite3PagerDontWrite(DbPage*); -int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int); int sqlite3PagerPageRefcount(DbPage*); -void *sqlite3PagerGetData(DbPage *); -void *sqlite3PagerGetExtra(DbPage *); - -/* Functions used to manage pager transactions and savepoints. */ +int sqlite3PagerRef(DbPage*); +int sqlite3PagerUnref(DbPage*); +int sqlite3PagerWrite(DbPage*); int sqlite3PagerPagecount(Pager*, int*); int sqlite3PagerBegin(DbPage*, int exFlag); int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int); -int sqlite3PagerSync(Pager *pPager); int sqlite3PagerCommitPhaseTwo(Pager*); int sqlite3PagerRollback(Pager*); -int sqlite3PagerOpenSavepoint(Pager *pPager, int n); -int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); - -/* Functions used to query pager state and configuration. */ u8 sqlite3PagerIsreadonly(Pager*); +void sqlite3PagerDontRollback(DbPage*); +int sqlite3PagerDontWrite(DbPage*); int sqlite3PagerRefcount(Pager*); +void sqlite3PagerSetSafetyLevel(Pager*,int,int); const char *sqlite3PagerFilename(Pager*); const sqlite3_vfs *sqlite3PagerVfs(Pager*); sqlite3_file *sqlite3PagerFile(Pager*); const char *sqlite3PagerDirname(Pager*); const char *sqlite3PagerJournalname(Pager*); int sqlite3PagerNosync(Pager*); +int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int); +void *sqlite3PagerGetData(DbPage *); +void *sqlite3PagerGetExtra(DbPage *); +int sqlite3PagerLockingMode(Pager *, int); +int sqlite3PagerJournalMode(Pager *, int); +i64 sqlite3PagerJournalSizeLimit(Pager *, i64); void *sqlite3PagerTempSpace(Pager*); +int sqlite3PagerSync(Pager *pPager); + +int sqlite3PagerOpenSavepoint(Pager *pPager, int n); +int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); -/* Functions used in auto-vacuum mode to truncate the database file. */ #ifndef SQLITE_OMIT_AUTOVACUUM void sqlite3PagerTruncateImage(Pager*,Pgno); + Pgno sqlite3PagerImageSize(Pager *); #endif -/* Used by encryption extensions. */ #ifdef SQLITE_HAS_CODEC void sqlite3PagerSetCodec(Pager*,void*(*)(void*,void*,Pgno,int),void*); #endif -/* Functions to support testing and debugging. */ #if !defined(NDEBUG) || defined(SQLITE_TEST) Pgno sqlite3PagerPagenumber(DbPage*); int sqlite3PagerIswriteable(DbPage*); #endif + #ifdef SQLITE_TEST int *sqlite3PagerStats(Pager*); void sqlite3PagerRefdump(Pager*); int sqlite3PagerIsMemdb(Pager*); - void disable_simulated_io_errors(void); - void enable_simulated_io_errors(void); +#endif + +#ifdef SQLITE_TEST +void disable_simulated_io_errors(void); +void enable_simulated_io_errors(void); #else # define disable_simulated_io_errors() # define enable_simulated_io_errors() diff --git a/src/pcache.c b/src/pcache.c index 98343b277..c16606dd4 100644 --- a/src/pcache.c +++ b/src/pcache.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file implements that page cache. ** -** @(#) $Id: pcache.c,v 1.40 2009/01/16 15:21:06 danielk1977 Exp $ +** @(#) $Id: pcache.c,v 1.41 2009/01/16 16:23:38 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -431,8 +431,9 @@ void sqlite3PcacheClose(PCache *pCache){ /* ** Discard the contents of the cache. */ -void sqlite3PcacheClear(PCache *pCache){ +int sqlite3PcacheClear(PCache *pCache){ sqlite3PcacheTruncate(pCache, 0); + return SQLITE_OK; } /* diff --git a/src/pcache.h b/src/pcache.h index ead549842..7cffac15c 100644 --- a/src/pcache.h +++ b/src/pcache.h @@ -12,7 +12,7 @@ ** This header file defines the interface that the sqlite page cache ** subsystem. ** -** @(#) $Id: pcache.h,v 1.17 2009/01/16 15:21:06 danielk1977 Exp $ +** @(#) $Id: pcache.h,v 1.18 2009/01/16 16:23:38 danielk1977 Exp $ */ #ifndef _PCACHE_H_ @@ -111,7 +111,7 @@ void sqlite3PcacheClose(PCache*); void sqlite3PcacheClearSyncFlags(PCache *); /* Discard the contents of the cache */ -void sqlite3PcacheClear(PCache*); +int sqlite3PcacheClear(PCache*); /* Return the total number of outstanding page references */ int sqlite3PcacheRefCount(PCache*); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 84f47cef8..3e860feb6 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.826 2009/01/16 15:21:06 danielk1977 Exp $ +** @(#) $Id: sqliteInt.h,v 1.827 2009/01/16 16:23:38 danielk1977 Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -2255,7 +2255,6 @@ int sqlite3BitvecTest(Bitvec*, u32); int sqlite3BitvecSet(Bitvec*, u32); void sqlite3BitvecClear(Bitvec*, u32); void sqlite3BitvecDestroy(Bitvec*); -u32 sqlite3BitvecSize(Bitvec*); int sqlite3BitvecBuiltinTest(int,int*); RowSet *sqlite3RowSetInit(sqlite3*, void*, unsigned int); diff --git a/src/test2.c b/src/test2.c index 5f1c12e14..f8665ba7f 100644 --- a/src/test2.c +++ b/src/test2.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test2.c,v 1.66 2009/01/16 15:21:06 danielk1977 Exp $ +** $Id: test2.c,v 1.67 2009/01/16 16:23:38 danielk1977 Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -418,13 +418,18 @@ static int page_unref( const char **argv /* Text of each argument */ ){ DbPage *pPage; + int rc; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " PAGE\"", 0); return TCL_ERROR; } pPage = (DbPage *)sqlite3TestTextToPtr(argv[1]); - sqlite3PagerUnref(pPage); + rc = sqlite3PagerUnref(pPage); + if( rc!=SQLITE_OK ){ + Tcl_AppendResult(interp, errorName(rc), 0); + return TCL_ERROR; + } return TCL_OK; } diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 24cd5f04c..059958c0e 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -14,7 +14,7 @@ ** to version 2.8.7, all this code was combined into the vdbe.c source file. ** But that file was getting too big so this subroutines were split out. ** -** $Id: vdbeaux.c,v 1.431 2009/01/16 15:21:06 danielk1977 Exp $ +** $Id: vdbeaux.c,v 1.432 2009/01/16 16:23:38 danielk1977 Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -1388,10 +1388,10 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ /* Sync the master journal file. If the IOCAP_SEQUENTIAL device ** flag is set this is not required. */ - if( needSync - && 0==(sqlite3OsDeviceCharacteristics(pMaster)&SQLITE_IOCAP_SEQUENTIAL) - && SQLITE_OK!=(rc = sqlite3OsSync(pMaster, SQLITE_SYNC_NORMAL)) - ){ + zMainFile = sqlite3BtreeGetDirname(db->aDb[0].pBt); + if( (needSync + && (0==(sqlite3OsDeviceCharacteristics(pMaster)&SQLITE_IOCAP_SEQUENTIAL)) + && (rc=sqlite3OsSync(pMaster, SQLITE_SYNC_NORMAL))!=SQLITE_OK) ){ sqlite3OsCloseFree(pMaster); sqlite3OsDelete(pVfs, zMaster, 0); sqlite3DbFree(db, zMaster); |